diff --git a/3rdparty/MDB2/Driver/mysql.php b/3rdparty/MDB2/Driver/mysql.php
index b9b46c0d762f445a384320a9fd6bc9b3870fc40d..2cec1e9c033b0fb36ef8b269bc6234d853053923 100644
--- a/3rdparty/MDB2/Driver/mysql.php
+++ b/3rdparty/MDB2/Driver/mysql.php
@@ -794,7 +794,7 @@ class MDB2_Driver_mysql extends MDB2_Driver_Common
             ? 'mysql_query' : 'mysql_unbuffered_query';
         $result = @$function($query, $connection);
         if (!$result) {
-            $err =& $this->raiseError(null, null, null,
+            $err =$this->raiseError(null, null, null,
                 'Could not execute statement', __FUNCTION__);
             return $err;
         }
diff --git a/3rdparty/phpass/PasswordHash.php b/3rdparty/phpass/PasswordHash.php
new file mode 100644
index 0000000000000000000000000000000000000000..12958c7f19ee12aeb724e862c07da1d3603070a1
--- /dev/null
+++ b/3rdparty/phpass/PasswordHash.php
@@ -0,0 +1,253 @@
+<?php
+#
+# Portable PHP password hashing framework.
+#
+# Version 0.3 / genuine.
+#
+# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
+# the public domain.  Revised in subsequent years, still public domain.
+#
+# There's absolutely no warranty.
+#
+# The homepage URL for this framework is:
+#
+#	http://www.openwall.com/phpass/
+#
+# Please be sure to update the Version line if you edit this file in any way.
+# It is suggested that you leave the main version number intact, but indicate
+# your project name (after the slash) and add your own revision information.
+#
+# Please do not change the "private" password hashing method implemented in
+# here, thereby making your hashes incompatible.  However, if you must, please
+# change the hash type identifier (the "$P$") to something different.
+#
+# Obviously, since this code is in the public domain, the above are not
+# requirements (there can be none), but merely suggestions.
+#
+class PasswordHash {
+	var $itoa64;
+	var $iteration_count_log2;
+	var $portable_hashes;
+	var $random_state;
+
+	function PasswordHash($iteration_count_log2, $portable_hashes)
+	{
+		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
+			$iteration_count_log2 = 8;
+		$this->iteration_count_log2 = $iteration_count_log2;
+
+		$this->portable_hashes = $portable_hashes;
+
+		$this->random_state = microtime();
+		if (function_exists('getmypid'))
+			$this->random_state .= getmypid();
+	}
+
+	function get_random_bytes($count)
+	{
+		$output = '';
+		if (is_readable('/dev/urandom') &&
+		    ($fh = @fopen('/dev/urandom', 'rb'))) {
+			$output = fread($fh, $count);
+			fclose($fh);
+		}
+
+		if (strlen($output) < $count) {
+			$output = '';
+			for ($i = 0; $i < $count; $i += 16) {
+				$this->random_state =
+				    md5(microtime() . $this->random_state);
+				$output .=
+				    pack('H*', md5($this->random_state));
+			}
+			$output = substr($output, 0, $count);
+		}
+
+		return $output;
+	}
+
+	function encode64($input, $count)
+	{
+		$output = '';
+		$i = 0;
+		do {
+			$value = ord($input[$i++]);
+			$output .= $this->itoa64[$value & 0x3f];
+			if ($i < $count)
+				$value |= ord($input[$i]) << 8;
+			$output .= $this->itoa64[($value >> 6) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			if ($i < $count)
+				$value |= ord($input[$i]) << 16;
+			$output .= $this->itoa64[($value >> 12) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			$output .= $this->itoa64[($value >> 18) & 0x3f];
+		} while ($i < $count);
+
+		return $output;
+	}
+
+	function gensalt_private($input)
+	{
+		$output = '$P$';
+		$output .= $this->itoa64[min($this->iteration_count_log2 +
+			((PHP_VERSION >= '5') ? 5 : 3), 30)];
+		$output .= $this->encode64($input, 6);
+
+		return $output;
+	}
+
+	function crypt_private($password, $setting)
+	{
+		$output = '*0';
+		if (substr($setting, 0, 2) == $output)
+			$output = '*1';
+
+		$id = substr($setting, 0, 3);
+		# We use "$P$", phpBB3 uses "$H$" for the same thing
+		if ($id != '$P$' && $id != '$H$')
+			return $output;
+
+		$count_log2 = strpos($this->itoa64, $setting[3]);
+		if ($count_log2 < 7 || $count_log2 > 30)
+			return $output;
+
+		$count = 1 << $count_log2;
+
+		$salt = substr($setting, 4, 8);
+		if (strlen($salt) != 8)
+			return $output;
+
+		# We're kind of forced to use MD5 here since it's the only
+		# cryptographic primitive available in all versions of PHP
+		# currently in use.  To implement our own low-level crypto
+		# in PHP would result in much worse performance and
+		# consequently in lower iteration counts and hashes that are
+		# quicker to crack (by non-PHP code).
+		if (PHP_VERSION >= '5') {
+			$hash = md5($salt . $password, TRUE);
+			do {
+				$hash = md5($hash . $password, TRUE);
+			} while (--$count);
+		} else {
+			$hash = pack('H*', md5($salt . $password));
+			do {
+				$hash = pack('H*', md5($hash . $password));
+			} while (--$count);
+		}
+
+		$output = substr($setting, 0, 12);
+		$output .= $this->encode64($hash, 16);
+
+		return $output;
+	}
+
+	function gensalt_extended($input)
+	{
+		$count_log2 = min($this->iteration_count_log2 + 8, 24);
+		# This should be odd to not reveal weak DES keys, and the
+		# maximum valid value is (2**24 - 1) which is odd anyway.
+		$count = (1 << $count_log2) - 1;
+
+		$output = '_';
+		$output .= $this->itoa64[$count & 0x3f];
+		$output .= $this->itoa64[($count >> 6) & 0x3f];
+		$output .= $this->itoa64[($count >> 12) & 0x3f];
+		$output .= $this->itoa64[($count >> 18) & 0x3f];
+
+		$output .= $this->encode64($input, 3);
+
+		return $output;
+	}
+
+	function gensalt_blowfish($input)
+	{
+		# This one needs to use a different order of characters and a
+		# different encoding scheme from the one in encode64() above.
+		# We care because the last character in our encoded string will
+		# only represent 2 bits.  While two known implementations of
+		# bcrypt will happily accept and correct a salt string which
+		# has the 4 unused bits set to non-zero, we do not want to take
+		# chances and we also do not want to waste an additional byte
+		# of entropy.
+		$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+		$output = '$2a$';
+		$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
+		$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
+		$output .= '$';
+
+		$i = 0;
+		do {
+			$c1 = ord($input[$i++]);
+			$output .= $itoa64[$c1 >> 2];
+			$c1 = ($c1 & 0x03) << 4;
+			if ($i >= 16) {
+				$output .= $itoa64[$c1];
+				break;
+			}
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 4;
+			$output .= $itoa64[$c1];
+			$c1 = ($c2 & 0x0f) << 2;
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 6;
+			$output .= $itoa64[$c1];
+			$output .= $itoa64[$c2 & 0x3f];
+		} while (1);
+
+		return $output;
+	}
+
+	function HashPassword($password)
+	{
+		$random = '';
+
+		if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
+			$random = $this->get_random_bytes(16);
+			$hash =
+			    crypt($password, $this->gensalt_blowfish($random));
+			if (strlen($hash) == 60)
+				return $hash;
+		}
+
+		if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
+			if (strlen($random) < 3)
+				$random = $this->get_random_bytes(3);
+			$hash =
+			    crypt($password, $this->gensalt_extended($random));
+			if (strlen($hash) == 20)
+				return $hash;
+		}
+
+		if (strlen($random) < 6)
+			$random = $this->get_random_bytes(6);
+		$hash =
+		    $this->crypt_private($password,
+		    $this->gensalt_private($random));
+		if (strlen($hash) == 34)
+			return $hash;
+
+		# Returning '*' on error is safe here, but would _not_ be safe
+		# in a crypt(3)-like function used _both_ for generating new
+		# hashes and for validating passwords against existing hashes.
+		return '*';
+	}
+
+	function CheckPassword($password, $stored_hash)
+	{
+		$hash = $this->crypt_private($password, $stored_hash);
+		if ($hash[0] == '*')
+			$hash = crypt($password, $stored_hash);
+
+		return $hash == $stored_hash;
+	}
+}
+
+?>
diff --git a/3rdparty/phpass/c/Makefile b/3rdparty/phpass/c/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fe48917f7f5d482186166dc19bc9b5ca69ab9193
--- /dev/null
+++ b/3rdparty/phpass/c/Makefile
@@ -0,0 +1,21 @@
+#
+# Written by Solar Designer and placed in the public domain.
+# See crypt_private.c for more information.
+#
+CC = gcc
+LD = $(CC)
+RM = rm -f
+CFLAGS = -Wall -O2 -fomit-frame-pointer -funroll-loops
+LDFLAGS = -s
+LIBS = -lcrypto
+
+all: crypt_private-test
+
+crypt_private-test: crypt_private-test.o
+	$(LD) $(LDFLAGS) $(LIBS) crypt_private-test.o -o $@
+
+crypt_private-test.o: crypt_private.c
+	$(CC) -c $(CFLAGS) crypt_private.c -DTEST -o $@
+
+clean:
+	$(RM) crypt_private-test*
diff --git a/3rdparty/phpass/c/crypt_private.c b/3rdparty/phpass/c/crypt_private.c
new file mode 100644
index 0000000000000000000000000000000000000000..6abc05bc1de1cfa3595e49cd6d639298d81284cc
--- /dev/null
+++ b/3rdparty/phpass/c/crypt_private.c
@@ -0,0 +1,106 @@
+/*
+ * This code exists for the sole purpose to serve as another implementation
+ * of the "private" password hashing method implemened in PasswordHash.php
+ * and thus to confirm that these password hashes are indeed calculated as
+ * intended.
+ *
+ * Other uses of this code are discouraged.  There are much better password
+ * hashing algorithms available to C programmers; one of those is bcrypt:
+ *
+ *	http://www.openwall.com/crypt/
+ *
+ * Written by Solar Designer <solar at openwall.com> in 2005 and placed in
+ * the public domain.
+ *
+ * There's absolutely no warranty.
+ */
+
+#include <string.h>
+#include <openssl/md5.h>
+
+#ifdef TEST
+#include <stdio.h>
+#endif
+
+static char *itoa64 =
+	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void encode64(char *dst, char *src, int count)
+{
+	int i, value;
+
+	i = 0;
+	do {
+		value = (unsigned char)src[i++];
+		*dst++ = itoa64[value & 0x3f];
+		if (i < count)
+			value |= (unsigned char)src[i] << 8;
+		*dst++ = itoa64[(value >> 6) & 0x3f];
+		if (i++ >= count)
+			break;
+		if (i < count)
+			value |= (unsigned char)src[i] << 16;
+		*dst++ = itoa64[(value >> 12) & 0x3f];
+		if (i++ >= count)
+			break;
+		*dst++ = itoa64[(value >> 18) & 0x3f];
+	} while (i < count);
+}
+
+char *crypt_private(char *password, char *setting)
+{
+	static char output[35];
+	MD5_CTX ctx;
+	char hash[MD5_DIGEST_LENGTH];
+	char *p, *salt;
+	int count_log2, length, count;
+
+	strcpy(output, "*0");
+	if (!strncmp(setting, output, 2))
+		output[1] = '1';
+
+	if (strncmp(setting, "$P$", 3))
+		return output;
+
+	p = strchr(itoa64, setting[3]);
+	if (!p)
+		return output;
+	count_log2 = p - itoa64;
+	if (count_log2 < 7 || count_log2 > 30)
+		return output;
+
+	salt = setting + 4;
+	if (strlen(salt) < 8)
+		return output;
+
+	length = strlen(password);
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, salt, 8);
+	MD5_Update(&ctx, password, length);
+	MD5_Final(hash, &ctx);
+
+	count = 1 << count_log2;
+	do {
+		MD5_Init(&ctx);
+		MD5_Update(&ctx, hash, MD5_DIGEST_LENGTH);
+		MD5_Update(&ctx, password, length);
+		MD5_Final(hash, &ctx);
+	} while (--count);
+
+	memcpy(output, setting, 12);
+	encode64(&output[12], hash, MD5_DIGEST_LENGTH);
+
+	return output;
+}
+
+#ifdef TEST
+int main(int argc, char **argv)
+{
+	if (argc != 3) return 1;
+
+	puts(crypt_private(argv[1], argv[2]));
+
+	return 0;
+}
+#endif
diff --git a/3rdparty/phpass/test.php b/3rdparty/phpass/test.php
new file mode 100644
index 0000000000000000000000000000000000000000..2f4a41c8c31ca5ee8a1efdcfc9c4f3ee3adc88bb
--- /dev/null
+++ b/3rdparty/phpass/test.php
@@ -0,0 +1,72 @@
+<?php
+#
+# This is a test program for the portable PHP password hashing framework.
+#
+# Written by Solar Designer and placed in the public domain.
+# See PasswordHash.php for more information.
+#
+
+require 'PasswordHash.php';
+
+header('Content-type: text/plain');
+
+$ok = 0;
+
+# Try to use stronger but system-specific hashes, with a possible fallback to
+# the weaker portable hashes.
+$t_hasher = new PasswordHash(8, FALSE);
+
+$correct = 'test12345';
+$hash = $t_hasher->HashPassword($correct);
+
+print 'Hash: ' . $hash . "\n";
+
+$check = $t_hasher->CheckPassword($correct, $hash);
+if ($check) $ok++;
+print "Check correct: '" . $check . "' (should be '1')\n";
+
+$wrong = 'test12346';
+$check = $t_hasher->CheckPassword($wrong, $hash);
+if (!$check) $ok++;
+print "Check wrong: '" . $check . "' (should be '0' or '')\n";
+
+unset($t_hasher);
+
+# Force the use of weaker portable hashes.
+$t_hasher = new PasswordHash(8, TRUE);
+
+$hash = $t_hasher->HashPassword($correct);
+
+print 'Hash: ' . $hash . "\n";
+
+$check = $t_hasher->CheckPassword($correct, $hash);
+if ($check) $ok++;
+print "Check correct: '" . $check . "' (should be '1')\n";
+
+$check = $t_hasher->CheckPassword($wrong, $hash);
+if (!$check) $ok++;
+print "Check wrong: '" . $check . "' (should be '0' or '')\n";
+
+# A correct portable hash for 'test12345'.
+# Please note the use of single quotes to ensure that the dollar signs will
+# be interpreted literally.  Of course, a real application making use of the
+# framework won't store password hashes within a PHP source file anyway.
+# We only do this for testing.
+$hash = '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0';
+
+print 'Hash: ' . $hash . "\n";
+
+$check = $t_hasher->CheckPassword($correct, $hash);
+if ($check) $ok++;
+print "Check correct: '" . $check . "' (should be '1')\n";
+
+$check = $t_hasher->CheckPassword($wrong, $hash);
+if (!$check) $ok++;
+print "Check wrong: '" . $check . "' (should be '0' or '')\n";
+
+if ($ok == 6)
+	print "All tests have PASSED\n";
+else
+	print "Some tests have FAILED\n";
+
+?>
diff --git a/apps/bookmarks/addBm.php b/apps/bookmarks/addBm.php
index f56022c42c2b4405c9f43c4cade29f4524fc316f..861b677222d2232f5ba7d4f12f49d11363c4b2b1 100644
--- a/apps/bookmarks/addBm.php
+++ b/apps/bookmarks/addBm.php
@@ -28,19 +28,6 @@ OC_Util::checkLoggedIn();
 OC_Util::checkAppEnabled('bookmarks');
 
 require_once('bookmarksHelper.php');
+addBookmark($_GET['url'], '', 'Read-Later');
 
-OC_App::setActiveNavigationEntry( 'bookmarks_index' );
-
-OC_Util::addScript('bookmarks','addBm');
-OC_Util::addStyle('bookmarks', 'bookmarks');
-
-$tmpl = new OC_Template( 'bookmarks', 'addBm', 'user' );
-
-$url = isset($_GET['url']) ? urldecode($_GET['url']) : '';
-$metadata = getURLMetadata($url);
-
-$tmpl->assign('URL', htmlentities($metadata['url'],ENT_COMPAT,'utf-8'));
-$title = isset($metadata['title']) ? $metadata['title'] : (isset($_GET['title']) ? $_GET['title'] : '');
-$tmpl->assign('TITLE', htmlentities($title,ENT_COMPAT,'utf-8'));
-
-$tmpl->printPage();
+include 'templates/addBm.php';
diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php
index 45b16ae5fa6e4a76c93b200f959187f844626349..8cda7f0f060dc4d03495cfe5fe89af405ef646e4 100644
--- a/apps/bookmarks/ajax/addBookmark.php
+++ b/apps/bookmarks/ajax/addBookmark.php
@@ -30,50 +30,6 @@ require_once('../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('bookmarks');
 
-$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
-if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
-	$_ut = "strftime('%s','now')";
-} elseif($CONFIG_DBTYPE == 'pgsql') {
-	$_ut = 'date_part(\'epoch\',now())::integer';
-} else {
-	$_ut = "UNIX_TIMESTAMP()";
-}
-
-//FIXME: Detect when user adds a known URL
-$query = OC_DB::prepare("
-	INSERT INTO *PREFIX*bookmarks
-	(url, title, user_id, public, added, lastmodified)
-	VALUES (?, ?, ?, 0, $_ut, $_ut)
-	");
-	
-	
-$params=array(
-	htmlspecialchars_decode($_GET["url"]),
-	htmlspecialchars_decode($_GET["title"]),
-	OC_User::getUser()
-	);
-$query->execute($params);
-
-$b_id = OC_DB::insertid('*PREFIX*bookmarks');
-
-
-if($b_id !== false) {
-	$query = OC_DB::prepare("
-		INSERT INTO *PREFIX*bookmarks_tags
-		(bookmark_id, tag)
-		VALUES (?, ?)
-		");
-		
-	$tags = explode(' ', urldecode($_GET["tags"]));
-	foreach ($tags as $tag) {
-		if(empty($tag)) {
-			//avoid saving blankspaces
-			continue;
-		}
-		$params = array($b_id, trim($tag));
-	    $query->execute($params);
-	}
-
-	OC_JSON::success(array('data' => $b_id));
-}
-
+require_once('../bookmarksHelper.php');
+$id = addBookmark($_GET['url'], $_GET['title'], $_GET['tags']);
+OC_JSON::success(array('data' => $id));
\ No newline at end of file
diff --git a/apps/bookmarks/ajax/getMeta.php b/apps/bookmarks/ajax/getMeta.php
deleted file mode 100644
index ca797315ef47cc54b1601e1feb35319fb1b509a6..0000000000000000000000000000000000000000
--- a/apps/bookmarks/ajax/getMeta.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
-* ownCloud - bookmarks plugin
-*
-* @author Arthur Schiwon
-* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
-* 
-* 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 Lesser General Public 
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-* 
-*/
-
-//no apps or filesystem
-$RUNTIME_NOSETUPFS=true;
-
-require_once('../../../lib/base.php');
-
-// Check if we are a user
-OC_JSON::checkLoggedIn();
-OC_JSON::checkAppEnabled('bookmarks');
-
-// $metadata = array();
-
-require '../bookmarksHelper.php';
-$metadata = getURLMetadata(htmlspecialchars_decode($_GET["url"]));
-
-
-OC_JSON::success(array('data' => $metadata));
diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php
index 479d8ed476751a830ef4486c6dbdd68c6ca6dab8..09d7b5df525bb13a83eb39ff4510cf4d491a724f 100644
--- a/apps/bookmarks/appinfo/app.php
+++ b/apps/bookmarks/appinfo/app.php
@@ -1,6 +1,6 @@
 <?php
 /**
-* Copyright (c) 2011 Marvin Thomas Rabe <m.rabe@echtzeitraum.de>
+* Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de>
 * Copyright (c) 2011 Arthur Schiwon <blizzz@arthur-schiwon.de>
 * This file is licensed under the Affero General Public License version 3 or
 * later.
@@ -8,6 +8,7 @@
 */
 
 OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'apps/bookmarks/lib/bookmarks.php';
+OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'apps/bookmarks/lib/search.php';
 
 OC_App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' ));
 
@@ -15,5 +16,5 @@ $l = new OC_l10n('bookmarks');
 OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OC_Helper::linkTo( 'bookmarks', 'index.php' ), 'icon' => OC_Helper::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => $l->t('Bookmarks')));
 
 OC_App::registerPersonal('bookmarks', 'settings');
-require_once('apps/bookmarks/lib/search.php');
 OC_Util::addScript('bookmarks','bookmarksearch');
+OC_Search::registerProvider('OC_Search_Provider_Bookmarks');
diff --git a/apps/bookmarks/appinfo/database.xml b/apps/bookmarks/appinfo/database.xml
index fca38ad84b2d5c5e2d82098cd54360635765e225..f2fc68e4b5824ed8c4690a12f8cbd6ab936ad947 100644
--- a/apps/bookmarks/appinfo/database.xml
+++ b/apps/bookmarks/appinfo/database.xml
@@ -75,14 +75,6 @@
 					<sorting>descending</sorting>
 				</field>
 			</index>
-<!--			<index>
-				<name>url</name>
-				<unique>true</unique>
-				<field>
-					<name>url</name>
-					<sorting>ascending</sorting>
-				</field>
-			</index>-->
 		</declaration>
 	</table>
 	
diff --git a/apps/bookmarks/appinfo/info.xml b/apps/bookmarks/appinfo/info.xml
index 23aa6c219a9fb6be93bb66bb4813ebe64e69fb1d..862ab805a60f2eafd29afa57e02bcac9c75113ea 100644
--- a/apps/bookmarks/appinfo/info.xml
+++ b/apps/bookmarks/appinfo/info.xml
@@ -3,8 +3,8 @@
 	<id>bookmarks</id>
 	<name>Bookmarks</name>
 	<description>Bookmark manager for ownCloud</description>
-	<version>0.1</version>
+	<version>0.2</version>
 	<licence>AGPL</licence>
-	<author>Arthur Schiwon</author>
+	<author>Arthur Schiwon, Marvin Thomas Rabe</author>
 	<require>2</require>
 </info>
\ No newline at end of file
diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php
index ac512fbc241f2bd98d17efc859619fd1a43749e8..8def7401e2ffd5511c9aae67e308d559f6e2f477 100644
--- a/apps/bookmarks/bookmarksHelper.php
+++ b/apps/bookmarks/bookmarksHelper.php
@@ -70,3 +70,55 @@ function getURLMetadata($url) {
 
 	return $metadata;
 }
+
+function addBookmark($url, $title='', $tags='') {
+	$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
+	if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
+		$_ut = "strftime('%s','now')";
+	} elseif($CONFIG_DBTYPE == 'pgsql') {
+		$_ut = 'date_part(\'epoch\',now())::integer';
+	} else {
+		$_ut = "UNIX_TIMESTAMP()";
+	}
+	
+	//FIXME: Detect when user adds a known URL
+	$query = OC_DB::prepare("
+		INSERT INTO *PREFIX*bookmarks
+		(url, title, user_id, public, added, lastmodified)
+		VALUES (?, ?, ?, 0, $_ut, $_ut)
+		");
+	
+	if(empty($title)) {
+		$metadata = getURLMetadata($url);
+		$title = $metadata['title'];
+	}
+	
+	$params=array(
+	htmlspecialchars_decode($url),
+	htmlspecialchars_decode($title),
+	OC_User::getUser()
+	);
+	$query->execute($params);
+	
+	$b_id = OC_DB::insertid('*PREFIX*bookmarks');
+	
+	if($b_id !== false) {
+		$query = OC_DB::prepare("
+			INSERT INTO *PREFIX*bookmarks_tags
+			(bookmark_id, tag)
+			VALUES (?, ?)
+			");
+	
+		$tags = explode(' ', urldecode($tags));
+		foreach ($tags as $tag) {
+			if(empty($tag)) {
+				//avoid saving blankspaces
+				continue;
+			}
+			$params = array($b_id, trim($tag));
+			$query->execute($params);
+		}
+	
+		return $b_id;
+	}
+}
\ No newline at end of file
diff --git a/apps/bookmarks/css/bookmarks.css b/apps/bookmarks/css/bookmarks.css
index 8dfdc8a07b948760a866c86c61bec3c28ef88e15..3a3e0fbf6b392bd07f339790d0093b1cf5ca83e6 100644
--- a/apps/bookmarks/css/bookmarks.css
+++ b/apps/bookmarks/css/bookmarks.css
@@ -1,4 +1,8 @@
-#content { overflow: auto; }
+#content { overflow: auto; height: 100%; }
+#firstrun { width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em;  color:#777;}
+#firstrun small { display: block; font-weight: normal; font-size: 0.5em; margin-bottom: 1.5em; }
+#firstrun .button { font-size: 0.7em; }
+#firstrun #selections { font-size:0.8em;  font-weight: normal; width: 100%; margin: 2em auto auto auto; clear: both; }
 
 .bookmarks_headline {
 	font-size: large;
@@ -12,13 +16,10 @@
 	padding: 0.5ex;
 }
 
-.bookmarks_add {
-	display: none;
-	margin-top: 45px;
-}
-
 .bookmarks_list {
-	margin-top: 36px;
+	overflow: auto;
+	position: fixed;
+	top: 6.5em;
 }
 
 .bookmarks_addBml {
@@ -32,7 +33,7 @@
 }
 
 .bookmarks_input {
-	width: 20em;
+	width: 8em;
 }
 
 .bookmark_actions {
@@ -84,16 +85,3 @@
 	display: none;
 	margin-left: 5px;
 }
-
-#footer {
-	color: #999;
-	font-size: medium;
-	text-align: center;
-	position: absolute;
-	bottom: 10px;
-	left: 0px;
-	width: 100%;
-	height: 20px;
-	visibility: visible;
-	display: block
-}
diff --git a/apps/bookmarks/js/addBm.js b/apps/bookmarks/js/addBm.js
index 6e13b59bb2e6e686900011bc6893ef2e67550b6d..d64e55e8920c0ab16af88f79d276b626dd6ae19b 100644
--- a/apps/bookmarks/js/addBm.js
+++ b/apps/bookmarks/js/addBm.js
@@ -4,13 +4,12 @@ $(document).ready(function() {
 
 function addBookmark(event) {
 	var url = $('#bookmark_add_url').val();
-	var title = $('#bookmark_add_title').val();
 	var tags = $('#bookmark_add_tags').val();
 	$.ajax({
 		url: 'ajax/addBookmark.php',
-		data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags),
+		data: 'url=' + encodeURI(url) + '&tags=' + encodeURI(tags),
 		success: function(data){ 
-			location.href='index.php';
+			window.close();
 		}
 	});
 }
\ No newline at end of file
diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js
index fadbbd5513ae43c7c5a68f87fa7486e3ab464be8..fa5adde2545a4abf3473feaeb9a822b0885e503d 100644
--- a/apps/bookmarks/js/bookmarks.js
+++ b/apps/bookmarks/js/bookmarks.js
@@ -3,19 +3,16 @@ var bookmarks_loading = false;
 
 var bookmarks_sorting = 'bookmarks_sorting_recent';
 
-$(document).ready(function() {
-	$('.bookmarks_addBtn').click(function(event){
-		$('.bookmarks_add').slideToggle();
-	});
-	
+$(document).ready(function() {	
 	$('#bookmark_add_submit').click(addOrEditBookmark);
-	$(window).scroll(updateOnBottom);
-	
-	$('#bookmark_add_url').focusout(getMetadata);
+	$(window).resize(function () {
+		fillWindow($('.bookmarks_list'));
+	});
+	$(window).resize();
+	$($('.bookmarks_list')).scroll(updateOnBottom);
 	
 	$('.bookmarks_list').empty();
 	getBookmarks();
-	
 });
 
 function getBookmarks() {
@@ -28,13 +25,19 @@ function getBookmarks() {
 		url: 'ajax/updateList.php',
 		data: 'tag=' + encodeURI($('#bookmarkFilterTag').val()) + '&page=' + bookmarks_page + '&sort=' + bookmarks_sorting,
 		success: function(bookmarks){
-			bookmarks_page += 1;
+			if (bookmarks.data.length) {
+				bookmarks_page += 1;
+			}
 			$('.bookmark_link').unbind('click', recordClick);
 			$('.bookmark_delete').unbind('click', delBookmark);
 			$('.bookmark_edit').unbind('click', showBookmark);
 	
 			for(var i in bookmarks.data) {
 				updateBookmarksList(bookmarks.data[i]);
+				$("#firstrun").hide();
+			}
+			if($('.bookmarks_list').is(':empty')) {
+				$("#firstrun").show();
 			}
 
 			$('.bookmark_link').click(recordClick);
@@ -42,24 +45,13 @@ function getBookmarks() {
 			$('.bookmark_edit').click(showBookmark);
 			
 			bookmarks_loading = false;
+			if (bookmarks.data.length) {
+				updateOnBottom()
+			}
 		}
 	});	
 }
 
-function getMetadata() {
-	var url = encodeEntities($('#bookmark_add_url').val());
-	$('.loading_meta').css('display','inline');
-	$.ajax({
-		url: 'ajax/getMeta.php',
-		data: 'url=' + encodeURIComponent(url),
-		success: function(pageinfo){
-			$('#bookmark_add_url').val(pageinfo.data.url);
-			$('#bookmark_add_title').val(pageinfo.data.title);
-			$('.loading_meta').css('display','none');
-		}
-	});
-}
-
 // function addBookmark() {
 // Instead of creating editBookmark() function, Converted the one above to
 // addOrEditBookmark() to make .js file more compact.
@@ -69,35 +61,17 @@ function addOrEditBookmark(event) {
 	var url = encodeEntities($('#bookmark_add_url').val());
 	var title = encodeEntities($('#bookmark_add_title').val());
 	var tags = encodeEntities($('#bookmark_add_tags').val());
-	var taglist = tags.split(' ');
-	var tagshtml = '';
-	for ( var i=0, len=taglist.length; i<len; ++i ){
-		tagshtml += '<a class="bookmark_tag" href="?tag=' + encodeURI(taglist[i]) + '">' + taglist[i] + '</a> ';
-	}
+	$("#firstrun").hide();
 	
 	if (id == 0) {
 		$.ajax({
 			url: 'ajax/addBookmark.php',
 			data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags),
 			success: function(response){ 
-				var bookmark_id = response.data;
-				$('.bookmarks_add').slideToggle(); 
-				$('.bookmarks_add').children('p').children('.bookmarks_input').val(''); 
-				$('.bookmarks_list').prepend(
-				'<div class="bookmark_single" data-id="' + bookmark_id + '" >' +
-					'<p class="bookmark_actions">' +
-						'<span class="bookmark_delete">' +
-							'<img class="svg" src="'+OC.imagePath('core', 'actions/delete')+'" title="Delete">' +
-						'</span>&nbsp;' +
-						'<span class="bookmark_edit">' +
-							'<img class="svg" src="'+OC.imagePath('core', 'actions/rename')+'" title="Edit">' +
-						'</span>' +
-					'</p>' +
-					'<p class="bookmark_title"><a href="' + url + '" target="_blank" class="bookmark_link">' + title + '</a></p>' +
-					'<p class="bookmark_tags">' + tagshtml + '</p>' +
-					'<p class="bookmark_url">' + url + '</p>' +
-				'</div>'
-				);
+				$('.bookmarks_input').val(''); 
+				$('.bookmarks_list').empty();
+				bookmarks_page = 0;
+				getBookmarks();
 			}
 		});
 	}
@@ -106,18 +80,11 @@ function addOrEditBookmark(event) {
 			url: 'ajax/editBookmark.php',
 			data: 'id=' + id + '&url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags),
 			success: function(){ 
-				$('.bookmarks_add').slideToggle(); 
-				$('.bookmarks_add').children('p').children('.bookmarks_input').val(''); 
+				$('.bookmarks_input').val('');
 				$('#bookmark_add_id').val('0');
-				
-				var record = $('.bookmark_single[data-id = "' + id + '"]');
-				record.children('.bookmark_url:first').text(url);
-				
-				var record_title = record.children('.bookmark_title:first').children('a:first');
-				record_title.attr('href', url);
-				record_title.text(title);
-				
-				record.children('.bookmark_tags:first').html(tagshtml);
+				$('.bookmarks_list').empty();
+				bookmarks_page = 0;
+				getBookmarks();
 			}
 		});
 	}
@@ -129,7 +96,12 @@ function delBookmark(event) {
 	$.ajax({
 		url: 'ajax/delBookmark.php',
 		data: 'url=' + encodeURI($(this).parent().parent().children('.bookmark_url:first').text()),
-		success: function(data){ record.animate({ opacity: 'hide' }, 'fast'); }
+		success: function(data){
+			record.remove();
+			if($('.bookmarks_list').is(':empty')) {
+				$("#firstrun").show();
+			}
+		}
 	});
 }
 
@@ -159,15 +131,16 @@ function updateBookmarksList(bookmark) {
 	if(!hasProtocol(bookmark.url)) {
 		bookmark.url = 'http://' + bookmark.url;
 	}
+	if(bookmark.title == '') bookmark.title = bookmark.url;
 	$('.bookmarks_list').append(
 		'<div class="bookmark_single" data-id="' + bookmark.id +'" >' +
 			'<p class="bookmark_actions">' +
-				'<span class="bookmark_delete">' +
-					'<img class="svg" src="'+OC.imagePath('core', 'actions/delete')+'" title="Delete">' +
-				'</span>&nbsp;' +
 				'<span class="bookmark_edit">' +
 					'<img class="svg" src="'+OC.imagePath('core', 'actions/rename')+'" title="Edit">' +
 				'</span>' +
+				'<span class="bookmark_delete">' +
+					'<img class="svg" src="'+OC.imagePath('core', 'actions/delete')+'" title="Delete">' +
+				'</span>&nbsp;' +
 			'</p>' +
 			'<p class="bookmark_title">'+
 				'<a href="' + encodeEntities(bookmark.url) + '" target="_blank" class="bookmark_link">' + encodeEntities(bookmark.title) + '</a>' +
@@ -182,7 +155,11 @@ function updateBookmarksList(bookmark) {
 
 function updateOnBottom() {
 	//check wether user is on bottom of the page
-	if ($('body').height() <= ($(window).height() + $(window).scrollTop())) {
+	var top = $('.bookmarks_list>:last-child').position().top;
+	var height = $('.bookmarks_list').height();
+	// use a bit of margin to begin loading before we are really at the
+	// bottom
+	if (top < height * 1.2) {
 		getBookmarks();
 	}
 }
diff --git a/apps/bookmarks/lib/search.php b/apps/bookmarks/lib/search.php
index 59495db82eac16151d1a95afa35d9b708f61246e..235587855d9aeaa0c4e039f36fef79e970830d8d 100644
--- a/apps/bookmarks/lib/search.php
+++ b/apps/bookmarks/lib/search.php
@@ -20,8 +20,8 @@
  *
  */
 
-class OC_Search_Provider_Bookmarks extends OC_Search_Provider{
-	function search($query){
+class OC_Search_Provider_Bookmarks implements OC_Search_Provider{
+	static function search($query){
 		$results=array();
 		
 		$offset = 0;
@@ -45,6 +45,3 @@ class OC_Search_Provider_Bookmarks extends OC_Search_Provider{
 		return $results;
 	}
 }
-new OC_Search_Provider_Bookmarks();
-
-?>
diff --git a/apps/bookmarks/settings.php b/apps/bookmarks/settings.php
index 0ace04fa2c8e3efe0b6fb481b105a21f4ae772e9..9d945f64dadc0a20ac8b011240c3bd543e30a34e 100644
--- a/apps/bookmarks/settings.php
+++ b/apps/bookmarks/settings.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Copyright (c) 2011 Marvin Thomas Rabe <m.rabe@echtzeitraum.de>
+ * Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de>
  * This file is licensed under the Affero General Public License version 3 or
  * later.
  * See the COPYING-README file.
@@ -8,6 +8,4 @@
 
 $tmpl = new OC_Template( 'bookmarks', 'settings');
 
-//OC_Util::addScript('bookmarks','settings');
-
 return $tmpl->fetchPage();
diff --git a/apps/bookmarks/templates/addBm.php b/apps/bookmarks/templates/addBm.php
index c285c3579c565e82d459130c0b035df498b32017..dbe673f53a8b87a07c221b3a7bb2a8ad3e483956 100644
--- a/apps/bookmarks/templates/addBm.php
+++ b/apps/bookmarks/templates/addBm.php
@@ -1,7 +1,11 @@
-<div class="bookmarks_addBm">
-	<p><label class="bookmarks_label"><?php echo $l->t('Address'); ?></label><input type="text" id="bookmark_add_url" class="bookmarks_input" value="<?php echo $_['URL']; ?>"/></p>
-	<p><label class="bookmarks_label"><?php echo $l->t('Title'); ?></label><input type="text" id="bookmark_add_title" class="bookmarks_input" value="<?php echo $_['TITLE']; ?>" /></p>
-	<p><label class="bookmarks_label"><?php echo $l->t('Tags'); ?></label><input type="text" id="bookmark_add_tags" class="bookmarks_input" /></p>
-	<p><label class="bookmarks_label"> </label><label class="bookmarks_hint"><?php echo $l->t('Hint: Use space to separate tags.'); ?></label></p>
-	<p><label class="bookmarks_label"></label><input type="submit" value="<?php echo $l->t('Add bookmark'); ?>" id="bookmark_add_submit" /></p>
-</div>
+<!DOCTYPE html>  
+<html lang="en">  
+  <head>  
+    <meta charset="utf-8">  
+    <title>Read later - ownCloud</title>  
+    <link rel="stylesheet" href="css/readlater.css">  
+  </head>
+  <body>
+  	<div class="message"><h1>Saved!</h1></div>
+  </body>  
+</html>  
\ No newline at end of file
diff --git a/apps/bookmarks/templates/bookmarklet.php b/apps/bookmarks/templates/bookmarklet.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ea67f04df2d32f6943e8c8e0b3b60fb6302164a
--- /dev/null
+++ b/apps/bookmarks/templates/bookmarklet.php
@@ -0,0 +1,8 @@
+<?php
+
+function createBookmarklet() {
+	$l = new OC_L10N('bookmarks');
+	echo '<small>' . $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:') . '</small>'
+	. '<a class="bookmarklet" href="javascript:(function(){var a=window,b=document,c=encodeURIComponent,d=a.open(\'' . OC_Helper::linkToAbsolute('bookmarks', 'addBm.php') . '?output=popup&url=\'+c(b.location),\'bkmk_popup\',\'left=\'+((a.screenX||a.screenLeft)+10)+\',top=\'+((a.screenY||a.screenTop)+10)+\',height=230px,width=230px,resizable=1,alwaysRaised=1\');a.setTimeout(function(){d.focus()},300);})();">'
+	. $l->t('Read later') . '</a>';
+} 
diff --git a/apps/bookmarks/templates/list.php b/apps/bookmarks/templates/list.php
index d44a0ecbcdbacd0eaf141b0e0ef8d86fd88b662f..1abdbb7f838af48d94d062e395f3463a58eca76d 100644
--- a/apps/bookmarks/templates/list.php
+++ b/apps/bookmarks/templates/list.php
@@ -1,6 +1,6 @@
 <?php 
 /**
- * Copyright (c) 2011 Marvin Thomas Rabe <m.rabe@echtzeitraum.de>
+ * Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de>
  * Copyright (c) 2011 Arthur Schiwon <blizzz@arthur-schiwon.de>
  * This file is licensed under the Affero General Public License version 3 or
  * later.
@@ -9,21 +9,18 @@
 ?>
 <input type="hidden" id="bookmarkFilterTag" value="<?php if(isset($_GET['tag'])) echo htmlentities($_GET['tag']); ?>" />
 <div id="controls">
-	<input type="button" class="bookmarks_addBtn" value="<?php echo $l->t('Add bookmark'); ?>"/>
-</div>
-<div class="bookmarks_add">
 	<input type="hidden" id="bookmark_add_id" value="0" />
-	<p><label class="bookmarks_label"><?php echo $l->t('Address'); ?></label><input type="text" id="bookmark_add_url" class="bookmarks_input" /></p>
-	<p><label class="bookmarks_label"><?php echo $l->t('Title'); ?></label><input type="text" id="bookmark_add_title" class="bookmarks_input" />
-       <img class="loading_meta" src="<?php echo OC_Helper::imagePath('core', 'loading.gif'); ?>" /></p>
-	<p><label class="bookmarks_label"><?php echo $l->t('Tags'); ?></label><input type="text" id="bookmark_add_tags" class="bookmarks_input" /></p>
-	<p><label class="bookmarks_label"> </label><label class="bookmarks_hint"><?php echo $l->t('Hint: Use space to separate tags.'); ?></label></p>
-	<p><label class="bookmarks_label"></label><input type="submit" value="<?php echo $l->t('Add bookmark'); ?>" id="bookmark_add_submit" /></p>
+	<input type="text" id="bookmark_add_url" placeholder="<?php echo $l->t('Address'); ?>" class="bookmarks_input" />
+	<input type="text" id="bookmark_add_title" placeholder="<?php echo $l->t('Title'); ?>" class="bookmarks_input" />
+	<input type="text" id="bookmark_add_tags" placeholder="<?php echo $l->t('Tags'); ?>" class="bookmarks_input" />
+	<input type="submit" value="<?php echo $l->t('Add bookmark'); ?>" id="bookmark_add_submit" />
 </div>
 <div class="bookmarks_list">
-	<?php echo $l->t('You have no bookmarks'); ?>
 </div>
-<div id="footer">
-Bookmark pages more easily. Drag this bookmarklet to the Bookmarks bar of your browser:
-<a style='background-color:#dddddd;border:1px groove #999; padding:5px;padding-top:0px;padding-bottom:2px; text-decoration:none; margin-top:5px' href='javascript:(function(){var a=window,b=document,c=encodeURIComponent,d=a.open("<?php echo OC_Helper::linkToAbsolute('bookmarks', 'addBm.php') ?>?output=popup&url="+c(b.location)+"&title="+c(b.title),"bkmk_popup","left="+((a.screenX||a.screenLeft)+10)+",top="+((a.screenY||a.screenTop)+10)+",height=510px,width=550px,resizable=1,alwaysRaised=1");a.setTimeout(function(){d.focus()},300)})();'>ownCloud bookmark</a>
+<div id="firstrun" style="display: none;">
+	<?php
+		echo $l->t('You have no bookmarks');
+		require_once('bookmarklet.php');
+		createBookmarklet(); 
+	?>
 </div>
diff --git a/apps/bookmarks/templates/settings.php b/apps/bookmarks/templates/settings.php
index a985ee9d61b0b8e2d811372bfd3ca60b6dd22ad9..31edf7478bfed53c5290ed207a387b840fa91b94 100644
--- a/apps/bookmarks/templates/settings.php
+++ b/apps/bookmarks/templates/settings.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Copyright (c) 2011 Marvin Thomas Rabe <m.rabe@echtzeitraum.de>
+ * Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de>
  * This file is licensed under the Affero General Public License version 3 or
  * later.
  * See the COPYING-README file.
@@ -8,7 +8,10 @@
 ?>
 <form id="bookmarks">
 		<fieldset class="personalblock">
-			<span class="bold"><?php echo $l->t('Bookmarklet:');?></span>&nbsp;<a class="bookmarks_addBml" href="javascript:(function(){url=encodeURIComponent(location.href);window.open('<?php echo OC_Helper::linkToAbsolute('bookmarks', 'addBm.php'); ?>?url='+url, 'owncloud-bookmarks') })()"><?php echo $l->t('Add page to ownCloud'); ?></a>
-			<br/><em><?php echo $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage.'); ?></em><br />
+			<span class="bold"><?php echo $l->t('Bookmarklet <br />');?></span>
+			<?php
+			    require_once('bookmarklet.php');
+			    createBookmarklet(); 
+			?>
 		</fieldset>
 </form>
diff --git a/apps/calendar/ajax/activation.php b/apps/calendar/ajax/calendar/activation.php
similarity index 93%
rename from apps/calendar/ajax/activation.php
rename to apps/calendar/ajax/calendar/activation.php
index ada2e44547bcf6ca46ac987c5e86c9d980d45955..7677d85aff30d67c1318aee177f4fbb4c83cbfa1 100644
--- a/apps/calendar/ajax/activation.php
+++ b/apps/calendar/ajax/calendar/activation.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once ("../../../lib/base.php");
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 $calendarid = $_POST['calendarid'];
diff --git a/apps/calendar/ajax/deletecalendar.php b/apps/calendar/ajax/calendar/delete.php
similarity index 92%
rename from apps/calendar/ajax/deletecalendar.php
rename to apps/calendar/ajax/calendar/delete.php
index 901cbbfcb08e95de7e1908e134a64ecf9b58a12a..a2f5311731c5edb0b137a8e0876639109dca3fd9 100644
--- a/apps/calendar/ajax/deletecalendar.php
+++ b/apps/calendar/ajax/calendar/delete.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
diff --git a/apps/calendar/ajax/editcalendar.php b/apps/calendar/ajax/calendar/edit.form.php
similarity index 93%
rename from apps/calendar/ajax/editcalendar.php
rename to apps/calendar/ajax/calendar/edit.form.php
index 7aeb5bbe30557223e49a0fccc461a15ad8c0c713..8922b3eba4ed5e05ae174d95ff357c3b6bb71806 100644
--- a/apps/calendar/ajax/editcalendar.php
+++ b/apps/calendar/ajax/calendar/edit.form.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 
diff --git a/apps/calendar/ajax/calendar/edit.php b/apps/calendar/ajax/calendar/edit.php
new file mode 100644
index 0000000000000000000000000000000000000000..8922b3eba4ed5e05ae174d95ff357c3b6bb71806
--- /dev/null
+++ b/apps/calendar/ajax/calendar/edit.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('calendar');
+
+$calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions();
+$calendar = OC_Calendar_App::getCalendar($_GET['calendarid']);
+$tmpl = new OC_Template("calendar", "part.editcalendar");
+$tmpl->assign('new', false);
+$tmpl->assign('calendarcolor_options', $calendarcolor_options);
+$tmpl->assign('calendar', $calendar);
+$tmpl->printPage();
+?>
diff --git a/apps/calendar/ajax/newcalendar.php b/apps/calendar/ajax/calendar/new.form.php
similarity index 93%
rename from apps/calendar/ajax/newcalendar.php
rename to apps/calendar/ajax/calendar/new.form.php
index af3ba4fbbeabdc84f17d82a34c23aaec81f56b3a..6e7423cbe922ff6909f29ba32207a43c2019f5ab 100644
--- a/apps/calendar/ajax/newcalendar.php
+++ b/apps/calendar/ajax/calendar/new.form.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 $l10n = new OC_L10N('calendar');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
diff --git a/apps/calendar/ajax/createcalendar.php b/apps/calendar/ajax/calendar/new.php
similarity index 96%
rename from apps/calendar/ajax/createcalendar.php
rename to apps/calendar/ajax/calendar/new.php
index 8d7b12f9b89550b60be9cd8c7d11403b09eb8b24..228e6247724dfbf0dcfc32235ca2294b86c6a5a4 100644
--- a/apps/calendar/ajax/createcalendar.php
+++ b/apps/calendar/ajax/calendar/new.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 // Check if we are a user
 OC_JSON::checkLoggedIn();
diff --git a/apps/calendar/ajax/choosecalendar.php b/apps/calendar/ajax/calendar/overview.php
similarity index 90%
rename from apps/calendar/ajax/choosecalendar.php
rename to apps/calendar/ajax/calendar/overview.php
index 9281c8edbdcf5b1160be02998a2c7b7209cbe1e0..2f73f5d07101deea02075aeccd90b7e2bfd27ee8 100644
--- a/apps/calendar/ajax/choosecalendar.php
+++ b/apps/calendar/ajax/calendar/overview.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 $l10n = new OC_L10N('calendar');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
diff --git a/apps/calendar/ajax/updatecalendar.php b/apps/calendar/ajax/calendar/update.php
similarity index 81%
rename from apps/calendar/ajax/updatecalendar.php
rename to apps/calendar/ajax/calendar/update.php
index 20c225d8a2968e86042bb370553682ea414a9f9a..f400c8c14b43018fdf12efb541b9f422430d5ba0 100644
--- a/apps/calendar/ajax/updatecalendar.php
+++ b/apps/calendar/ajax/calendar/update.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 // Check if we are a user
 OC_JSON::checkLoggedIn();
@@ -25,16 +25,8 @@ foreach($calendars as $cal){
 }
 
 $calendarid = $_POST['id'];
-$calendarcolor = $_POST['color'];
-if (preg_match('/^#?([0-9a-f]{6})/', $calendarcolor, $matches)) {
-	$calendarcolor = '#'.$matches[1];
-}
-else {
-	$calendarcolor = null;
-}
-
 $calendar = OC_Calendar_App::getCalendar($calendarid);//access check
-OC_Calendar_Calendar::editCalendar($calendarid, strip_tags($_POST['name']), null, null, null, $calendarcolor);
+OC_Calendar_Calendar::editCalendar($calendarid, strip_tags($_POST['name']), null, null, null, $_POST['color']);
 OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
 
 $calendar = OC_Calendar_App::getCalendar($calendarid);
diff --git a/apps/calendar/ajax/changeview.php b/apps/calendar/ajax/changeview.php
index ef05c7cd496056582a73f2cbe71af2828e4cada4..ae48b229b164f6be44dbed1664fe37958fc4ed87 100644
--- a/apps/calendar/ajax/changeview.php
+++ b/apps/calendar/ajax/changeview.php
@@ -9,7 +9,16 @@
 require_once ("../../../lib/base.php");
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
-$currentview = $_GET["v"];
-OC_Preferences::setValue(OC_USER::getUser(), "calendar", "currentview", $currentview);
+$view = $_GET['v'];
+switch($view){
+	case 'agendaWeek':
+	case 'month';
+	case 'list':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter: ' . $view));
+		exit;
+}
+OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'currentview', $view);
 OC_JSON::success();
 ?>
diff --git a/apps/calendar/ajax/deleteevent.php b/apps/calendar/ajax/event/delete.php
similarity index 91%
rename from apps/calendar/ajax/deleteevent.php
rename to apps/calendar/ajax/event/delete.php
index b25a5af1a291921659db3b3a78a6d87918b44a9c..862dec6bf5b2ce92b96dfa936f854be1c9dd18cc 100644
--- a/apps/calendar/ajax/deleteevent.php
+++ b/apps/calendar/ajax/event/delete.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 $l10n = new OC_L10N('calendar');
 
diff --git a/apps/calendar/ajax/editeventform.php b/apps/calendar/ajax/event/edit.form.php
similarity index 95%
rename from apps/calendar/ajax/editeventform.php
rename to apps/calendar/ajax/event/edit.form.php
index 19f6a80a167c66bc8aea47da4a2ed005ec3b357e..837edbbbf0538f14e7f71600f803d81076f6586e 100644
--- a/apps/calendar/ajax/editeventform.php
+++ b/apps/calendar/ajax/event/edit.form.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 if(!OC_USER::isLoggedIn()) {
 	die('<script type="text/javascript">document.location = oc_webroot;</script>');
@@ -243,16 +243,6 @@ if($repeat['repeat'] != 'doesnotrepeat'){
 	$tmpl->assign('repeat_bymonth', $repeat['bymonth']);
 	$tmpl->assign('repeat_byweekno', $repeat['byweekno']);
 }
-else {
-	$tmpl->assign('repeat_month', 'monthday');
-	$tmpl->assign('repeat_weekdays', array());
-	$tmpl->assign('repeat_interval', 1);
-	$tmpl->assign('repeat_end', 'never');
-	$tmpl->assign('repeat_count', '10');
-	$tmpl->assign('repeat_weekofmonth', 'auto');
-	$tmpl->assign('repeat_date', '');
-	$tmpl->assign('repeat_year', 'bydate');
-}
 $tmpl->printpage();
 
-?>
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/editevent.php b/apps/calendar/ajax/event/edit.php
similarity index 95%
rename from apps/calendar/ajax/editevent.php
rename to apps/calendar/ajax/event/edit.php
index 5a487da17588ff369ab380badfe8dd5921df627c..64daffddef01b6aad9a7755af270681b65976368 100644
--- a/apps/calendar/ajax/editevent.php
+++ b/apps/calendar/ajax/event/edit.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 
diff --git a/apps/calendar/ajax/moveevent.php b/apps/calendar/ajax/event/move.php
similarity index 97%
rename from apps/calendar/ajax/moveevent.php
rename to apps/calendar/ajax/event/move.php
index f2256d4eee6135ad4254c12402ad9fc362b066b4..8150fdbaa3207fcee344fe15c201476fbea12d52 100644
--- a/apps/calendar/ajax/moveevent.php
+++ b/apps/calendar/ajax/event/move.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 
 $id = $_POST['id'];
diff --git a/apps/calendar/ajax/neweventform.php b/apps/calendar/ajax/event/new.form.php
similarity index 98%
rename from apps/calendar/ajax/neweventform.php
rename to apps/calendar/ajax/event/new.form.php
index 3870c879b0ed513dc586e5439474a08608a95f88..c19928a727e48d48fabc8fad6491744f7cacdf78 100644
--- a/apps/calendar/ajax/neweventform.php
+++ b/apps/calendar/ajax/event/new.form.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 if(!OC_USER::isLoggedIn()) {
 	die('<script type="text/javascript">document.location = oc_webroot;</script>');
diff --git a/apps/calendar/ajax/newevent.php b/apps/calendar/ajax/event/new.php
similarity index 93%
rename from apps/calendar/ajax/newevent.php
rename to apps/calendar/ajax/event/new.php
index c7c4d29943a0b3daf783b1801df20349197ff193..59fda79da731c7be2847406e4fe2f687649e26b7 100644
--- a/apps/calendar/ajax/newevent.php
+++ b/apps/calendar/ajax/event/new.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 $l10n = new OC_L10N('calendar');
 
diff --git a/apps/calendar/ajax/resizeevent.php b/apps/calendar/ajax/event/resize.php
similarity index 96%
rename from apps/calendar/ajax/resizeevent.php
rename to apps/calendar/ajax/event/resize.php
index 683479065299db7ac94a415d72c1c50045d5026c..aa2d420e77d595d5b8ade1288f79b1576ccfb3d2 100644
--- a/apps/calendar/ajax/resizeevent.php
+++ b/apps/calendar/ajax/event/resize.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 
 $id = $_POST['id'];
diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php
index 7734129bd954fec9d62348ef6c556c2fdd170474..922df90b76be21b50cf3c0e3912a13d99f1d1a90 100755
--- a/apps/calendar/ajax/events.php
+++ b/apps/calendar/ajax/events.php
@@ -7,7 +7,7 @@
  */
 
 require_once ('../../../lib/base.php');
-require_once('../../../3rdparty/when/When.php');
+require_once('when/When.php');
 
 function create_return_event($event, $vevent){
 	$return_event = array();
@@ -27,25 +27,40 @@ function create_return_event($event, $vevent){
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 
-$start = DateTime::createFromFormat('U', $_GET['start']);
-$end = DateTime::createFromFormat('U', $_GET['end']);
+if(version_compare(PHP_VERSION, '5.3.0', '>=')){
+	$start = DateTime::createFromFormat('U', $_GET['start']);
+	$end = DateTime::createFromFormat('U', $_GET['end']);
+}else{
+	$start = new DateTime('@' . $_GET['start']);
+	$end = new DateTime('@' . $_GET['end']);
+}
 
-$calendar = OC_Calendar_App::getCalendar($_GET['calendar_id']);
-OC_Response::enableCaching(0);
-OC_Response::setETagHeader($calendar['ctag']);
+$calendar_id = $_GET['calendar_id'];
+if (is_numeric($calendar_id)) {
+	$calendar = OC_Calendar_App::getCalendar($calendar_id);
+	OC_Response::enableCaching(0);
+	OC_Response::setETagHeader($calendar['ctag']);
+	$events = OC_Calendar_Object::allInPeriod($calendar_id, $start, $end);
+} else {
+	$events = array();
+	OC_Hook::emit('OC_Calendar', 'getEvents', array('calendar_id' => $calendar_id, 'events' => &$events));
+}
 
-$events = OC_Calendar_Object::allInPeriod($_GET['calendar_id'], $start, $end);
 $user_timezone = OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
 $return = array();
 foreach($events as $event){
-	$object = OC_VObject::parse($event['calendardata']);
-	$vevent = $object->VEVENT;
+	if (isset($event['calendardata'])) {
+		$object = OC_VObject::parse($event['calendardata']);
+		$vevent = $object->VEVENT;
+	} else {
+		$vevent = $event['vevent'];
+	}
 
 	$return_event = create_return_event($event, $vevent);
 
 	$dtstart = $vevent->DTSTART;
-	$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
 	$start_dt = $dtstart->getDateTime();
+	$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
 	$end_dt = $dtend->getDateTime();
 	if ($dtstart->getDateType() == Sabre_VObject_Element_DateTime::DATE){
 		$return_event['allDay'] = true;
diff --git a/apps/calendar/ajax/guesstimezone.php b/apps/calendar/ajax/guesstimezone.php
deleted file mode 100755
index 41aea26985ffd58799faf04ed82e2f6559180564..0000000000000000000000000000000000000000
--- a/apps/calendar/ajax/guesstimezone.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011, 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-function make_array_out_of_xml ($xml){
-	$returnarray = array();
-	$xml = (array)$xml ;
-	foreach ($xml as $property => $value){
-		$value = (array)$value;
-		if(!isset($value[0])){
-			$returnarray[$property] = make_array_out_of_xml($value);
-		}else{
-			$returnarray[$property] = trim($value[0]);
-		}
-	}
-	return $returnarray;
-}
-require_once ("../../../lib/base.php");
-OC_JSON::checkLoggedIn();
-OC_JSON::checkAppEnabled('calendar');
-$l = new OC_L10N('calendar');
-$lat = $_GET['lat'];
-$long = $_GET['long'];
-if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'position') == $lat . '-' . $long && OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone') != null){
-	OC_JSON::success();
-	exit;
-}
-OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'position', $lat . '-' . $long);
-$geolocation = file_get_contents('http://ws.geonames.org/timezone?lat=' . $lat . '&lng=' . $long);
-//Information are by Geonames (http://www.geonames.org) and licensed under the Creative Commons Attribution 3.0 License
-$geoxml = simplexml_load_string($geolocation);
-$geoarray = make_array_out_of_xml($geoxml);
-if($geoarray['timezone']['timezoneId'] == OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone')){
-	OC_JSON::success();
-	exit;
-}
-if(in_array($geoarray['timezone']['timezoneId'], DateTimeZone::listIdentifiers())){
-	OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $geoarray['timezone']['timezoneId']);
-	$message = array('message'=> $l->t('New Timezone:') . $geoarray['timezone']['timezoneId']);
-	OC_JSON::success($message);
-}else{
-	OC_JSON::error();
-}
-
-?>
diff --git a/apps/calendar/ajax/importdialog.php b/apps/calendar/ajax/import/dialog.php
similarity index 91%
rename from apps/calendar/ajax/importdialog.php
rename to apps/calendar/ajax/import/dialog.php
index f6b8453fc22593ba6bb47e1f876b03e0fd764728..2e0020921505d3f72539eaee56e15d7d4ef9c1ea 100644
--- a/apps/calendar/ajax/importdialog.php
+++ b/apps/calendar/ajax/import/dialog.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_Util::checkAppEnabled('calendar');
 $l10n = new OC_L10N('calendar');
diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php
new file mode 100644
index 0000000000000000000000000000000000000000..c0797f6e425524124ff91a96748c630e856dbb65
--- /dev/null
+++ b/apps/calendar/ajax/import/import.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+//check for calendar rights or create new one
+ob_start();
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_Util::checkAppEnabled('calendar');
+$nl = "\n";
+$progressfile = OC::$APPSROOT . '/apps/calendar/import_tmp/' . md5(session_id()) . '.txt';
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '10');
+	fclose($progressfopen);
+}
+$file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
+if($_POST['method'] == 'new'){
+	$id = OC_Calendar_Calendar::addCalendar(OC_User::getUser(), $_POST['calname']);
+	OC_Calendar_Calendar::setCalendarActive($id, 1);
+}else{
+	$calendar = OC_Calendar_App::getCalendar($_POST['id']);
+	if($calendar['userid'] != OC_USER::getUser()){
+		OC_JSON::error();
+		exit();
+	}
+	$id = $_POST['id'];
+}
+//analyse the calendar file
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '20');
+	fclose($progressfopen);
+}
+$searchfor = array('VEVENT', 'VTODO', 'VJOURNAL');
+$parts = $searchfor;
+$filearr = explode($nl, $file);
+$inelement = false;
+$parts = array();
+$i = 0;
+foreach($filearr as $line){
+	foreach($searchfor as $search){
+		if(substr_count($line, $search) == 1){
+			list($attr, $val) = explode(':', $line);
+			if($attr == 'BEGIN'){
+				$parts[]['begin'] = $i;
+				$inelement = true;
+			}
+			if($attr == 'END'){
+				$parts[count($parts) - 1]['end'] = $i;
+				$inelement = false;
+			}
+		}
+	}
+	$i++;
+}
+//import the calendar
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '40');
+	fclose($progressfopen);
+}
+$start = '';
+for ($i = 0; $i < $parts[0]['begin']; $i++) { 
+	if($i == 0){
+		$start = $filearr[0];
+	}else{
+		$start .= $nl . $filearr[$i];
+	}
+}
+$end = '';
+for($i = $parts[count($parts) - 1]['end'] + 1;$i <= count($filearr) - 1; $i++){
+	if($i == $parts[count($parts) - 1]['end'] + 1){
+		$end = $filearr[$parts[count($parts) - 1]['end'] + 1];
+	}else{
+		$end .= $nl . $filearr[$i];
+	}
+}
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '50');
+	fclose($progressfopen);
+}
+$importready = array();
+foreach($parts as $part){
+	for($i = $part['begin']; $i <= $part['end'];$i++){
+		if($i == $part['begin']){
+			$content = $filearr[$i];
+		}else{
+			$content .= $nl . $filearr[$i];
+		}
+	}
+	$importready[] = $start . $nl . $content . $nl . $end;
+}
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '70');
+	fclose($progressfopen);
+}
+if(count($parts) == 1){
+	OC_Calendar_Object::add($id, $file);
+}else{
+	foreach($importready as $import){
+		OC_Calendar_Object::add($id, $import);
+	}
+}
+//done the import
+if(is_writable('import_tmp/')){
+	$progressfopen = fopen($progressfile, 'w');
+	fwrite($progressfopen, '100');
+	fclose($progressfopen);
+}
+sleep(3);
+if(is_writable('import_tmp/')){
+	unlink($progressfile);
+}
+OC_JSON::success();
diff --git a/apps/calendar/ajax/settings/getfirstday.php b/apps/calendar/ajax/settings/getfirstday.php
new file mode 100644
index 0000000000000000000000000000000000000000..cab5870509a3718c598beee612987dfc7a8665c1
--- /dev/null
+++ b/apps/calendar/ajax/settings/getfirstday.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+$firstday = OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'firstday', 'mo');
+OC_JSON::encodedPrint(array('firstday' => $firstday));
+?> 
diff --git a/apps/calendar/ajax/gettimezonedetection.php b/apps/calendar/ajax/settings/gettimezonedetection.php
similarity index 90%
rename from apps/calendar/ajax/gettimezonedetection.php
rename to apps/calendar/ajax/settings/gettimezonedetection.php
index ae58370712da2f0bcf12a3fbc15e7ffd5c6421b0..11255fe8ef386739105d36fe99838ba729032bfe 100644
--- a/apps/calendar/ajax/gettimezonedetection.php
+++ b/apps/calendar/ajax/settings/gettimezonedetection.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once ("../../../lib/base.php");
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 OC_JSON::success(array('detection' => OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezonedetection')));
\ No newline at end of file
diff --git a/apps/calendar/ajax/settings/guesstimezone.php b/apps/calendar/ajax/settings/guesstimezone.php
new file mode 100755
index 0000000000000000000000000000000000000000..d45a70e1ce304d16375e1d7d7e8ed1e05cc6c870
--- /dev/null
+++ b/apps/calendar/ajax/settings/guesstimezone.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright (c) 2011, 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('calendar');
+
+$l = new OC_L10N('calendar');
+
+$lat = $_GET['lat'];
+$lng = $_GET['long'];
+
+$timezone =  OC_Geo::timezone($lat, $lng);
+
+if($timezone == OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone')){
+	OC_JSON::success();
+	exit;
+}
+OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $timezone);
+$message = array('message'=> $l->t('New Timezone:') . $timezone);
+OC_JSON::success($message);
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/settings/setfirstday.php b/apps/calendar/ajax/settings/setfirstday.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b6522122059598ac64f545e75e92fec9888e684
--- /dev/null
+++ b/apps/calendar/ajax/settings/setfirstday.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+if(isset($_POST["firstday"])){
+	OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'firstday', $_POST["firstday"]);
+	OC_JSON::success();
+}else{
+	OC_JSON::error();
+}
+?> 
+ 
diff --git a/apps/calendar/ajax/settimeformat.php b/apps/calendar/ajax/settings/settimeformat.php
similarity index 90%
rename from apps/calendar/ajax/settimeformat.php
rename to apps/calendar/ajax/settings/settimeformat.php
index 7805120ba5efd756c08102ec9eb3a74bd5c8946d..8f65447065c1deb22908bcf23b68d23a5bb0a358 100644
--- a/apps/calendar/ajax/settimeformat.php
+++ b/apps/calendar/ajax/settings/settimeformat.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 if(isset($_POST["timeformat"])){
 	OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'timeformat', $_POST["timeformat"]);
diff --git a/apps/calendar/ajax/settimezone.php b/apps/calendar/ajax/settings/settimezone.php
similarity index 94%
rename from apps/calendar/ajax/settimezone.php
rename to apps/calendar/ajax/settings/settimezone.php
index c726a11471d2c3c11876d545fefa7d62018595ed..c639753fe2fe14f34ba59e67babf37ecb6212d64 100644
--- a/apps/calendar/ajax/settimezone.php
+++ b/apps/calendar/ajax/settings/settimezone.php
@@ -7,7 +7,7 @@
  */
 
 // Init owncloud
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 
 $l=new OC_L10N('calendar');
 
diff --git a/apps/calendar/ajax/timeformat.php b/apps/calendar/ajax/settings/timeformat.php
similarity index 90%
rename from apps/calendar/ajax/timeformat.php
rename to apps/calendar/ajax/settings/timeformat.php
index 3533adcf8e03303c80c34a389868c7e3e1263b61..e0dbe8d3cd7b8631d6780434eb532e0fd7afaf5f 100644
--- a/apps/calendar/ajax/timeformat.php
+++ b/apps/calendar/ajax/settings/timeformat.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once('../../../lib/base.php');
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 $timeformat = OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'timeformat', "24");
 OC_JSON::encodedPrint(array("timeformat" => $timeformat));
diff --git a/apps/calendar/ajax/timezonedetection.php b/apps/calendar/ajax/settings/timezonedetection.php
similarity index 92%
rename from apps/calendar/ajax/timezonedetection.php
rename to apps/calendar/ajax/settings/timezonedetection.php
index 77e4c4f6ebe424be2e76b887601b042af426c683..f67bab901e7e6197f30360a8f32d6ed17cf2f955 100644
--- a/apps/calendar/ajax/timezonedetection.php
+++ b/apps/calendar/ajax/settings/timezonedetection.php
@@ -5,7 +5,7 @@
  * later.
  * See the COPYING-README file.
  */
-require_once ("../../../lib/base.php");
+require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('calendar');
 if($_POST['timezonedetection'] == 'on'){
diff --git a/apps/calendar/ajax/share/changepermission.php b/apps/calendar/ajax/share/changepermission.php
new file mode 100644
index 0000000000000000000000000000000000000000..d91f87b613f617487c76dcc5a2a84debd919690d
--- /dev/null
+++ b/apps/calendar/ajax/share/changepermission.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+$id = strip_tags($_GET['id']);
+$idtype = strip_tags($_GET['idtype']);
+$permission = (int) strip_tags($_GET['permission']);
+switch($idtype){
+	case 'calendar':
+	case 'event':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+$sharewith = $_GET['sharewith'];
+$sharetype = strip_tags($_GET['sharetype']);
+switch($sharetype){
+	case 'user':
+	case 'group':
+	case 'public':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+if($sharetype == 'user' && !OC_User::userExists($sharewith)){
+	OC_JSON::error(array('message'=>'user not found'));
+	exit;
+}
+if($sharetype == 'group' && !OC_Group::groupExists($sharewith)){
+	OC_JSON::error(array('message'=>'group not found'));
+	exit;
+}
+$success = OC_Calendar_Share::changepermission($sharewith, $sharetype, $id, $permission, (($idtype=='calendar') ? OC_Calendar_Share::CALENDAR : OC_Calendar_Share::Event));
+OC_JSON::success();
\ No newline at end of file
diff --git a/apps/calendar/ajax/share/dropdown.php b/apps/calendar/ajax/share/dropdown.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb396d38fd9f067c562b8e449c659852bfc28499
--- /dev/null
+++ b/apps/calendar/ajax/share/dropdown.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+$user = OC_USER::getUser();
+$calid = $_GET['calid'];
+$calendar = OC_Calendar_Calendar::find($calid);
+if($calendar['userid'] != $user){
+	OC_JSON::error();
+	exit;
+}
+$tmpl = new OC_Template('calendar', 'share.dropdown');
+$tmpl->assign('calid', $calid);
+$tmpl->printPage();
\ No newline at end of file
diff --git a/apps/calendar/ajax/share/share.php b/apps/calendar/ajax/share/share.php
new file mode 100644
index 0000000000000000000000000000000000000000..d892727701a9c5cb7ebc2f2add41af2bacbaef76
--- /dev/null
+++ b/apps/calendar/ajax/share/share.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+$id = strip_tags($_GET['id']);
+$idtype = strip_tags($_GET['idtype']);
+switch($idtype){
+	case 'calendar':
+	case 'event':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+$sharewith = $_GET['sharewith'];
+$sharetype = strip_tags($_GET['sharetype']);
+switch($sharetype){
+	case 'user':
+	case 'group':
+	case 'public':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+if($sharetype == 'user' && !OC_User::userExists($sharewith)){
+	OC_JSON::error(array('message'=>'user not found'));
+	exit;
+}
+if($sharetype == 'group' && !OC_Group::groupExists($sharewith)){
+	OC_JSON::error(array('message'=>'group not found'));
+	exit;
+}
+if($sharetype == 'user' && OC_User::getUser() == $sharewith){
+	OC_JSON::error(array('meesage'=>'you can not share with yourself'));
+}
+$success = OC_Calendar_Share::share(OC_User::getUser(), $sharewith, $sharetype, $id, (($idtype=='calendar') ? OC_Calendar_Share::CALENDAR : OC_Calendar_Share::Event));
+if($success){
+	if($sharetype == 'public'){
+		OC_JSON::success(array('message'=>$success));
+	}else{
+		OC_JSON::success(array('message'=>'shared'));
+	}
+}else{
+	OC_JSON::error(array('message'=>'can not share'));
+	exit;
+}
\ No newline at end of file
diff --git a/apps/calendar/ajax/share/unshare.php b/apps/calendar/ajax/share/unshare.php
new file mode 100644
index 0000000000000000000000000000000000000000..ec3150a89aa882040f2a9ad5a5bbdcde4d49ba1e
--- /dev/null
+++ b/apps/calendar/ajax/share/unshare.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+require_once('../../../../lib/base.php');
+$id = strip_tags($_GET['id']);
+$idtype = strip_tags($_GET['idtype']);
+switch($idtype){
+	case 'calendar':
+	case 'event':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+$sharewith = $_GET['sharewith'];
+$sharetype = strip_tags($_GET['sharetype']);
+switch($sharetype){
+	case 'user':
+	case 'group':
+	case 'public':
+		break;
+	default:
+		OC_JSON::error(array('message'=>'unexspected parameter'));
+		exit;
+}
+if($sharetype == 'user' && !OC_User::userExists($sharewith)){
+	OC_JSON::error(array('message'=>'user not found'));
+	exit;
+}
+if($sharetype == 'group' && !OC_Group::groupExists($sharewith)){
+	OC_JSON::error(array('message'=>'group not found'));
+	exit;
+}
+$success = OC_Calendar_Share::unshare(OC_User::getUser(), $sharewith, $sharetype, $id, (($idtype=='calendar') ? OC_Calendar_Share::CALENDAR : OC_Calendar_Share::Event));
+if($success){
+	OC_JSON::success();
+}else{
+	OC_JSON::error(array('message'=>'can not unshare'));
+	exit;
+}
\ No newline at end of file
diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php
index 9c95768895fd76d2013e07c115133b890b7f52f6..f297c4d16d4eac5dfa705cef089b4f12f6f83b7f 100644
--- a/apps/calendar/appinfo/app.php
+++ b/apps/calendar/appinfo/app.php
@@ -1,23 +1,23 @@
 <?php
-if(version_compare(PHP_VERSION, '5.3.0', '>=')){
-	$l=new OC_L10N('calendar');
-	OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php';
-	OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php';
-	OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php';
-	OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php';
-	OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php';
-	OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser');
-	OC_Util::addScript('calendar','loader');
-	OC_App::register( array( 
-	  'order' => 10,
-	  'id' => 'calendar',
-	  'name' => 'Calendar' ));
-	OC_App::addNavigationEntry( array( 
-	  'id' => 'calendar_index',
-	  'order' => 10,
-	  'href' => OC_Helper::linkTo( 'calendar', 'index.php' ),
-	  'icon' => OC_Helper::imagePath( 'calendar', 'icon.svg' ),
-	  'name' => $l->t('Calendar')));
-	OC_App::registerPersonal('calendar', 'settings');
-	require_once('apps/calendar/lib/search.php');
-}
+$l=new OC_L10N('calendar');
+OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php';
+OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php';
+OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php';
+OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php';
+OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php';
+OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php';
+OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser');
+OC_Hook::connect('OC_DAV', 'initialize', 'OC_Calendar_Hooks', 'initializeCalDAV');
+OC_Util::addScript('calendar','loader');
+OC_App::register( array(
+  'order' => 10,
+  'id' => 'calendar',
+  'name' => 'Calendar' ));
+OC_App::addNavigationEntry( array(
+  'id' => 'calendar_index',
+  'order' => 10,
+  'href' => OC_Helper::linkTo( 'calendar', 'index.php' ),
+  'icon' => OC_Helper::imagePath( 'calendar', 'icon.svg' ),
+  'name' => $l->t('Calendar')));
+OC_App::registerPersonal('calendar', 'settings');
+OC_Search::registerProvider('OC_Search_Provider_Calendar');
\ No newline at end of file
diff --git a/apps/calendar/caldav.php b/apps/calendar/caldav.php
index db0b35da118f668626afec7a5cb43c080da4d88a..b710b99ea43bf069cc8cf989e35a1b120b080dd3 100644
--- a/apps/calendar/caldav.php
+++ b/apps/calendar/caldav.php
@@ -25,7 +25,7 @@ $nodes = array(
 
 // Fire up server
 $server = new Sabre_DAV_Server($nodes);
-$server->setBaseUri(OC::$WEBROOT.'/apps/calendar/caldav.php');
+$server->setBaseUri(OC::$APPSWEBROOT.'/apps/calendar/caldav.php');
 // Add plugins
 $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
 $server->addPlugin(new Sabre_CalDAV_Plugin());
diff --git a/apps/calendar/index.php b/apps/calendar/index.php
index 12b51f564b4bb8e0dba40d4a6d121aa090128178..c00a4098f7ad7f4a3f919dac828894b13cbd5981 100644
--- a/apps/calendar/index.php
+++ b/apps/calendar/index.php
@@ -9,16 +9,20 @@
 require_once ('../../lib/base.php');
 OC_Util::checkLoggedIn();
 OC_Util::checkAppEnabled('calendar');
+
 // Create default calendar ...
 $calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
 if( count($calendars) == 0){
 	OC_Calendar_Calendar::addCalendar(OC_User::getUser(),'Default calendar');
 	$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
 }
+
 $eventSources = array();
 foreach($calendars as $calendar){
 	$eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar);
 }
+OC_Hook::emit('OC_Calendar', 'getSources', array('sources' => &$eventSources));
+
 //Fix currentview for fullcalendar
 if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'month') == "oneweekview"){
 	OC_Preferences::setValue(OC_USER::getUser(), "calendar", "currentview", "agendaWeek");
diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js
index ba8e293819a29f59f4708c6ae0c0dd4a78079974..907d94edb021044ee8372563c14cff872488cf53 100644
--- a/apps/calendar/js/calendar.js
+++ b/apps/calendar/js/calendar.js
@@ -69,17 +69,20 @@ Calendar={
 				$('#event').dialog('destroy').remove();
 			}else{
 				Calendar.UI.loading(true);
-				$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'neweventform.php'), {start:start, end:end, allday:allday?1:0}, Calendar.UI.startEventDialog);
+				$('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'new.form.php'), {start:start, end:end, allday:allday?1:0}, Calendar.UI.startEventDialog);
 			}
 		},
 		editEvent:function(calEvent, jsEvent, view){
+			if (calEvent.editable == false || calEvent.source.editable == false) {
+				return;
+			}
 			var id = calEvent.id;
 			if($('#event').dialog('isOpen') == true){
 				// TODO: save event
 				$('#event').dialog('destroy').remove();
 			}else{
 				Calendar.UI.loading(true);
-				$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'editeventform.php') + '?id=' + id, Calendar.UI.startEventDialog);
+				$('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'edit.form.php') + '?id=' + id, Calendar.UI.startEventDialog);
 			}
 		},
 		submitDeleteEventForm:function(url){
@@ -141,7 +144,7 @@ Calendar={
 		moveEvent:function(event, dayDelta, minuteDelta, allDay, revertFunc){
 			$('.tipsy').remove();
 			Calendar.UI.loading(true);
-			$.post(OC.filePath('calendar', 'ajax', 'moveevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, allDay: allDay?1:0, lastmodified: event.lastmodified},
+			$.post(OC.filePath('calendar', 'ajax/event', 'move.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, allDay: allDay?1:0, lastmodified: event.lastmodified},
 			function(data) {
 				Calendar.UI.loading(false);
 				if (data.status == 'success'){
@@ -156,7 +159,7 @@ Calendar={
 		resizeEvent:function(event, dayDelta, minuteDelta, revertFunc){
 			$('.tipsy').remove();
 			Calendar.UI.loading(true);
-			$.post(OC.filePath('calendar', 'ajax', 'resizeevent.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, lastmodified: event.lastmodified},
+			$.post(OC.filePath('calendar', 'ajax/event', 'resize.php'), { id: event.id, dayDelta: dayDelta, minuteDelta: minuteDelta, lastmodified: event.lastmodified},
 			function(data) {
 				Calendar.UI.loading(false);
 				if (data.status == 'success'){
@@ -214,7 +217,7 @@ Calendar={
 		},
 		initScroll:function(){
 			if(window.addEventListener)
-				document.addEventListener('DOMMouseScroll', Calendar.UI.scrollCalendar);
+				document.addEventListener('DOMMouseScroll', Calendar.UI.scrollCalendar, false);
 			//}else{
 				document.onmousewheel = Calendar.UI.scrollCalendar;
 			//}
@@ -373,7 +376,7 @@ Calendar={
 					$('#choosecalendar_dialog').dialog('moveToTop');
 				}else{
 					Calendar.UI.loading(true);
-					$('#dialog_holder').load(OC.filePath('calendar', 'ajax', 'choosecalendar.php'), function(){
+					$('#dialog_holder').load(OC.filePath('calendar', 'ajax/calendar', 'overview.php'), function(){
 						$('#choosecalendar_dialog').dialog({
 							width : 600,
 							close : function(event, ui) {
@@ -387,7 +390,7 @@ Calendar={
 			activation:function(checkbox, calendarid)
 			{
 				Calendar.UI.loading(true);
-				$.post(OC.filePath('calendar', 'ajax', 'activation.php'), { calendarid: calendarid, active: checkbox.checked?1:0 },
+				$.post(OC.filePath('calendar', 'ajax/calendar', 'activation.php'), { calendarid: calendarid, active: checkbox.checked?1:0 },
 				  function(data) {
 					Calendar.UI.loading(false);
 					if (data.status == 'success'){
@@ -402,13 +405,13 @@ Calendar={
 			},
 			newCalendar:function(object){
 				var tr = $(document.createElement('tr'))
-					.load(OC.filePath('calendar', 'ajax', 'newcalendar.php'),
+					.load(OC.filePath('calendar', 'ajax/calendar', 'new.form.php'),
 						function(){Calendar.UI.Calendar.colorPicker(this)});
 				$(object).closest('tr').after(tr).hide();
 			},
 			edit:function(object, calendarid){
 				var tr = $(document.createElement('tr'))
-					.load(OC.filePath('calendar', 'ajax', 'editcalendar.php') + "?calendarid="+calendarid,
+					.load(OC.filePath('calendar', 'ajax/calendar', 'edit.form.php') + "?calendarid="+calendarid,
 						function(){Calendar.UI.Calendar.colorPicker(this)});
 				$(object).closest('tr').after(tr).hide();
 			},
@@ -417,7 +420,7 @@ Calendar={
 				if(check == false){
 					return false;
 				}else{
-					$.post(OC.filePath('calendar', 'ajax', 'deletecalendar.php'), { calendarid: calid},
+					$.post(OC.filePath('calendar', 'ajax/calendar', 'delete.php'), { calendarid: calid},
 					  function(data) {
 						if (data.status == 'success'){
 							var url = 'ajax/events.php?calendar_id='+calid;
@@ -442,9 +445,9 @@ Calendar={
 				
 				var url;
 				if (calendarid == 'new'){
-					url = OC.filePath('calendar', 'ajax', 'createcalendar.php');
+					url = OC.filePath('calendar', 'ajax/calendar', 'new.php');
 				}else{
-					url = OC.filePath('calendar', 'ajax', 'updatecalendar.php');
+					url = OC.filePath('calendar', 'ajax/calendar', 'update.php');
 				}
 				$.post(url, { id: calendarid, name: displayname, active: active, description: description, color: calendarcolor },
 					function(data){
@@ -661,7 +664,7 @@ $(document).ready(function(){
 	Calendar.UI.initScroll();
 	$('#calendar_holder').fullCalendar({
 		header: false,
-		firstDay: 1,
+		firstDay: firstDay,
 		editable: true,
 		defaultView: defaultView,
 		timeFormat: {
diff --git a/apps/calendar/js/geo.js b/apps/calendar/js/geo.js
index ae6a971e938f94b4bd52bbd281bc87a40c3dc63d..7018c6298a20c46cb8c21b2206537780341554d1 100755
--- a/apps/calendar/js/geo.js
+++ b/apps/calendar/js/geo.js
@@ -6,11 +6,10 @@
  */
 if (navigator.geolocation) { 
 	navigator.geolocation.getCurrentPosition(function(position) {
-		$.getJSON(OC.filePath('calendar', 'ajax', 'guesstimezone.php?lat=' + position.coords.latitude + '&long=' + position.coords.longitude + ''),
+		$.getJSON(OC.filePath('calendar', 'ajax/settings', 'guesstimezone.php?lat=' + position.coords.latitude + '&long=' + position.coords.longitude + ''),
 		function(data){
 			if (data.status == 'success' && typeof(data.message) != 'undefined'){
 				$('#notification').html(data.message);
-				$('#notification').attr('title', 'CC BY 3.0 by Geonames.org');
 				$('#notification').slideDown();
 				window.setTimeout(function(){$('#notification').slideUp();}, 5000);
 			}else{
diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js
index 4e483f9864e92cb073e9ef6277c2c3ca71e54acb..54003879759d15b2d37b277e84b352697720bb1c 100644
--- a/apps/calendar/js/loader.js
+++ b/apps/calendar/js/loader.js
@@ -8,7 +8,7 @@ Calendar_Import={
 	importdialog: function(filename){
 		var path = $('#dir').val();
 		$('body').append('<div id="calendar_import"></div>');
-		$('#calendar_import').load(OC.filePath('calendar', 'ajax', 'importdialog.php'), {filename:filename, path:path},	function(){Calendar_Import.initdialog(filename);});
+		$('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path},	function(){Calendar_Import.initdialog(filename);});
 	},
 	initdialog: function(filename){
 		$('#calendar_import_dialog').dialog({
@@ -68,7 +68,7 @@ Calendar_Import={
 			if(percent < 100){
 				window.setTimeout('Calendar_Import.getimportstatus(\'' + progressfile + '\')', 500);
 			}else{
-				$('#import_done').css('display', 'block');
+				$('#import_done').css('display', 'block');
 			}
 		});
 	}
@@ -78,4 +78,4 @@ $(document).ready(function(){
 		FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); 
 		FileActions.setDefault('text/calendar','importcal');
 	};
-});
\ No newline at end of file
+});
diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js
index 73300885565639c2b7d3c0c3311ee34411249629..c768a47a797f36d38dee3c0385e4884356a7a3dc 100644
--- a/apps/calendar/js/settings.js
+++ b/apps/calendar/js/settings.js
@@ -3,7 +3,7 @@ $(document).ready(function(){
 		OC.msg.startSaving('#calendar .msg')
 		// Serialize the data
 		var post = $( '#timezone' ).serialize();
-		$.post( OC.filePath('calendar', 'ajax', 'settimezone.php'), post, function(data){
+		$.post( OC.filePath('calendar', 'ajax/settings', 'settimezone.php'), post, function(data){
 			//OC.msg.finishedSaving('#calendar .msg', data);
 		});
 		return false;
@@ -11,25 +11,37 @@ $(document).ready(function(){
 	$('#timezone').chosen();
 	$('#timeformat').change( function(){
 		var data = $('#timeformat').serialize();
-		$.post( OC.filePath('calendar', 'ajax', 'settimeformat.php'), data, function(data){
+		$.post( OC.filePath('calendar', 'ajax/settings', 'settimeformat.php'), data, function(data){
 			if(data == 'error'){
 				console.log('saving timeformat failed');
 			}
 		});
 	});
+	$('#firstday').change( function(){
+		var data = $('#firstday').serialize();
+		$.post( OC.filePath('calendar', 'ajax/settings', 'setfirstday.php'), data, function(data){
+			if(data == 'error'){
+				console.log('saving firstday failed');
+			}
+		});
+	});
 	$('#timezonedetection').change( function(){
 		var post = $('#timezonedetection').serialize();
-		$.post( OC.filePath('calendar', 'ajax', 'timezonedetection.php'), post, function(data){
+		$.post( OC.filePath('calendar', 'ajax/settings', 'timezonedetection.php'), post, function(data){
 			
 		});
 	});
-	$.getJSON(OC.filePath('calendar', 'ajax', 'timeformat.php'), function(jsondata, status) {
+	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) {
 		$('#' + jsondata.timeformat).attr('selected',true);
 		$('#timeformat').chosen();
 	});
-	$.getJSON(OC.filePath('calendar', 'ajax', 'gettimezonedetection.php'), function(jsondata, status){
+	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){
 		if(jsondata.detection == 'true'){
 			$('#timezonedetection').attr('checked', 'checked');
 		}
 	});
+	$.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) {
+		$('#' + jsondata.firstday).attr('selected',true);
+		$('#firstday').chosen();
+	});
 });
diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php
index 277539af97d7cbbeb1010b21a3c921a0759ad2f0..7eeb004d181c3e47bcfd4fa1b276756cb4df9119 100644
--- a/apps/calendar/lib/calendar.php
+++ b/apps/calendar/lib/calendar.php
@@ -240,9 +240,10 @@ class OC_Calendar_Calendar{
 			'#9fc6e7', // "light blue"
 		);
 	}
+
 	public static function getEventSourceInfo($calendar){
 		return array(
-			'url' => 'ajax/events.php?calendar_id='.$calendar['id'],
+			'url' => OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'],
 			'backgroundColor' => $calendar['calendarcolor'],
 			'borderColor' => '#888',
 			'textColor' => 'black',
diff --git a/apps/calendar/lib/hooks.php b/apps/calendar/lib/hooks.php
index 14f96bb5fe128bfc55f0d9f2bc0df0d0988b99ec..54f1680a36e55d4a68427902ca3ce2816cc8b1d5 100644
--- a/apps/calendar/lib/hooks.php
+++ b/apps/calendar/lib/hooks.php
@@ -17,11 +17,24 @@ class OC_Calendar_Hooks{
 	 */
 	public static function deleteUser($parameters) {
 		$calendars = OC_Calendar_Calendar::allCalendars($parameters['uid']);
-		
+
 		foreach($calendars as $calendar) {
 			OC_Calendar_Calendar::deleteCalendar($calendar['id']);
 		}
 
 		return true;
 	}
+
+	/**
+	 * @brief Adds the CardDAV resource to the DAV server
+	 * @param paramters parameters from initialize-Hook
+	 * @return array
+	 */
+	public static function initializeCalDAV($parameters){
+		// We need a backend, the root node and the caldav plugin
+		$parameters['backends']['caldav'] = new OC_Connector_Sabre_CalDAV();
+		$parameters['nodes'][] = new Sabre_CalDAV_CalendarRootNode($parameters['backends']['principal'], $parameters['backends']['caldav']);
+		$parameters['plugins'][] = new Sabre_CalDAV_Plugin();
+		return true;
+	}
 }
diff --git a/apps/calendar/lib/object.php b/apps/calendar/lib/object.php
index 230c610d35a4305df3711857d1b26a0aa103aad7..e0c0e83d5d0f71742e1c3a932d22736867a1e7fa 100644
--- a/apps/calendar/lib/object.php
+++ b/apps/calendar/lib/object.php
@@ -96,8 +96,7 @@ class OC_Calendar_Object{
 		list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
 
 		if(is_null($uid)){
-			$uid = self::createUID();
-			$object->add('UID',$uid);
+			$object->setUID();
 			$data = $object->serialize();
 		}
 
@@ -208,14 +207,6 @@ class OC_Calendar_Object{
 		return true;
 	}
 
-	/**
-	 * @brief Creates a UID
-	 * @return string
-	 */
-	protected static function createUID(){
-		return substr(md5(rand().time()),0,10);
-	}
-
 	/**
 	 * @brief Extracts data from a vObject-Object
 	 * @param Sabre_VObject $object
@@ -309,6 +300,8 @@ class OC_Calendar_Object{
 			$dtend = $vevent->DTEND;
 		}else{
 			$dtend = clone $vevent->DTSTART;
+			// clone creates a shallow copy, also clone DateTime
+			$dtend->setDateTime(clone $dtend->getDateTime(), $dtend->getDateType());
 			if ($vevent->DURATION){
 				$duration = strval($vevent->DURATION);
 				$invert = 0;
@@ -817,4 +810,4 @@ class OC_Calendar_Object{
 
 		return $vcalendar;
 	}
-}
\ No newline at end of file
+}
diff --git a/apps/calendar/lib/search.php b/apps/calendar/lib/search.php
index 0016751a66ad1166e17651b228a97967cb04db2e..8405866392d709630d503b0cb509ec387018b182 100644
--- a/apps/calendar/lib/search.php
+++ b/apps/calendar/lib/search.php
@@ -1,6 +1,6 @@
 <?php
-class OC_Search_Provider_Calendar extends OC_Search_Provider{
-	function search($query){
+class OC_Search_Provider_Calendar implements OC_Search_Provider{
+	static function search($query){
 		$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
 		if(count($calendars)==0 || !OC_App::isEnabled('calendar')){
 			//return false;
@@ -44,4 +44,3 @@ class OC_Search_Provider_Calendar extends OC_Search_Provider{
 		return $results;
 	}
 }
-new OC_Search_Provider_Calendar();
diff --git a/apps/calendar/templates/calendar.php b/apps/calendar/templates/calendar.php
index eb82d0d02ada02bec9eee382505a653c90e8c057..63c3b500296511be200c60e2a0cbefd648c8150e 100755
--- a/apps/calendar/templates/calendar.php
+++ b/apps/calendar/templates/calendar.php
@@ -19,6 +19,7 @@
 				var missing_field_startsbeforeends = '<?php echo addslashes($l->t('The event ends before it starts')) ?>';
 				var missing_field_dberror = '<?php echo addslashes($l->t('There was a database fail')) ?>';
 				var totalurl = '<?php echo OC_Helper::linkToAbsolute('calendar', 'caldav.php'); ?>/calendars';
+				var firstDay = '<?php echo (OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'firstday', 'mo') == 'mo' ? '1' : '0'); ?>';
 				$(document).ready(function() {
 				<?php
 				if(array_key_exists('showevent', $_)){
diff --git a/apps/calendar/templates/part.editevent.php b/apps/calendar/templates/part.editevent.php
index b3acfc4a072220167d44466e2b75424398a3a865..6e319e1b4e0fe7b67607ef735f0219c8a39e63ea 100644
--- a/apps/calendar/templates/part.editevent.php
+++ b/apps/calendar/templates/part.editevent.php
@@ -5,8 +5,8 @@
 <?php echo $this->inc("part.eventform"); ?>
 	<div style="width: 100%;text-align: center;color: #FF1D1D;" id="errorbox"></div>
 	<span id="actions">
-		<input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('ajax/editevent.php');">
-		<input type="button" class="submit" style="float: left;" name="delete" value="<?php echo $l->t("Delete");?>" onclick="Calendar.UI.submitDeleteEventForm('ajax/deleteevent.php');">
+		<input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('ajax/event/edit.php');">
+		<input type="button" class="submit" style="float: left;" name="delete" value="<?php echo $l->t("Delete");?>" onclick="Calendar.UI.submitDeleteEventForm('ajax/event/delete.php');">
 		<input type="button" class="submit" style="float: right;" name="export" value="<?php echo $l->t("Export");?>" onclick="window.location='export.php?eventid=<?php echo $_['id'] ?>';">
 	</span>
 	</form>
diff --git a/apps/calendar/templates/part.eventform.php b/apps/calendar/templates/part.eventform.php
index 49214aca77cd8c63a65bca5374826649cf5c9936..8a8f420f84636041c36ebab930e7c3445b2ceb51 100644
--- a/apps/calendar/templates/part.eventform.php
+++ b/apps/calendar/templates/part.eventform.php
@@ -17,6 +17,7 @@
 					?>
 				</select>
 			</td>
+			<?php if(count($_['calendar_options']) > 1) { ?>
 			<th width="75px">&nbsp;&nbsp;&nbsp;<?php echo $l->t("Calendar");?>:</th>
 			<td>
 				<select style="width:140px;" name="calendar">
@@ -26,6 +27,12 @@
 					?>
 				</select>
 			</td>
+			<?php } else { ?>
+			<th width="75px">&nbsp;</th>
+			<td>
+				<input type="hidden" name="calendar" value="<?php echo $_['calendar_options'][0]['id'] ?>">
+			</td>
+			<?php } ?>
 		</tr>
 	</table>
 	<hr>
diff --git a/apps/calendar/templates/part.newevent.php b/apps/calendar/templates/part.newevent.php
index b1e2a5eab848ecb53c108cf7e2d3c30f70ef8b74..11416260344b07c4a1f9dde7e33e9b9d9cc08b17 100644
--- a/apps/calendar/templates/part.newevent.php
+++ b/apps/calendar/templates/part.newevent.php
@@ -3,7 +3,7 @@
 <?php echo $this->inc("part.eventform"); ?>
 	<div style="width: 100%;text-align: center;color: #FF1D1D;" id="errorbox"></div>
 	<span id="actions">
-		<input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('ajax/newevent.php');">
+		<input type="button" class="submit" style="float: left;" value="<?php echo $l->t("Submit");?>" onclick="Calendar.UI.validateEventForm('ajax/event/new.php');">
 	</span>
 	</form>
 </div>
diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php
index 979634874e4cdf9a0339aafb4ad9e3ce81332f95..f74a45203e1803cf39fa813b994eae4706a9d981 100644
--- a/apps/calendar/templates/settings.php
+++ b/apps/calendar/templates/settings.php
@@ -37,6 +37,13 @@
                 </select>
             </td></tr>
 
+            <tr><td><label for="firstday" class="bold"><?php echo $l->t('First day of the week');?></label></td><td>
+                <select style="display: none;" id="firstday" title="<?php echo "First day"; ?>" name="firstday">
+                    <option value="mo" id="mo"><?php echo $l->t("Monday"); ?></option>
+                    <option value="su" id="su"><?php echo $l->t("Sunday"); ?></option>
+                </select>
+            </td></tr>
+
         </table>
 
         <?php echo $l->t('Calendar CalDAV syncing address:');?>
diff --git a/apps/contacts/ajax/addcard.php b/apps/contacts/ajax/addcard.php
index b1dc69a469105d56dfc56b3423dd40078ff6948f..49a4a16170b8a18c62ed482132522778a1419580 100644
--- a/apps/contacts/ajax/addcard.php
+++ b/apps/contacts/ajax/addcard.php
@@ -92,7 +92,7 @@ foreach( $add as $propname){
 		}
 	}
 }
-$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
+$id = OC_Contacts_VCard::add($aid,$vcard);
 if(!$id) {
 	OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
 	OC_Log::write('contacts','ajax/addcard.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR);
diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php
index 5d17631caa4deff8bc04386d3e2b3419121aa081..839a39199811dbbe451e0b3ed3991a357d038dcb 100644
--- a/apps/contacts/ajax/addcontact.php
+++ b/apps/contacts/ajax/addcontact.php
@@ -52,7 +52,7 @@ $vcard->setUID();
 $vcard->setString('FN',$fn);
 $vcard->setString('N',$n);
 
-$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
+$id = OC_Contacts_VCard::add($aid,$vcard);
 if(!$id) {
 	OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
 	OC_Log::write('contacts','ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR);
diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php
index 028974e1c66b1bc38dea45ec7512127a3a1e07aa..b6b5dc7c416446eb333bfc32329b461a78d5750b 100644
--- a/apps/contacts/ajax/addproperty.php
+++ b/apps/contacts/ajax/addproperty.php
@@ -113,7 +113,7 @@ foreach ($parameters as $key=>$element) {
 }
 $checksum = md5($vcard->children[$line]->serialize());
 
-if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
+if(!OC_Contacts_VCard::edit($id,$vcard)) {
 	OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error adding contact property.'))));
 	OC_Log::write('contacts','ajax/addproperty.php: Error updating contact property: '.$name, OC_Log::ERROR);
 	exit();
diff --git a/apps/contacts/ajax/deleteproperty.php b/apps/contacts/ajax/deleteproperty.php
index a9afffaad4c490f6902f416a4940c4be78ed456b..ab0958cac58ab0a6cdb5c9ff3d2da78871c9845b 100644
--- a/apps/contacts/ajax/deleteproperty.php
+++ b/apps/contacts/ajax/deleteproperty.php
@@ -39,7 +39,7 @@ if(is_null($line)){
 
 unset($vcard->children[$line]);
 
-if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
+if(!OC_Contacts_VCard::edit($id,$vcard)) {
 	OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error deleting contact property.'))));
 	OC_Log::write('contacts','ajax/deleteproperty.php: Error deleting contact property', OC_Log::ERROR);
 	exit();
diff --git a/apps/contacts/ajax/savecrop.php b/apps/contacts/ajax/savecrop.php
index 1a84f6fdfaea55c0d5b5711b0fff52aeef26f062..ffbfaeb6e384c1998c8e05b99f6a513e0101eeb0 100644
--- a/apps/contacts/ajax/savecrop.php
+++ b/apps/contacts/ajax/savecrop.php
@@ -95,7 +95,7 @@ if(file_exists($tmp_path)) {
 						OC_Log::write('contacts','savecrop.php: files: Adding PHOTO property.', OC_Log::DEBUG);
 						$card->addProperty('PHOTO', $image->__toString(), array('ENCODING' => 'b', 'TYPE' => $image->mimeType()));
 					}
-					if(!OC_Contacts_VCard::edit($id,$card->serialize())) {
+					if(!OC_Contacts_VCard::edit($id,$card)) {
 						bailOut('Error saving contact.');
 					}
 					unlink($tmpfname);
diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php
index 6f8366243fe59377fe7dfce0be076102ee9b8f1c..db209fedfc702b94050736341dc8895855fdf5a6 100644
--- a/apps/contacts/ajax/saveproperty.php
+++ b/apps/contacts/ajax/saveproperty.php
@@ -122,7 +122,7 @@ switch($element) {
 $checksum = md5($vcard->children[$line]->serialize());
 debug('New checksum: '.$checksum);
 
-if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
+if(!OC_Contacts_VCard::edit($id,$vcard)) {
 	OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error updating contact property.'))));
 	OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR);
 	exit();
diff --git a/apps/contacts/ajax/setproperty.php b/apps/contacts/ajax/setproperty.php
index f9e2a8e864733dfec8652cb15f63c382a3cb2c07..8e07b4a8f1cd383498f4683b91e32e2e163b1fcb 100644
--- a/apps/contacts/ajax/setproperty.php
+++ b/apps/contacts/ajax/setproperty.php
@@ -80,7 +80,7 @@ foreach($missingparameters as $i){
 // NOTE: This checksum is not used..?
 $checksum = md5($vcard->children[$line]->serialize());
 
-if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
+if(!OC_Contacts_VCard::edit($id,$vcard)) {
 	OC_JSON::error(array('data' => array('message' => $l->t('Error updating contact property.'))));
 	OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR);
 	exit();
diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
index 9e424aa89f8c4fc9edd352f9b55b72b7f201adf2..85c383c4c3224a88cf54e7da546272a67e1cd406 100644
--- a/apps/contacts/appinfo/app.php
+++ b/apps/contacts/appinfo/app.php
@@ -4,7 +4,11 @@ OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php';
 OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php';
 OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php';
 OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php';
+OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php';
 OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser');
+OC_HOOK::connect('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents');
+OC_HOOK::connect('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources');
+OC_Hook::connect('OC_DAV', 'initialize', 'OC_Contacts_Hooks', 'initializeCardDAV');
 
 OC_App::register( array(
   'order' => 10,
@@ -21,4 +25,4 @@ OC_App::addNavigationEntry( array(
 
 OC_APP::registerPersonal('contacts','settings');
 OC_UTIL::addScript('contacts', 'loader');
-require_once('apps/contacts/lib/search.php');
+OC_Search::registerProvider('OC_Search_Provider_Contacts');
diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php
index a2bf492e2062643b00c474123505ddd0651a0fa5..654aeb66a72174ec9d73c60c76c4b3f13f6ab896 100644
--- a/apps/contacts/carddav.php
+++ b/apps/contacts/carddav.php
@@ -39,7 +39,7 @@ $nodes = array(
 
 // Fire up server
 $server = new Sabre_DAV_Server($nodes);
-$server->setBaseUri(OC::$WEBROOT.'/apps/contacts/carddav.php');
+$server->setBaseUri(OC::$APPSWEBROOT.'/apps/contacts/carddav.php');
 // Add plugins
 $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
 $server->addPlugin(new Sabre_CardDAV_Plugin());
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 384541f37518cc751b1b48b30f4360adec5d94ad..b24ec438f247c68498477d2f2463a92c0e5d5c16 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -17,8 +17,8 @@
 #contacts_propertymenu li a:hover { color: #fff }
 #actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;}
 #card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
-#firstrun { /*border: thin solid lightgray;*/ width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em;  color:#777;}
-#firstrun #selections { /*border: thin solid lightgray;*/ font-size:0.8em;  width: 100%; margin: 2em auto auto auto; clear: both; }
+#firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; }
+#firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; }
 
 #card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 16em; }
 #card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none  !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }
diff --git a/apps/contacts/import.php b/apps/contacts/import.php
index 4638bf0d73cd609ff6ac4e9e4d6efc6ede26954e..04cfc397d56a7322dadbaae7ad90f8e551092f3b 100644
--- a/apps/contacts/import.php
+++ b/apps/contacts/import.php
@@ -97,11 +97,15 @@ if(is_writable('import_tmp/')){
 	fclose($progressfopen);
 }
 if(count($parts) == 1){
-	OC_Contacts_VCard::add($id, $file);
-}else{
-	foreach($importready as $import){
-		OC_Contacts_VCard::add($id, $import);
+	$importready = array($file);
+}
+foreach($importready as $import){
+	$card = OC_VObject::parse($import);
+	if (!$card) {
+		OC_Log::write('contacts','Import: skipping card. Error parsing VCard: '.$import, OC_Log::ERROR);
+		continue; // Ditch cards that can't be parsed by Sabre.
 	}
+	OC_Contacts_VCard::add($id, $card);
 }
 //done the import
 if(is_writable('import_tmp/')){
@@ -113,4 +117,4 @@ sleep(3);
 if(is_writable('import_tmp/')){
 	unlink($progressfile);
 }
-OC_JSON::success();
\ No newline at end of file
+OC_JSON::success();
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index 11661320c5901d237f3953eb49f9cac7fd57e776..94876f5cd0906693d84381aebf596afef129c093 100644
--- a/apps/contacts/js/contacts.js
+++ b/apps/contacts/js/contacts.js
@@ -8,7 +8,7 @@ String.prototype.strip_tags = function(){
 	tags = this;
 	stripped = tags.replace(/[\<\>]/gi, "");
 	return stripped;
-}
+};
 
 
 Contacts={
@@ -68,7 +68,7 @@ Contacts={
 			return $(obj).parents('.propertycontainer').first().data('element');
 		},
 		showHideContactInfo:function() {
-			var show = ($('#emaillist li[class*="propertycontainer"]').length > 0 || $('#phonelist li[class*="propertycontainer"]').length > 0 || $('#addressdisplay dl[class*="propertycontainer"]').length > 0);
+			var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0);
 			console.log('showHideContactInfo: ' + show);
 			if(show) {
 				$('#contact_communication').show();
@@ -82,19 +82,19 @@ Contacts={
 			switch (type) {
 				case 'EMAIL':
 					console.log('emails: '+$('#emaillist>li').length);
-					if($('#emaillist li[class*="propertycontainer"]').length == 0) {
+					if($('#emaillist li.propertycontainer').length == 0) {
 						$('#emails').hide();
 					}
 					break;
 				case 'TEL':
 					console.log('phones: '+$('#phonelist>li').length);
-					if($('#phonelist li[class*="propertycontainer"]').length == 0) {
+					if($('#phonelist li.propertycontainer').length == 0) {
 						$('#phones').hide();
 					}
 					break;
 				case 'ADR':
 					console.log('addresses: '+$('#addressdisplay>dl').length);
-					if($('#addressdisplay dl[class*="propertycontainer"]').length == 0) {
+					if($('#addressdisplay dl.propertycontainer').length == 0) {
 						$('#addresses').hide();
 					}
 					break;
@@ -142,7 +142,7 @@ Contacts={
 							}
 					});
 				});
-			}
+			};
 		},
 		loadListHandlers:function() {
 			//$('.add,.delete').hide();
@@ -183,7 +183,7 @@ Contacts={
 						dateFormat : 'dd-mm-yy'
 			});
 			// Style phone types
-			$('#phonelist').find('select[class*="contacts_property"]').multiselect({
+			$('#phonelist').find('select.contacts_property').multiselect({
 													noneSelectedText: t('contacts', 'Select type'),
 													header: false,
 													selectedList: 4,
@@ -323,7 +323,7 @@ Contacts={
 					}
 				});
 			},
-			delete:function() {
+			delete: function() {
 				$('#contacts_deletecard').tipsy('hide');
 				$.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){
 					if(jsondata.status == 'success'){
@@ -373,7 +373,7 @@ Contacts={
 			loadSingleProperties:function() {
 				var props = ['BDAY', 'NICKNAME', 'ORG'];
 				// Clear all elements
-				$('#ident .propertycontainer[class*="propertycontainer"]').each(function(){
+				$('#ident .propertycontainer').each(function(){
 					if(props.indexOf($(this).data('element')) > -1) {
 						$(this).data('checksum', '');
 						$(this).find('input').val('');
@@ -448,6 +448,9 @@ Contacts={
 				$('#fn_select option').remove();
 				$('#fn_select').combobox('value', this.fn);
 				var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
+				if(this.data.ORG) {
+					names[names.length]=this.data.ORG[0].value;
+				}
 				$.each(names, function(key, value) {
 					$('#fn_select')
 						.append($('<option></option>')
@@ -518,8 +521,8 @@ Contacts={
 				var checksum = container.data('checksum');
 				var name = container.data('element');
 				console.log('saveProperty: ' + name);
-				var fields = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serializeArray();
-				var q = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serialize();
+				var fields = container.find('input.contacts_property,select.contacts_property').serializeArray();
+				var q = container.find('input.contacts_property,select.contacts_property').serialize();
 				if(q == '' || q == undefined) {
 					console.log('Couldn\'t serialize elements.');
 					Contacts.UI.loading(container, false);
@@ -708,7 +711,7 @@ Contacts={
 			},
 			loadAddresses:function(){
 				$('#addresses').hide();
-				$('#addressdisplay dl[class*="propertycontainer"]').remove();
+				$('#addressdisplay dl.propertycontainer').remove();
 				for(var adr in this.data.ADR) {
 					$('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show();
 					$('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer');
@@ -920,15 +923,15 @@ Contacts={
 			},
 			addMail:function() {
 				//alert('addMail');
-				$('#emaillist li[class*="template"]:first-child').clone().appendTo($('#emaillist')).show();
-				$('#emaillist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer');
+				$('#emaillist li.template:first-child').clone().appendTo($('#emaillist')).show();
+				$('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer');
 				$('#emaillist li:last-child').find('input[type="email"]').focus();
 				Contacts.UI.loadListHandlers();
 				return false;
 			},
 			loadMails:function() {
 				$('#emails').hide();
-				$('#emaillist li[class*="propertycontainer"]').remove();
+				$('#emaillist li.propertycontainer').remove();
 				for(var mail in this.data.EMAIL) {
 					this.addMail();
 					//$('#emaillist li:first-child').clone().appendTo($('#emaillist')).show();
@@ -950,9 +953,9 @@ Contacts={
 				return false;
 			},
 			addPhone:function() {
-				$('#phonelist li[class*="template"]:first-child').clone().appendTo($('#phonelist')); //.show();
-				$('#phonelist li[class*="template"]:last-child').find('select').addClass('contacts_property');
-				$('#phonelist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer');
+				$('#phonelist li.template:first-child').clone().appendTo($('#phonelist')); //.show();
+				$('#phonelist li.template:last-child').find('select').addClass('contacts_property');
+				$('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer');
 				$('#phonelist li:last-child').find('input[type="text"]').focus();
 				Contacts.UI.loadListHandlers();
 				$('#phonelist li:last-child').find('select').multiselect({
@@ -966,7 +969,7 @@ Contacts={
 			},
 			loadPhones:function() {
 				$('#phones').hide();
-				$('#phonelist li[class*="propertycontainer"]').remove();
+				$('#phonelist li.propertycontainer').remove();
 				for(var phone in this.data.TEL) {
 					this.addPhone();
 					$('#phonelist li:last-child').find('select').multiselect('destroy');
diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php
index 48298952c12883a11a5dc07a4151786d23678a3b..1fa441475d2ec76aff1b37c2a64fdeeee7319fea 100644
--- a/apps/contacts/lib/app.php
+++ b/apps/contacts/lib/app.php
@@ -85,7 +85,7 @@ class OC_Contacts_App {
 		$vcard = OC_VObject::parse($card['carddata']);
 		// Try to fix cards with missing 'N' field from pre ownCloud 4. Hot damn, this is ugly...
 		if(!is_null($vcard) && !$vcard->__isset('N')) {
-			$appinfo = $info=OC_App::getAppInfo('contacts');
+			$appinfo = OC_App::getAppInfo('contacts');
 			if($appinfo['version'] >= 5) {
 				OC_Log::write('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OC_Log::DEBUG);
 			}
@@ -94,7 +94,7 @@ class OC_Contacts_App {
 				OC_Log::write('contacts','getContactVCard, found FN field: '.$vcard->__get('FN'), OC_Log::DEBUG);
 				$n = implode(';', array_reverse(array_slice(explode(' ', $vcard->__get('FN')), 0, 2))).';;;';
 				$vcard->setString('N', $n);
-				OC_Contacts_VCard::edit( $id, $vcard->serialize());
+				OC_Contacts_VCard::edit( $id, $vcard);
 			} else { // Else just add an empty 'N' field :-P
 				$vcard->setString('N', 'Unknown;Name;;;');
 			}
diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php
index 155cf40f914c5b65796c363ede154010dc6d985b..e09da20be8633ac651e66d26e01ed34daea6a00d 100644
--- a/apps/contacts/lib/hooks.php
+++ b/apps/contacts/lib/hooks.php
@@ -29,7 +29,7 @@ class OC_Contacts_Hooks{
 	 * @param paramters parameters from postDeleteUser-Hook
 	 * @return array
 	 */
-	public function deleteUser($parameters) {
+	static public function deleteUser($parameters) {
 		$addressbooks = OC_Contacts_Addressbook::all($parameters['uid']);
 
 		foreach($addressbooks as $addressbook) {
@@ -38,4 +38,62 @@ class OC_Contacts_Hooks{
 
 		return true;
 	}
+
+	/**
+	 * @brief Adds the CardDAV resource to the DAV server
+	 * @param paramters parameters from initialize-Hook
+	 * @return array
+	 */
+	static public function initializeCardDAV($parameters){
+		// We need a backend, the root node and the carddav plugin
+		$parameters['backends']['carddav'] = new OC_Connector_Sabre_CardDAV();
+		$parameters['nodes'][] = new Sabre_CardDAV_AddressBookRoot($parameters['backends']['principal'], $parameters['backends']['carddav']);
+		$parameters['plugins'][] = new Sabre_CardDAV_Plugin();
+		return true;
+	}
+
+	static public function getCalenderSources($parameters) {
+		$base_url = OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id=';
+		foreach(OC_Contacts_Addressbook::all(OC_User::getUser()) as $addressbook) {
+			$parameters['sources'][] =
+				array(
+					'url' => $base_url.'birthday_'. $addressbook['id'],
+					'backgroundColor' => '#cccccc',
+					'borderColor' => '#888',
+					'textColor' => 'black',
+					'cache' => true,
+					'editable' => false,
+				);
+		}
+	}
+
+	static public function getBirthdayEvents($parameters) {
+		$name = $parameters['calendar_id'];
+		if (strpos('birthday_', $name) != 0) {
+			return;
+		}
+		$info = explode('_', $name);
+		$aid = $info[1];
+		OC_Contacts_App::getAddressbook($aid);
+		foreach(OC_Contacts_VCard::all($aid) as $card){
+			$vcard = OC_VObject::parse($card['carddata']);
+			$birthday = $vcard->BDAY;
+			if ($birthday) {
+				$date = new DateTime($birthday);
+				$vevent = new OC_VObject('VEVENT');
+				$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV));
+				$vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE);
+				$vevent->setString('DURATION', 'P1D');
+				// DESCRIPTION?
+				$vevent->setString('RRULE', 'FREQ=YEARLY');
+				$title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday'));
+				$parameters['events'][] = array(
+					'id' => 0,//$card['id'],
+					'vevent' => $vevent,
+					'repeating' => true,
+					'summary' => $title,
+					);
+			}
+		}
+	}
 }
diff --git a/apps/contacts/lib/search.php b/apps/contacts/lib/search.php
index 5aad6a25f09535c1d515a78666e7b649023ff0a9..cf0a5fe69974b59d8bc8bf4ce86f217f2b06e8ce 100644
--- a/apps/contacts/lib/search.php
+++ b/apps/contacts/lib/search.php
@@ -1,6 +1,6 @@
 <?php
-class OC_Search_Provider_Contacts extends OC_Search_Provider{
-	function search($query){
+class OC_Search_Provider_Contacts implements OC_Search_Provider{
+	static function search($query){
 		$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser(), 1);
 // 		if(count($calendars)==0 || !OC_App::isEnabled('contacts')){
 // 			//return false;
@@ -26,4 +26,3 @@ class OC_Search_Provider_Contacts extends OC_Search_Provider{
 		return $results;
 	}
 }
-new OC_Search_Provider_Contacts();
diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php
index 99b1edd656f888eab758e89dcb25ff3f40440616..40e17b6ddd88f5001f6dc3e7d714262a2a50615d 100644
--- a/apps/contacts/lib/vcard.php
+++ b/apps/contacts/lib/vcard.php
@@ -174,6 +174,9 @@ class OC_Contacts_VCard{
 			if($property->name == 'UID'){
 				$uid = $property->value;
 			}
+			if($property->name == 'ORG'){
+				$org = $property->value;
+			}
 			if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN.
 				$email = $property->value;
 			}
@@ -184,6 +187,8 @@ class OC_Contacts_VCard{
 				$fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
 			} elseif($email) {
 				$fn = $email;
+			} elseif($org) {
+				$fn = $org;
 			} else {
 				$fn = 'Unknown Name';
 			}
@@ -217,32 +222,37 @@ class OC_Contacts_VCard{
 
 	/**
 	 * @brief Adds a card
-	 * @param integer $id Addressbook id
-	 * @param string $data  vCard file
-	 * @return insertid on success or null if card is not parseable.
+	 * @param integer $aid Addressbook id
+	 * @param OC_VObject $card  vCard file
+	 * @param string $uri the uri of the card, default based on the UID
+	 * @return insertid on success or null if no card.
 	 */
-	public static function add($id,$data){
-		$fn = null;
-
-		$card = OC_VObject::parse($data);
-		if(!is_null($card)){
-			OC_Contacts_App::$categories->loadFromVObject($card);
-			self::updateValuesFromAdd($card);
-			$data = $card->serialize();
-		}
-		else{
-			OC_Log::write('contacts','OC_Contacts_VCard::add. Error parsing VCard: '.$data,OC_Log::ERROR);
-			return null; // Ditch cards that can't be parsed by Sabre.
+	public static function add($aid, $card, $uri=null){
+		if(is_null($card)){
+			OC_Log::write('contacts','OC_Contacts_VCard::add. No vCard supplied', OC_Log::ERROR);
+			return null;
 		};
 
+		OC_Contacts_App::$categories->loadFromVObject($card);
+
+		self::updateValuesFromAdd($card);
+
 		$fn = $card->getAsString('FN');
-		$uid = $card->getAsString('UID');
-		$uri = $uid.'.vcf';
+		if (empty($fn)) {
+			$fn = null;
+		}
+
+		if (!$uri) {
+			$uid = $card->getAsString('UID');
+			$uri = $uid.'.vcf';
+		}
+
+		$data = $card->serialize();
 		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
-		$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
+		$result = $stmt->execute(array($aid,$fn,$data,$uri,time()));
 		$newid = OC_DB::insertid('*PREFIX*contacts_cards');
 
-		OC_Contacts_Addressbook::touch($id);
+		OC_Contacts_Addressbook::touch($aid);
 
 		return $newid;
 	}
@@ -256,51 +266,33 @@ class OC_Contacts_VCard{
 	 */
 	public static function addFromDAVData($id,$uri,$data){
 		$card = OC_VObject::parse($data);
-		if(!is_null($card)){
-			OC_Contacts_App::$categories->loadFromVObject($card);
-			self::updateValuesFromAdd($card);
-			$data = $card->serialize();
-		} else {
-			OC_Log::write('contacts','OC_Contacts_VCard::addFromDAVData. Error parsing VCard: '.$data, OC_Log::ERROR);
-			return null; // Ditch cards that can't be parsed by Sabre.
-		};
-		$fn = $card->getAsString('FN');
-
-		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
-		$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
-		$newid = OC_DB::insertid('*PREFIX*contacts_cards');
-
-		OC_Contacts_Addressbook::touch($id);
-
-		return $newid;
+		return self::add($id, $data, $uri);
 	}
 
 	/**
 	 * @brief edits a card
 	 * @param integer $id id of card
-	 * @param string $data  vCard file
+	 * @param OC_VObject $card  vCard file
 	 * @return boolean
 	 */
-	public static function edit($id, $data){
+	public static function edit($id, OC_VObject $card){
 		$oldcard = self::find($id);
-		$fn = null;
 
-		$card = OC_VObject::parse($data);
-		if(!is_null($card)){
-			OC_Contacts_App::$categories->loadFromVObject($card);
-			foreach($card->children as $property){
-				if($property->name == 'FN'){
-					$fn = $property->value;
-					break;
-				}
-			}
-		} else {
+		if(is_null($card)) {
 			return false;
 		}
+
+		OC_Contacts_App::$categories->loadFromVObject($card);
+
+		$fn = $card->getAsString('FN');
+		if (empty($fn)) {
+			$fn = null;
+		}
+
 		$now = new DateTime;
 		$card->setString('REV', $now->format(DateTime::W3C));
-		$data = $card->serialize();
 
+		$data = $card->serialize();
 		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
 		$result = $stmt->execute(array($fn,$data,time(),$id));
 
@@ -318,28 +310,8 @@ class OC_Contacts_VCard{
 	 */
 	public static function editFromDAVData($aid,$uri,$data){
 		$oldcard = self::findWhereDAVDataIs($aid,$uri);
-
-		$fn = null;
 		$card = OC_VObject::parse($data);
-		if(!is_null($card)){
-			OC_Contacts_App::$categories->loadFromVObject($card);
-			foreach($card->children as $property){
-				if($property->name == 'FN'){
-					$fn = $property->value;
-					break;
-				}
-			}
-		}
-		$now = new DateTime;
-		$card->setString('REV', $now->format(DateTime::W3C));
-		$data = $card->serialize();
-
-		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
-		$result = $stmt->execute(array($fn,$data,time(),$oldcard['id']));
-
-		OC_Contacts_Addressbook::touch($oldcard['addressbookid']);
-
-		return true;
+		return self::edit($oldcard['id'], $card);
 	}
 
 	/**
@@ -355,14 +327,6 @@ class OC_Contacts_VCard{
 		return true;
 	}
 
-	/**
-	 * @brief Creates a UID
-	 * @return string
-	 */
-	public static function createUID(){
-		return substr(md5(rand().time()),0,10);
-	}
-
 	/**
 	 * @brief deletes a card with the data provided by sabredav
 	 * @param integer $aid Addressbook id
diff --git a/apps/contacts/templates/part.contactphoto.php b/apps/contacts/templates/part.contactphoto.php
index 9e3f5876cd14a197e5ee267205ae57fc63792423..8107650d161fd14510049e19456f185cc24d07a1 100644
--- a/apps/contacts/templates/part.contactphoto.php
+++ b/apps/contacts/templates/part.contactphoto.php
@@ -3,7 +3,7 @@ $id = $_['id'];
 $wattr = isset($_['width'])?'width="'.$_['width'].'"':'';
 $hattr = isset($_['height'])?'height="'.$_['height'].'"':'';
 ?>
-<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OC_Helper::linkToAbsolute('contacts', 'photo.php'); ?>?id=<?php echo $id; ?>&amp;refresh=<?php echo rand(); ?>" />
+<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OC_Helper::linkToAbsolute('contacts', 'photo.php'); ?>?id=<?php echo $id; ?>" />
 <progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
 
 
diff --git a/apps/external/ajax/setsites.php b/apps/external/ajax/setsites.php
new file mode 100644
index 0000000000000000000000000000000000000000..0537b7ea5810fb92f9252c770b2100d8390641e4
--- /dev/null
+++ b/apps/external/ajax/setsites.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Copyright (c) 2011, Frank Karlitschek <karlitschek@kde.org>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../../../lib/base.php');
+OC_Util::checkAdminUser();
+
+$sites = array();
+for ($i = 0; $i < sizeof($_POST['site_name']); $i++) {
+	if (!empty($_POST['site_name'][$i]) && !empty($_POST['site_url'][$i])) {
+		array_push($sites, array($_POST['site_name'][$i], $_POST['site_url'][$i]));
+	}
+}
+
+if (sizeof($sites) == 0)
+	OC_Appconfig::deleteKey('external', 'sites');
+else
+	OC_Appconfig::setValue('external', 'sites', json_encode($sites));
+
+echo 'true';
+?>
diff --git a/apps/external/ajax/seturls.php b/apps/external/ajax/seturls.php
deleted file mode 100644
index e994385a1994f8596e2a59cb4f047dba6a470fa2..0000000000000000000000000000000000000000
--- a/apps/external/ajax/seturls.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011, Frank Karlitschek <karlitschek@kde.org>
- * This file is licensed under the Affero General Public License version 3 or later.
- * See the COPYING-README file.
- */
-
-require_once('../../../lib/base.php');
-OC_Util::checkAdminUser();
-
-if(isset($_POST['s1name'])) OC_Appconfig::setValue( 'external','site1name', $_POST['s1name'] );
-if(isset($_POST['s1url'])) OC_Appconfig::setValue( 'external','site1url', $_POST['s1url'] );
-if(isset($_POST['s2name'])) OC_Appconfig::setValue( 'external','site2name', $_POST['s2name'] );
-if(isset($_POST['s2url'])) OC_Appconfig::setValue( 'external','site2url', $_POST['s2url'] );
-if(isset($_POST['s3name'])) OC_Appconfig::setValue( 'external','site3name', $_POST['s3name'] );
-if(isset($_POST['s3url'])) OC_Appconfig::setValue( 'external','site3url', $_POST['s3url'] );
-if(isset($_POST['s4name'])) OC_Appconfig::setValue( 'external','site4name', $_POST['s4name'] );
-if(isset($_POST['s4url'])) OC_Appconfig::setValue( 'external','site4url', $_POST['s4url'] );
-if(isset($_POST['s5name'])) OC_Appconfig::setValue( 'external','site5name', $_POST['s5name'] );
-if(isset($_POST['s5url'])) OC_Appconfig::setValue( 'external','site5url', $_POST['s5url'] );
-
-echo 'true';
-
-?>
diff --git a/apps/external/appinfo/app.php b/apps/external/appinfo/app.php
index 0f536cbf4183d5c6ce0419c2a3584e534fa17da7..74e6d5c94c68c71f8f560b983804ed9260814922 100644
--- a/apps/external/appinfo/app.php
+++ b/apps/external/appinfo/app.php
@@ -1,37 +1,35 @@
 <?php
 
 /**
-* ownCloud - External plugin
-*
-* @author Frank Karlitschek
-* @copyright 2011 Frank Karlitschek karlitschek@kde.org
-* 
-* 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 Lesser General Public 
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-* 
-*/
-
-OC_APP::registerAdmin('external','settings');
-
-OC_App::register( array( 'order' => 70, 'id' => 'external', 'name' => 'External' ));
-
-if(OC_Appconfig::getValue( "external","site1name", '' )<>'')  OC_App::addNavigationEntry( array( 'id' => 'external_index1', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=1', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site1name", '' )));
-
-if(OC_Appconfig::getValue( "external","site2name", '' )<>'')  OC_App::addNavigationEntry( array( 'id' => 'external_index2', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=2', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site2name", '' )));
-
-if(OC_Appconfig::getValue( "external","site3name", '' )<>'')  OC_App::addNavigationEntry( array( 'id' => 'external_index3', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=3', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site3name", '' )));
-
-if(OC_Appconfig::getValue( "external","site4name", '' )<>'')  OC_App::addNavigationEntry( array( 'id' => 'external_index4', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=4', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site4name", '' )));
-
-if(OC_Appconfig::getValue( "external","site5name", '' )<>'')  OC_App::addNavigationEntry( array( 'id' => 'external_index5', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=5', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site5name", '' )));
-
+ * ownCloud - External plugin
+ *
+ * @author Frank Karlitschek
+ * @copyright 2011 Frank Karlitschek karlitschek@kde.org
+ * 
+ * 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 Lesser General Public 
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+OC::$CLASSPATH['OC_External'] = 'apps/external/lib/external.php';
+OC_Util::addStyle( 'external', 'style');
+
+OC_APP::registerAdmin('external', 'settings');
+
+OC_App::register(array('order' => 70, 'id' => 'external', 'name' => 'External'));
+
+$sites = OC_External::getSites();
+for ($i = 0; $i < sizeof($sites); $i++) {
+	OC_App::addNavigationEntry(
+			array('id' => 'external_index' . ($i + 1), 'order' => 80 + $i, 'href' => OC_Helper::linkTo('external', 'index.php') . '?id=' . ($i + 1), 'icon' => OC_Helper::imagePath('external', 'external.png'), 'name' => $sites[$i][0]));
+}
\ No newline at end of file
diff --git a/apps/external/css/style.css b/apps/external/css/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..f891cb4bc551e1fb9dc6783515151f188030f33b
--- /dev/null
+++ b/apps/external/css/style.css
@@ -0,0 +1,14 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+
+.site_url {
+    width: 250px;
+}
+
+.delete_button {
+    display: none;
+}
+
+.external_sites {
+    width: 450px;
+}
diff --git a/apps/external/index.php b/apps/external/index.php
index 51cdc344bbfbddd4926211ba54e8a5e4a3240d54..1c20f59eaffc9b0e280e1fdef58c776f10d6d842 100644
--- a/apps/external/index.php
+++ b/apps/external/index.php
@@ -1,42 +1,43 @@
 <?php
 
 /**
-* ownCloud - External plugin
-*
-* @author Frank Karlitschek
-* @copyright 2011 Frank Karlitschek karlitschek@kde.org
-* 
-* 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 Lesser General Public 
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-* 
-*/
-
+ * ownCloud - External plugin
+ *
+ * @author Frank Karlitschek
+ * @copyright 2011 Frank Karlitschek karlitschek@kde.org
+ * 
+ * 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 Lesser General Public 
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
 require_once('../../lib/base.php');
+require_once('lib/external.php');
 
 OC_Util::checkLoggedIn();
 
-if(isset($_GET['id'])){
+if (isset($_GET['id'])) {
 
-	$id=$_GET['id'];
+	$id = $_GET['id'];
 	$id = (int) $id;
 
-	$url=OC_Appconfig::getValue( "external","site".$id."url", '' );
-	OC_App::setActiveNavigationEntry( 'external_index'.$id );
-
-	$tmpl = new OC_Template( 'external', 'frame', 'user' );
-	$tmpl->assign('url',$url);
-	$tmpl->printPage();
+	$sites = OC_External::getSites();
+	if (sizeof($sites) >= $id) {
+		$url = $sites[$id - 1][1];
+		OC_App::setActiveNavigationEntry('external_index' . $id);
 
+		$tmpl = new OC_Template('external', 'frame', 'user');
+		$tmpl->assign('url', $url);
+		$tmpl->printPage();
+	}
 }
-
 ?>
diff --git a/apps/external/js/admin.js b/apps/external/js/admin.js
index 6b9b6c67737fe175f2af81b0cb043d348b28e2c0..0caaabd0b96f7af69a5d117babde842185e44061 100644
--- a/apps/external/js/admin.js
+++ b/apps/external/js/admin.js
@@ -1,67 +1,56 @@
 $(document).ready(function(){
+    	newSiteHtml = '<li><input type="text" class="site_name" name="site_name[]" value="" placeholder="Name" />\n\
+    <input type="text" name="site_url[]" class="site_url" value=""  placeholder="URL" />\n\
+<img class="svg action delete_button" src="'+OC.imagePath("core", "actions/delete") +'" title="Remove site" /></li>';
 
-
-
-	$('#s1name').blur(function(event){
+	// Handler functions
+	function addSiteEventHandler(event) {
 		event.preventDefault();
-		var post = $( "#s1name" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1name .msg', data);   });
-	});
+		
+		saveSites();
+	}
 
-	$('#s2name').blur(function(event){
+	function deleteButtonEventHandler(event) {
 		event.preventDefault();
-		var post = $( "#s2name" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2name .msg', data);   });
-	});
 
-	$('#s3name').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s3name" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3name .msg', data);   });
-	});
+		$(this).tipsy('hide');
+		$(this).parent().remove();
 
-	$('#s4name').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s4name" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4name .msg', data);   });
-	});
+		saveSites();
+	}
 
-	$('#s5name').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s5name" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5name .msg', data);   });
-	});
+	function saveSites() {
+		var post = $('#external').serialize();
+		$.post( OC.filePath('external','ajax','setsites.php') , post, function(data) {
+			// OC.msg.finishedSaving('#site_name .msg', data); 
+		});
+	}
 
-	$('#s1url').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s1url" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1url .msg', data);   });
-	});
+	function showDeleteButton(event) {
+		$(this).find('img.delete_button').fadeIn(100);
+	} 
 
-	$('#s2url').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s2url" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2url .msg', data);   });
-	});
+	function hideDeleteButton(event) {
+		$(this).find('img.delete_button').fadeOut(100);
+	}
 
-	$('#s3url').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s3url" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3url .msg', data);   });
-	});
+	// Initialize events
+	$('input[name^=site_]').change(addSiteEventHandler);
+	$('img.delete_button').click(deleteButtonEventHandler);
+	$('img.delete_button').tipsy();
 
-	$('#s4url').blur(function(event){
-		event.preventDefault();
-		var post = $( "#s4url" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4url .msg', data);   });
-	});
+	$('#external li').hover(showDeleteButton, hideDeleteButton);
 
-	$('#s5url').blur(function(event){
+	$('#add_external_site').click(function(event) {
 		event.preventDefault();
-		var post = $( "#s5url" ).serialize();
-		$.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5url .msg', data);   });
-	});
+		$('#external ul').append(newSiteHtml);
 
+		$('input.site_url:last').prev('input.site_name').andSelf().change(addSiteEventHandler);
+		$('img.delete_button').click(deleteButtonEventHandler);
+		$('img.delete_button:last').tipsy();
+		$('#external li:last').hover(showDeleteButton, hideDeleteButton);
+		
+	});
 
 });
 
diff --git a/apps/external/lib/external.php b/apps/external/lib/external.php
new file mode 100644
index 0000000000000000000000000000000000000000..9dd32321135d3030ead9cfd37f02a1b1257572d1
--- /dev/null
+++ b/apps/external/lib/external.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * ownCloud - gallery application
+ *
+ * @author Bartek Przybylski
+ * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.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 Lesser General Public 
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+class OC_External {
+
+	public static function getSites() {
+		if (($sites = json_decode(OC_Appconfig::getValue("external", "sites", ''))) != NULL) {
+			return $sites;	
+		}
+
+		return array();
+	}
+
+}
+
+?>
diff --git a/apps/external/settings.php b/apps/external/settings.php
index 3e0c34251281e6da9ed9009c74bf7de6e54900e8..416c9a5c11f13da7fc21c043cb43e17d65790ff1 100644
--- a/apps/external/settings.php
+++ b/apps/external/settings.php
@@ -6,17 +6,5 @@ OC_Util::addScript( "external", "admin" );
 
 $tmpl = new OC_Template( 'external', 'settings');
 
-	$tmpl->assign('s1name',OC_Appconfig::getValue( "external","site1name", '' ));
-	$tmpl->assign('s2name',OC_Appconfig::getValue( "external","site2name", '' ));
-	$tmpl->assign('s3name',OC_Appconfig::getValue( "external","site3name", '' ));
-	$tmpl->assign('s4name',OC_Appconfig::getValue( "external","site4name", '' ));
-	$tmpl->assign('s5name',OC_Appconfig::getValue( "external","site5name", '' ));
-
-	$tmpl->assign('s1url',OC_Appconfig::getValue( "external","site1url", '' ));
-	$tmpl->assign('s2url',OC_Appconfig::getValue( "external","site2url", '' ));
-	$tmpl->assign('s3url',OC_Appconfig::getValue( "external","site3url", '' ));
-	$tmpl->assign('s4url',OC_Appconfig::getValue( "external","site4url", '' ));
-	$tmpl->assign('s5url',OC_Appconfig::getValue( "external","site5url", '' ));
-
 return $tmpl->fetchPage();
 ?>
diff --git a/apps/external/templates/settings.php b/apps/external/templates/settings.php
index a72327d35c81f002356cb1f9f614f92a3a061f5a..a130477d465223e8049ee36ee40d323150eef419 100644
--- a/apps/external/templates/settings.php
+++ b/apps/external/templates/settings.php
@@ -1,23 +1,21 @@
 <form id="external">
 	<fieldset class="personalblock">
 		<strong>External Sites</strong><br />
-		<input type="text" name="s1name" id="s1name" value="<?php echo $_['s1name']; ?>" placeholder="<?php echo $l->t('Name');?>" />
-		<input type="text" name="s1url"  id="s1url"  value="<?php echo $_['s1url']; ?>"  placeholder="<?php echo $l->t('Url');?>" />
-                <br />
-		<input type="text" name="s2name" id="s2name" value="<?php echo $_['s2name']; ?>" placeholder="<?php echo $l->t('Name');?>" />
-		<input type="text" name="s2url"  id="s2url"  value="<?php echo $_['s2url']; ?>"  placeholder="<?php echo $l->t('Url');?>" />
-                <br />
-		<input type="text" name="s3name" id="s3name" value="<?php echo $_['s3name']; ?>" placeholder="<?php echo $l->t('Name');?>" />
-		<input type="text" name="s3url"  id="s3url"  value="<?php echo $_['s3url']; ?>"  placeholder="<?php echo $l->t('Url');?>" />
-                <br />
-		<input type="text" name="s4name" id="s4name" value="<?php echo $_['s4name']; ?>" placeholder="<?php echo $l->t('Name');?>" />
-		<input type="text" name="s4url"  id="s4url"  value="<?php echo $_['s4url']; ?>"  placeholder="<?php echo $l->t('Url');?>" />
-                <br />
-		<input type="text" name="s5name" id="s5name" value="<?php echo $_['s5name']; ?>" placeholder="<?php echo $l->t('Name');?>" />
-		<input type="text" name="s5url"  id="s5url"  value="<?php echo $_['s5url']; ?>"  placeholder="<?php echo $l->t('Url');?>" />
-                <br />
+		<ul class="external_sites">
 
+		<?php
+		$sites = OC_External::getSites();
+		for($i = 0; $i < sizeof($sites); $i++) {
+			echo '<li><input type="text" name="site_name[]" class="site_name" value="'.$sites[$i][0].'" placeholder="'.$l->t('Name').'" />
+			<input type="text" class="site_url" name="site_url[]"  value="'.$sites[$i][1].'"  placeholder="'.$l->t('URL').'" />
+			<img class="svg action delete_button" src="'.image_path("", "actions/delete.svg") .'" title="'.$l->t("Remove site").'" />
+			</li>';
+		}
+		?>
 
-<span class="msg"></span>
+		</ul>
+
+        <input type="button" id="add_external_site" value="Add" />
+		<span class="msg"></span>
 	</fieldset>
 </form>
diff --git a/apps/files_archive/appinfo/app.php b/apps/files_archive/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..693c28d98a07a44d4b6e7fffc8a9d80c02fb895d
--- /dev/null
+++ b/apps/files_archive/appinfo/app.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php';
+foreach(array('ZIP') as $type){
+	OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php';
+}
+
+OC::$CLASSPATH['OC_Filestorage_Archive']='apps/files_archive/lib/storage.php';
+
+OC_Hook::connect('OC_Filesystem','get_mountpoint','OC_Filestorage_Archive','autoMount');
+
+OC_Util::addScript( 'files_archive', 'archive' );
diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..df767d39f6b602d297fb040c3017737b870dfa50
--- /dev/null
+++ b/apps/files_archive/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_archive</id>
+	<name>Archive support</name>
+	<description>Transparent opening of archives</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>3</require>
+</info>
diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec316c7bf2c3dd0d2e45cd887a3554341dbe2042
--- /dev/null
+++ b/apps/files_archive/js/archive.js
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$(document).ready(function() {
+	if(typeof FileActions!=='undefined'){
+		FileActions.register('application/zip','Open','',function(filename){
+			window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
+		});
+		FileActions.setDefault('application/zip','Open');
+	}
+});
diff --git a/apps/files_archive/lib/archive.php b/apps/files_archive/lib/archive.php
new file mode 100644
index 0000000000000000000000000000000000000000..be89f894fb7ff8c3fcab13a5a81ce9bb53d31100
--- /dev/null
+++ b/apps/files_archive/lib/archive.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+abstract class OC_Archive{
+	/**
+	 * open any of the supporeted archive types
+	 * @param string path
+	 * @return OC_Archive
+	 */
+	public static function open($path){
+		$ext=substr($path,strrpos($path,'.'));
+		switch($ext){
+			case '.zip':
+				return new OC_Archive_ZIP($path);
+		}
+	}
+	
+	abstract function __construct($source);
+	/**
+	 * add an empty folder to the archive
+	 * @param string path
+	 * @return bool
+	 */
+	abstract function addFolder($path);
+	/**
+	 * add a file to the archive
+	 * @param string path
+	 * @param string source either a local file or string data
+	 * @return bool
+	 */
+	abstract function addFile($path,$source='');
+	/**
+	 * rename a file or folder in the archive
+	 * @param string source
+	 * @param string dest
+	 * @return bool
+	 */
+	abstract function rename($source,$dest);
+	/**
+	 * get the uncompressed size of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	abstract function filesize($path);
+	/**
+	 * get the last modified time of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	abstract function mtime($path);
+	/**
+	 * get the files in a folder
+	 * @param path
+	 * @return array
+	 */
+	abstract function getFolder($path);
+	/**
+	 *get all files in the archive
+	 * @return array
+	 */
+	abstract function getFiles();
+	/**
+	 * get the content of a file
+	 * @param string path
+	 * @return string
+	 */
+	abstract function getFile($path);
+	/**
+	 * extract a single file from the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	abstract function extractFile($path,$dest);
+	/**
+	 * check if a file or folder exists in the archive
+	 * @param string path
+	 * @return bool
+	 */
+	abstract function fileExists($path);
+	/**
+	 * remove a file or folder from the archive
+	 * @param string path
+	 * @return bool
+	 */
+	abstract function remove($path);
+	/**
+	 * get a file handler
+	 * @param string path
+	 * @param string mode
+	 * @return resource
+	 */
+	abstract function getStream($path,$mode);
+}
diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php
new file mode 100644
index 0000000000000000000000000000000000000000..72a96ca5a5d7f4b8eead02d621f87818d18accdb
--- /dev/null
+++ b/apps/files_archive/lib/storage.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Filestorage_Archive extends OC_Filestorage_Common{
+	/**
+	 * underlying local storage used for missing functions
+	 * @var OC_Archive
+	 */
+	private $archive;
+	private $path;
+	private static $mounted=array();
+	private static $enableAutomount=true;
+	private static $rootView;
+	
+	private function stripPath($path){//files should never start with /
+		if(substr($path,0,1)=='/'){
+			$path=substr($path,1);
+		}
+		return $path;
+	}
+	
+	public function __construct($params){
+		$this->archive=OC_Archive::open($params['archive']);
+		$this->path=$params['archive'];
+	}
+
+	public function mkdir($path){
+		$path=$this->stripPath($path);
+		return $this->archive->addFolder($path);
+	}
+	public function rmdir($path){
+		$path=$this->stripPath($path);
+		return $this->archive->remove($path.'/');
+	}
+	public function opendir($path){
+		$path=$this->stripPath($path);
+		$content=$this->archive->getFolder($path);
+		foreach($content as &$file){
+			if(substr($file,-1)=='/'){
+				$file=substr($file,0,-1);
+			}
+		}
+		$id=md5($this->path.$path);
+		OC_FakeDirStream::$dirs[$id]=$content;
+		return opendir('fakedir://'.$id);
+	}
+	public function stat($path){
+		$ctime=filectime($this->path);
+		$path=$this->stripPath($path);
+		if($path==''){
+			$stat=stat($this->path);
+		}else{
+			if($this->is_dir($path)){
+				$stat=array('size'=>0);
+				$stat['mtime']=filemtime($this->path);
+			}else{
+				$stat=array();
+				$stat['mtime']=$this->archive->mtime($path);
+				$stat['size']=$this->archive->filesize($path);
+			}
+		}
+		$stat['ctime']=$ctime;
+		return $stat;
+	}
+	public function filetype($path){
+		$path=$this->stripPath($path);
+		if($path==''){
+			return 'dir';
+		}
+		if(substr($path,-1)=='/'){
+			return $this->archive->fileExists($path)?'dir':'file';
+		}else{
+			return $this->archive->fileExists($path.'/')?'dir':'file';
+		}
+	}
+	public function is_readable($path){
+		return is_readable($this->path);
+	}
+	public function is_writable($path){
+		return is_writable($this->path);
+	}
+	public function file_exists($path){
+		$path=$this->stripPath($path);
+		if($path==''){
+			return file_exists($this->path);
+		}
+		return $this->archive->fileExists($path) or $this->archive->fileExists($path.'/');
+	}
+	public function unlink($path){
+		$path=$this->stripPath($path);
+		return $this->archive->remove($path);
+	}
+	public function fopen($path,$mode){
+		$path=$this->stripPath($path);
+		return $this->archive->getStream($path,$mode);
+	}
+	public function free_space($path){
+		return 0;
+	}
+	public function touch($path, $mtime=null){
+		if(is_null($mtime)){
+			$tmpFile=OC_Helper::tmpFile();
+			$this->archive->extractFile($path,$tmpFile);
+			$this->archive->addfile($path,$tmpFile);
+		}else{
+			return false;//not supported
+		}
+	}
+
+	/**
+	 * automount paths from file hooks
+	 * @param aray params
+	 */
+	public static function autoMount($params){
+		if(!self::$enableAutomount){
+			return;
+		}
+		$path=$params['path'];
+		if(!self::$rootView){
+			self::$rootView=new OC_FilesystemView('');
+		}
+		self::$enableAutomount=false;//prevent recursion
+		$supported=array('zip');
+		foreach($supported as $type){
+			$ext='.'.$type.'/';
+			if(($pos=strpos(strtolower($path),$ext))!==false){
+				$archive=substr($path,0,$pos+strlen($ext)-1);
+				if(self::$rootView->file_exists($archive) and  array_search($archive,self::$mounted)===false){
+					$localArchive=self::$rootView->getLocalFile($archive);
+					OC_Filesystem::mount('OC_Filestorage_Archive',array('archive'=>$localArchive),$archive.'/');
+					self::$mounted[]=$archive;
+				}
+			}
+		}
+		self::$enableAutomount=true;
+	}
+}
diff --git a/apps/files_archive/lib/zip.php b/apps/files_archive/lib/zip.php
new file mode 100644
index 0000000000000000000000000000000000000000..eab101b3a5c0d7ae2bcff246d2527309201eff0e
--- /dev/null
+++ b/apps/files_archive/lib/zip.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Archive_ZIP extends OC_Archive{
+	/**
+	 * @var ZipArchive zip
+	 */
+	private $zip=null;
+	private $contents=array();
+	private $success=false;
+	private $path;
+	
+	function __construct($source){
+		$this->path=$source;
+		$this->zip=new ZipArchive();
+		if($this->zip->open($source,ZipArchive::CREATE)){
+		}else{
+			OC_LOG::write('files_archive','Error while opening archive '.$source,OC_Log::WARN);
+		}
+	}
+	/**
+	 * add an empty folder to the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function addFolder($path){
+		return $this->zip->addEmptyDir($path);
+	}
+	/**
+	 * add a file to the archive
+	 * @param string path
+	 * @param string source either a local file or string data
+	 * @return bool
+	 */
+	function addFile($path,$source=''){
+		if(file_exists($source)){
+			$result=$this->zip->addFile($source,$path);
+		}else{
+			$result=$this->zip->addFromString($path,$source);
+		}
+		if($result){
+			$this->zip->close();//close and reopen to save the zip
+			$this->zip->open($this->path);
+		}
+		return $result;
+	}
+	/**
+	 * rename a file or folder in the archive
+	 * @param string source
+	 * @param string dest
+	 * @return bool
+	 */
+	function rename($source,$dest){
+		return $this->zip->renameName($source,$dest);
+	}
+	/**
+	 * get the uncompressed size of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	function filesize($path){
+		$stat=$this->zip->statName($path);
+		return $stat['size'];
+	}
+	/**
+	 * get the last modified time of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	function mtime($path){
+		$stat=$this->zip->statName($path);
+		return $stat['mtime'];
+	}
+	/**
+	 * get the files in a folder
+	 * @param path
+	 * @return array
+	 */
+	function getFolder($path){
+		$files=$this->getFiles();
+		$folderContent=array();
+		$pathLength=strlen($path);
+		foreach($files as $file){
+			if(substr($file,0,$pathLength)==$path and $file!=$path){
+				if(strrpos(substr($file,0,-1),'/')<=$pathLength){
+					$folderContent[]=substr($file,$pathLength);
+				}
+			}
+		}
+		return $folderContent;
+	}
+	/**
+	 *get all files in the archive
+	 * @return array
+	 */
+	function getFiles(){
+		if(count($this->contents)){
+			return $this->contents;
+		}
+		$fileCount=$this->zip->numFiles;
+		$files=array();
+		for($i=0;$i<$fileCount;$i++){
+			$files[]=$this->zip->getNameIndex($i);
+		}
+		$this->contents=$files;
+		return $files;
+	}
+	/**
+	 * get the content of a file
+	 * @param string path
+	 * @return string
+	 */
+	function getFile($path){
+		return $this->zip->getFromName($path);
+	}
+	/**
+	 * extract a single file from the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	function extractFile($path,$dest){
+		$fp = $this->zip->getStream($path);
+		file_put_contents($dest,$fp);
+	}
+	/**
+	 * check if a file or folder exists in the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function fileExists($path){
+		return $this->zip->locateName($path)!==false;
+	}
+	/**
+	 * remove a file or folder from the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function remove($path){
+		return $this->zip->deleteName($path);
+	}
+	/**
+	 * get a file handler
+	 * @param string path
+	 * @param string mode
+	 * @return resource
+	 */
+	function getStream($path,$mode){
+		if($mode=='r' or $mode=='rb'){
+			return $this->zip->getStream($path);
+		}else{//since we cant directly get a writable stream, make a temp copy of the file and put it back in the archive when the stream is closed
+			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->fileExists($path)){
+				$this->extractFile($path,$tmpFile);
+			}
+			self::$tempFiles[$tmpFile]=$path;
+			return fopen('close://'.$tmpFile,$mode);
+		}
+	}
+
+	private static $tempFiles=array();
+	/**
+	 * write back temporary files
+	 */
+	function writeBack($tmpFile){
+		if(isset(self::$tempFiles[$tmpFile])){
+			$this->addFile(self::$tempFiles[$tmpFile],$tmpFile);
+			unlink($tmpFile);
+		}
+	}
+}
diff --git a/apps/files_archive/tests/archive.php b/apps/files_archive/tests/archive.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e26b5e03b5c430456394f7e28f9a6030beb25d6
--- /dev/null
+++ b/apps/files_archive/tests/archive.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+abstract class Test_Archive extends UnitTestCase {
+	/**
+	 * @var OC_Archive
+	 */
+	protected $instance;
+
+	/**
+	 * get the existing test archive
+	 * @return OC_Archive
+	 */
+	abstract protected function getExisting();
+	/**
+	 * get a new archive for write testing
+	 * @return OC_Archive
+	 */
+	abstract protected function getNew();
+	
+	public function testGetFiles(){
+		$this->instance=$this->getExisting();
+		$allFiles=$this->instance->getFiles();
+		$expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt');
+		$this->assertEqual(4,count($allFiles));
+		foreach($expected as $file){
+			$this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive');
+			$this->assertTrue($this->instance->fileExists($file));
+		}
+		$this->assertFalse($this->instance->fileExists('non/existing/file'));
+		
+		$rootContent=$this->instance->getFolder('');
+		$expected=array('lorem.txt','logo-wide.png','dir/');
+		$this->assertEqual(3,count($rootContent));
+		foreach($expected as $file){
+			$this->assertNotIdentical(false,array_search($file,$rootContent),'cant find '.$file.' in archive');
+		}
+
+		$dirContent=$this->instance->getFolder('dir/');
+		$expected=array('lorem.txt');
+		$this->assertEqual(1,count($dirContent));
+		foreach($expected as $file){
+			$this->assertNotIdentical(false,array_search($file,$dirContent),'cant find '.$file.' in archive');
+		}
+	}
+	
+	public function testContent(){
+		$this->instance=$this->getExisting();
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$textFile=$dir.'/lorem.txt';
+		$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt'));
+		
+		$tmpFile=OC_Helper::tmpFile('.txt');
+		$this->instance->extractFile('lorem.txt',$tmpFile);
+		$this->assertEqual(file_get_contents($textFile),file_get_contents($tmpFile));
+	}
+
+	public function testWrite(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$textFile=$dir.'/lorem.txt';
+		$this->instance=$this->getNew();
+		$this->assertEqual(0,count($this->instance->getFiles()));
+		$this->instance->addFile('lorem.txt',$textFile);
+		$this->assertEqual(1,count($this->instance->getFiles()));
+		$this->assertTrue($this->instance->fileExists('lorem.txt'));
+		
+		$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt'));
+		$this->instance->addFile('lorem.txt','foobar');
+		$this->assertEqual('foobar',$this->instance->getFile('lorem.txt'));
+	}
+
+	public function testReadStream(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$this->instance=$this->getExisting();
+		$fh=$this->instance->getStream('lorem.txt','r');
+		$this->assertTrue($fh);
+		$content=fread($fh,$this->instance->filesize('lorem.txt'));
+		fclose($fh);
+		$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$content);
+	}
+	public function testWriteStream(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$this->instance=$this->getNew();
+		$fh=$this->instance->getStream('lorem.txt','w');
+		$source=fopen($dir.'/lorem.txt','r');
+		OC_Helper::streamCopy($source,$fh);
+		fclose($source);
+		fclose($fh);
+		$this->assertTrue($this->instance->fileExists('lorem.txt'));
+		$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt'));
+	}
+}
diff --git a/apps/files_archive/tests/storage.php b/apps/files_archive/tests/storage.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d0a83356bdb22a45b3dfdd132c4f15f93b7a9e5
--- /dev/null
+++ b/apps/files_archive/tests/storage.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class Test_Filestorage_Archive_Zip extends Test_FileStorage {
+	/**
+	 * @var string tmpDir
+	 */
+	private $tmpFile;
+	
+	public function setUp(){
+		$this->tmpFile=OC_Helper::tmpFile('.zip');
+		$this->instance=new OC_Filestorage_Archive(array('archive'=>$this->tmpFile));
+	}
+
+	public function tearDown(){
+		unlink($this->tmpFile);
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_archive/tests/zip.php b/apps/files_archive/tests/zip.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ff713eda706361be11a668ac37b5b659b835520
--- /dev/null
+++ b/apps/files_archive/tests/zip.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('archive.php');
+
+class Test_Archive_ZIP extends Test_Archive{
+	protected function getExisting(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		return new OC_Archive_ZIP($dir.'/data.zip');
+	}
+
+	protected function getNew(){
+		return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip'));
+	}
+}
diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..68c445d5d775299801c846bfd8382aa109884c0e
--- /dev/null
+++ b/apps/files_encryption/appinfo/app.php
@@ -0,0 +1,19 @@
+<?php
+
+OC::$CLASSPATH['OC_Crypt'] = 'apps/files_encryption/lib/crypt.php';
+OC::$CLASSPATH['OC_CryptStream'] = 'apps/files_encryption/lib/cryptstream.php';
+OC::$CLASSPATH['OC_FileProxy_Encryption'] = 'apps/files_encryption/lib/proxy.php';
+
+OC_FileProxy::register(new OC_FileProxy_Encryption());
+
+OC_Hook::connect('OC_User','post_login','OC_Crypt','loginListener');
+
+stream_wrapper_register('crypt','OC_CryptStream');
+
+if(!isset($_SESSION['enckey']) and OC_User::isLoggedIn()){//force the user to re-loggin if the encryption key isn't unlocked (happens when a user is logged in before the encryption app is enabled)
+	OC_User::logout();
+	header("Location: ".OC::$WEBROOT.'/');
+	exit();
+}
+
+OC_App::registerAdmin('files_encryption', 'settings');
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..053044aaed25a74d2def50f69259fa33fc9696ff
--- /dev/null
+++ b/apps/files_encryption/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_encryption</id>
+	<name>Encryption</name>
+	<description>Server side encryption of files</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>3</require>
+</info>
diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js
new file mode 100644
index 0000000000000000000000000000000000000000..adbf0c8724582e5cabaa9fde98c523eb9d262d82
--- /dev/null
+++ b/apps/files_encryption/js/settings.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+
+$(document).ready(function(){
+	$('#encryption_blacklist').multiSelect({
+		oncheck:blackListChange,
+		onuncheck:blackListChange,
+		createText:'...',
+	});
+	
+	function blackListChange(){
+		var blackList=$('#encryption_blacklist').val().join(',');
+		OC.AppConfig.setValue('files_encryption','type_blacklist',blackList);
+	}
+})
\ No newline at end of file
diff --git a/lib/crypt.php b/apps/files_encryption/lib/crypt.php
similarity index 51%
rename from lib/crypt.php
rename to apps/files_encryption/lib/crypt.php
index 6002067948040a712517881ec1b4396a5bc5a2c2..0a593b98c4b366cf7e7442814296e70e8ef43ef4 100644
--- a/lib/crypt.php
+++ b/apps/files_encryption/lib/crypt.php
@@ -37,90 +37,111 @@ require_once('Crypt_Blowfish/Blowfish.php');
  * This class is for crypting and decrypting
  */
 class OC_Crypt {
+	static private $bf = null;
 
-        static $encription_extension='.encrypted';
+	public static function loginListener($params){
+		self::init($params['uid'],$params['password']);
+	}
 
 	public static function init($login,$password) {
-		$_SESSION['user_password'] = $password;  // save the password as passcode for the encryption
-		if(OC_User::isLoggedIn()){
-			// does key exist?
-			if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){
-				OC_Crypt::createkey($_SESSION['user_password']);
-			}
+		$view=new OC_FilesystemView('/'.$login);
+		OC_FileProxy::$enabled=false;
+		if(!$view->file_exists('/encryption.key')){// does key exist?
+			OC_Crypt::createkey($login,$password);
 		}
+		$key=$view->file_get_contents('/encryption.key');
+		OC_FileProxy::$enabled=true;
+		$_SESSION['enckey']=OC_Crypt::decrypt($key, $password);
 	}
 
+	/**
+	 * get the blowfish encryption handeler for a key
+	 * @param string $key (optional)
+	 * @return Crypt_Blowfish
+	 *
+	 * if the key is left out, the default handeler will be used
+	 */
+	public static function getBlowfish($key=''){
+		if($key){
+			return new Crypt_Blowfish($key);
+		}else{
+			if(!isset($_SESSION['enckey'])){
+				return false;
+			}
+			if(!self::$bf){
+				self::$bf=new Crypt_Blowfish($_SESSION['enckey']);
+			}
+			return self::$bf;
+		}
+	}
 
+	public static function createkey($username,$passcode) {
+		// generate a random key
+		$key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999);
 
-	public static function createkey($passcode) {
-		if(OC_User::isLoggedIn()){
-			// generate a random key
-			$key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999);
-
-			// encrypt the key with the passcode of the user
-			$enckey=OC_Crypt::encrypt($key,$passcode);
+		// encrypt the key with the passcode of the user
+		$enckey=OC_Crypt::encrypt($key,$passcode);
 
-			// Write the file
-		        $username=OC_USER::getUser();
-			@file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey );
-		}
+		// Write the file
+		$proxyEnabled=OC_FileProxy::$enabled;
+		OC_FileProxy::$enabled=false;
+		$view=new OC_FilesystemView('/'.$username);
+		$view->file_put_contents('/encryption.key',$enckey);
+		OC_FileProxy::$enabled=$proxyEnabled;
 	}
 
-	public static function changekeypasscode( $newpasscode) {
+	public static function changekeypasscode($oldPassword, $newPassword) {
 		if(OC_User::isLoggedIn()){
-		        $username=OC_USER::getUser();
+			$username=OC_USER::getUser();
+			$view=new OC_FilesystemView('/'.$username);
 
 			// read old key
-			$key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key');
+			$key=$view->file_get_contents('/encryption.key');
 
 			// decrypt key with old passcode
-			$key=OC_Crypt::decrypt($key, $_SESSION['user_password']);
+			$key=OC_Crypt::decrypt($key, $oldPassword);
 
 			// encrypt again with new passcode
-			$key=OC_Crypt::encrypt($key,$newpassword);
+			$key=OC_Crypt::encrypt($key, $newPassword);
 
 			// store the new key
-			file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key );
-
-			 $_SESSION['user_password']=$newpasscode;
+			$view->file_put_contents('/encryption.key', $key );
 		}
 	}
 
 	/**
 	 * @brief encrypts an content
 	 * @param $content the cleartext message you want to encrypt
-	 * @param $key the encryption key
+	 * @param $key the encryption key (optional)
 	 * @returns encrypted content
 	 *
 	 * This function encrypts an content
 	 */
-	public static function encrypt( $content, $key) {
-		$bf = new Crypt_Blowfish($key);
+	public static function encrypt( $content, $key='') {
+		$bf = self::getBlowfish($key);
 		return($bf->encrypt($content));
 	}
 
+	/**
+	* @brief decryption of an content
+	* @param $content the cleartext message you want to decrypt
+	* @param $key the encryption key (optional)
+	* @returns cleartext content
+	*
+	* This function decrypts an content
+	*/
+	public static function decrypt( $content, $key='') {
+		$bf = self::getBlowfish($key);
+		return($bf->decrypt($content));
+	}
 
-        /**
-         * @brief decryption of an content
-         * @param $content the cleartext message you want to decrypt
-         * @param $key the encryption key
-         * @returns cleartext content
-         *
-         * This function decrypts an content
-         */
-        public static function decrypt( $content, $key) {
-		$bf = new Crypt_Blowfish($key);
-		return($bf->encrypt($contents));
-        }       
-
-
-        /**
-         * @brief encryption of a file
-         * @param $filename
-         * @param $key the encryption key
-         *
-         * This function encrypts a file
-         */
+	/**
+	* @brief encryption of a file
+	* @param $filename
+	* @param $key the encryption key
+	*
+	* This function encrypts a file
+	*/
 	public static function encryptfile( $filename, $key) {
 		$handleread  = fopen($filename, "rb");
 		if($handleread<>FALSE) {
@@ -158,8 +179,28 @@ class OC_Crypt {
 		}
 		fclose($handleread);
 	}
-
-
-
-
+	
+	/**
+	 * encrypt data in 8192b sized blocks
+	 */
+	public static function blockEncrypt($data){
+		$result='';
+		while(strlen($data)){
+			$result=self::encrypt(substr($data,0,8192));
+			$data=substr($data,8192);
+		}
+		return $result;
+	}
+	
+	/**
+	 * decrypt data in 8192b sized blocks
+	 */
+	public static function blockDecrypt($data){
+		$result='';
+		while(strlen($data)){
+			$result=self::decrypt(substr($data,0,8192));
+			$data=substr($data,8192);
+		}
+		return $result;
+	}
 }
diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php
new file mode 100644
index 0000000000000000000000000000000000000000..86583096f1dbeaba4273dad36bfd2eb829550da7
--- /dev/null
+++ b/apps/files_encryption/lib/cryptstream.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Robin Appelman
+ * @copyright 2011 Robin Appelman icewind1991@gmail.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/>.
+ *
+ */
+
+/**
+ * transparently encrypted filestream
+ *
+ * you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream)
+ *   and then fopen('crypt://streams/foo');
+ */
+
+class OC_CryptStream{
+	public static $sourceStreams=array();
+	private $source;
+	private $path;
+	private $readBuffer;//for streams that dont support seeking
+	private $meta=array();//header/meta for source stream
+
+	public function stream_open($path, $mode, $options, &$opened_path){
+		$path=str_replace('crypt://','',$path);
+		if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){
+			$this->source=self::$sourceStreams[basename($path)]['stream'];
+			$this->path=self::$sourceStreams[basename($path)]['path'];
+		}else{
+			$this->path=$path;
+			OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG);
+			OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file
+			$this->source=OC_FileSystem::fopen($path,$mode);
+			OC_FileProxy::$enabled=true;
+			if(!is_resource($this->source)){
+				OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR);
+			}
+		}
+		if(is_resource($this->source)){
+			$this->meta=stream_get_meta_data($this->source);
+		}
+		return is_resource($this->source);
+	}
+	
+	public function stream_seek($offset, $whence=SEEK_SET){
+		fseek($this->source,$offset,$whence);
+	}
+	
+	public function stream_tell(){
+		return ftell($this->source);
+	}
+	
+	public function stream_read($count){
+		$pos=0;
+		$currentPos=ftell($this->source);
+		$offset=$currentPos%8192;
+		$result='';
+		if($offset>0){
+			if($this->meta['seekable']){
+				fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used
+			}else{
+				$pos=strlen($this->readBuffer);
+				$result=$this->readBuffer;
+			}
+		}
+		while($count>$pos){
+			$data=fread($this->source,8192);
+			$pos+=8192;
+			if(strlen($data)){
+				$result.=OC_Crypt::decrypt($data);
+			}
+		}
+		if(!$this->meta['seekable']){
+			$this->readBuffer=substr($result,$count);
+		}
+		return substr($result,0,$count);
+	}
+	
+	public function stream_write($data){
+		$length=strlen($data);
+		$written=0;
+		$currentPos=ftell($this->source);
+		if($currentPos%8192!=0){
+			//make sure we always start on a block start
+			fseek($this->source,-($currentPos%8192),SEEK_CUR);
+			$encryptedBlock=fread($this->source,8192);
+			fseek($this->source,-($currentPos%8192),SEEK_CUR);
+			$block=OC_Crypt::decrypt($encryptedBlock);
+			$data=substr($block,0,$currentPos%8192).$data;
+		}
+		while(strlen($data)>0){
+			if(strlen($data)<8192){
+				//fetch the current data in that block and append it to the input so we always write entire blocks
+				$oldPos=ftell($this->source);
+				$encryptedBlock=fread($this->source,8192);
+				fseek($this->source,$oldPos);
+				$block=OC_Crypt::decrypt($encryptedBlock);
+				$data.=substr($block,strlen($data));
+			}
+			$encrypted=OC_Crypt::encrypt(substr($data,0,8192));
+			fwrite($this->source,$encrypted);
+			$data=substr($data,8192);
+		}
+		return $length;
+	}
+
+	public function stream_set_option($option,$arg1,$arg2){
+		switch($option){
+			case STREAM_OPTION_BLOCKING:
+				stream_set_blocking($this->source,$arg1);
+				break;
+			case STREAM_OPTION_READ_TIMEOUT:
+				stream_set_timeout($this->source,$arg1,$arg2);
+				break;
+			case STREAM_OPTION_WRITE_BUFFER:
+				stream_set_write_buffer($this->source,$arg1,$arg2);
+		}
+	}
+
+	public function stream_stat(){
+		return fstat($this->source);
+	}
+	
+	public function stream_lock($mode){
+		flock($this->source,$mode);
+	}
+	
+	public function stream_flush(){
+		return fflush($this->source);
+	}
+
+	public function stream_eof(){
+		return feof($this->source);
+	}
+
+	public function stream_close(){
+		OC_FileCache::put($this->path,array('encrypted'=>true));
+		return fclose($this->source);
+	}
+}
\ No newline at end of file
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..c1c26d7754f31c9498ce6983bbef6028c47a9b93
--- /dev/null
+++ b/apps/files_encryption/lib/proxy.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2011 Robin Appelman icewind1991@gmail.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/>.
+*
+*/
+
+/**
+ * transparent encryption
+ */
+
+class OC_FileProxy_Encryption extends OC_FileProxy{
+	private static $blackList=null; //mimetypes blacklisted from encryption
+	private static $metaData=array(); //metadata cache
+	
+	/**
+	 * check if a file should be encrypted during write
+	 * @param string $path
+	 * @return bool
+	 */
+	private static function shouldEncrypt($path){
+		if(is_null(self::$blackList)){
+			self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
+		}
+		if(self::isEncrypted($path)){
+			return true;
+		}
+		$extention=substr($path,strrpos($path,'.')+1);
+		if(array_search($extention,self::$blackList)===false){
+			return true;
+		}
+	}
+
+	/**
+	 * check if a file is encrypted
+	 * @param string $path
+	 * @return bool
+	 */
+	private static function isEncrypted($path){
+		if(isset(self::$metaData[$path])){
+			$metadata=self::$metaData[$path];
+		}else{
+			$metadata=OC_FileCache::getCached($path);
+			self::$metaData[$path]=$metadata;
+		}
+		return (bool)$metadata['encrypted'];
+	}
+	
+	public function preFile_put_contents($path,&$data){
+		if(self::shouldEncrypt($path)){
+			if (!is_resource($data)) {//stream put contents should have been converter to fopen
+				$data=OC_Crypt::blockEncrypt($data);
+				OC_FileCache::put($path,array('encrypted'=>true));
+			}
+		}
+	}
+	
+	public function postFile_get_contents($path,$data){
+		if(self::isEncrypted($path)){
+			$data=OC_Crypt::blockDecrypt($data);
+		}
+		return $data;
+	}
+	
+	public function postFopen($path,&$result){
+		if(!$result){
+			return $result;
+		}
+		$meta=stream_get_meta_data($result);
+		if(self::isEncrypted($path)){
+			fclose($result);
+			$result=fopen('crypt://'.$path,$meta['mode']);
+		}elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb'){
+			if(OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0){
+				//first encrypt the target file so we don't end up with a half encrypted file
+				OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG);
+				$tmp=fopen('php://temp');
+				while(!feof($result)){
+					$chunk=fread($result,8192);
+					if($chunk){
+						fwrite($tmp,$chunk);
+					}
+				}
+				fclose($result);
+				OC_Filesystem::file_put_contents($path,$tmp);
+				fclose($tmp);
+			}
+			$result=fopen('crypt://'.$path,$meta['mode']);
+		}
+		return $result;
+	}
+
+	public function postGetMimeType($path,$mime){
+		if(self::isEncrypted($path)){
+			$mime=OC_Helper::getMimeType('crypt://'.$path,'w');
+		}
+		return $mime;
+	}
+}
diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..396ad1ba78d7f28e9374110664dfb07169b2c699
--- /dev/null
+++ b/apps/files_encryption/settings.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright (c) 2011 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$tmpl = new OC_Template( 'files_encryption', 'settings');
+$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg'));
+$tmpl->assign('blacklist',$blackList);
+
+OC_Util::addScript('files_encryption','settings');
+OC_Util::addScript('core','multiselect');
+
+return $tmpl->fetchPage();
diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..724a03836a8ea37767c8aee32155a4e5d8d19bfc
--- /dev/null
+++ b/apps/files_encryption/templates/settings.php
@@ -0,0 +1,11 @@
+<form id="calendar">
+	<fieldset class="personalblock">
+	<strong><?php echo $l->t('Encryption'); ?></strong>
+		<?php echo $l->t("Exclude the following file types from encryption"); ?>
+		<select id='encryption_blacklist' title="<?php echo $l->t('None')?>" multiple="multiple">
+			<?php foreach($_["blacklist"] as $type): ?>
+				<option selected="selected" value="<?php echo $type;?>"><?php echo $type;?></option>
+			<?php endforeach;?>
+		</select>
+	</fieldset>
+</form>
diff --git a/apps/files_pdfviewer/appinfo/info.xml b/apps/files_pdfviewer/appinfo/info.xml
index 86a6c3f22f754eaef9b09fa83ee21ab0dfb928f1..f133f1900d77173f2f5d49522735ad492f1e20e7 100755
--- a/apps/files_pdfviewer/appinfo/info.xml
+++ b/apps/files_pdfviewer/appinfo/info.xml
@@ -1,7 +1,8 @@
 <?xml version="1.0"?> 
 <info>
 	<id>files_pdfviewer</id>
-	<name>PDF viewer (pdfjs-based)</name>
+	<name>PDF Viewer</name>
+	<description>Inline PDF viewer (pdfjs-based)</description>
 	<version>0.1</version>
 	<licence>GPL</licence>
 	<author>Joan Creus</author>
diff --git a/apps/files_sharing/css/sharing.css b/apps/files_sharing/css/sharing.css
index 0759af2c274425098ff35503bff4a914257d0bca..db59a3d340b608f67ff895c2392a0ee48d2732a1 100644
--- a/apps/files_sharing/css/sharing.css
+++ b/apps/files_sharing/css/sharing.css
@@ -5,4 +5,5 @@
 #shared_list { padding:0.5em; list-style-type: none; }
 #public { border-top:1px solid #ddd; padding-top:0.5em; }
 a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; }
-a.unshare:hover { opacity:1; }
\ No newline at end of file
+a.unshare:hover { opacity:1; }
+#share_with { width: 16em; }
\ No newline at end of file
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index d01a07447a66c81c482f788aabf21d56adbeb4a8..fc9e17c25c7f75e57c64c7f01620eb5148eb5774 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -174,7 +174,7 @@ $(document).ready(function() {
 function createDropdown(filename, files) {
 	var html = '<div id="dropdown" class="drop" data-file="'+files+'">';
 	html += '<div id="private">';
-	html += '<select data-placeholder="User or Group" style="width:220px;" id="share_with" class="chzen-select">';
+	html += '<select data-placeholder="User or Group" id="share_with" class="chzen-select">';
 	html += '<option value=""></option>';
 	html += '</select>';
 	html += '<ul id="shared_list"></ul>';
diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php
index cb641e68a84e38e015b9c2de0d6f897b052756b7..845659588efc325865dda15b5cd5c147ab7e1e53 100644
--- a/apps/files_sharing/sharedstorage.php
+++ b/apps/files_sharing/sharedstorage.php
@@ -58,7 +58,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
 	}
 	
 	public function mkdir($path) {
-		if ($path == "" || $path == "/" || !$this->is_writeable($path)) {
+		if ($path == "" || $path == "/" || !$this->is_writable($path)) {
 			return false; 
 		} else {
 			$source = $this->getSource($path);
@@ -79,7 +79,6 @@ class OC_Filestorage_Shared extends OC_Filestorage {
 		if ($path == "" || $path == "/") {
 			$path = $this->datadir.$path;
 			$sharedItems = OC_Share::getItemsInFolder($path);
-			global $FAKEDIRS;
 			$files = array();
 			foreach ($sharedItems as $item) {
 				// If item is in the root of the shared storage provider and the item exists add it to the fakedirs
@@ -87,7 +86,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
 					$files[] = basename($item['target']);
 				}
 			}
-			$FAKEDIRS['shared'] = $files;
+			OC_FakeDirStream::$dirs['shared']=$files;
 			return opendir('fakedir://shared');
 		} else {
 			$source = $this->getSource($path);
@@ -281,14 +280,6 @@ class OC_Filestorage_Shared extends OC_Filestorage {
 		}
 	}
 	
-	public function readfile($path) {
-		$source = $this->getSource($path);
-		if ($source) {
-			$storage = OC_Filesystem::getStorage($source);
-			return $storage->readfile($this->getInternalPath($source));
-		}	
-	}
-	
 	public function filectime($path) {
 		if ($path == "" || $path == "/") {
 			$ctime = 0; 
@@ -514,7 +505,13 @@ class OC_Filestorage_Shared extends OC_Filestorage {
 			return $storage->getLocalFile($this->getInternalPath($source));
 		}
 	}
-	
+	public function touch($path, $mtime=null){
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->touch($this->getInternalPath($source),$time);
+		}
+	}
 }
 
 ?>
diff --git a/apps/files_texteditor/css/style.css b/apps/files_texteditor/css/style.css
index cf24950232fb6dd12574188deb2ca80213a14579..e260ab0dd46c30c3eaf5b69dc930a78d1b30e6ef 100644
--- a/apps/files_texteditor/css/style.css
+++ b/apps/files_texteditor/css/style.css
@@ -1,8 +1,8 @@
 #editor{
-	position: absoloute;
+	position: fixed;
 	display: block;
-	top: 80px;
-	left: 160px;
+	top: 6.5em;
+	left: 12.5em;
 }
 #editorwrapper{
 	position: absoloute;
diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js
index 7473d5323044c13f1cbe44ad3803415f8643b1c5..e45652b6ef36b46b82e19d00bd9c6ed9fb25fabb 100644
--- a/apps/files_texteditor/js/editor.js
+++ b/apps/files_texteditor/js/editor.js
@@ -172,6 +172,7 @@ function giveEditorFocus(){
 function showFileEditor(dir,filename){
 	if(!editorIsShown()){
 		// Loads the file editor and display it.
+		$('#content').append('<div id="editor"></div>');
 		var data = $.getJSON(
 			OC.filePath('files_texteditor','ajax','loadfile.php'),
 			{file:filename,dir:dir},
@@ -273,10 +274,10 @@ $(document).ready(function(){
 			var dir=text.substr(0,text.length-file.length-1);
 			showFileEditor(dir,file);
 		});
-	}
+	};
 	// Binds the file save and close editor events, and gotoline button
 	bindControlEvents();
-	
+	$('#editor').remove();
 	// Binds the save keyboard shortcut events
 	//$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress);
 });
diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php
index 5ac6d295108be5a395c36c2b8976201fd313d7b4..64f1b1697ba681801e8a09ac5d5246fd0cedc9f4 100644
--- a/apps/gallery/ajax/galleryOp.php
+++ b/apps/gallery/ajax/galleryOp.php
@@ -41,7 +41,7 @@ function handleRemove($name) {
 
 function handleGetThumbnails($albumname) {
   OC_Response::enableCaching(3600 * 24); // 24 hour
-  $thumbnail = OC::$CONFIG_DATADIRECTORY.'/../gallery/'.$albumname.'.png';
+  $thumbnail = OC::$CONFIG_DATADIRECTORY.'/../gallery/'.urldecode($albumname).'.png';
   header('Content-Type: '.OC_Image::getMimeTypeForFile($thumbnail));
   OC_Response::sendFile($thumbnail);
 }
@@ -61,7 +61,7 @@ function handleFilescan($cleanup) {
 
 function handlePartialCreate($path) {
   if (empty($path)) OC_JSON::error(array('cause' => 'No path specified'));
-  if (!OC_Filesystem::is_dir($path)) OC_JSON::error(array('cause' => 'Invalid path given'));
+  if (!OC_Filesystem::is_dir($path.'/')) OC_JSON::error(array('cause' => 'Invalid path given'));
 
   $album = OC_Gallery_Album::find(OC_User::getUser(), null, $path);
   $albums = array();
@@ -87,6 +87,37 @@ function handleStoreSettings($root, $order) {
   OC_JSON::success(array('rescan' => $rescan));
 }
 
+function handleGetGallery($path) {
+  $a = array();
+  $root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/');
+  if (strlen($root) > 1)
+    $path = $root.'/'.trim($path, '/');
+  else
+    $path = '/'.ltrim($path, '/');
+  if (strlen($path) > 1) $path = rtrim($path, '/');
+  $result = OC_Gallery_Album::find(OC_User::getUser(), null, $path);
+  $album_details = $result->fetchRow();
+
+  $result = OC_Gallery_Album::find(OC_User::getUser(), null, null, $path);
+
+  while ($r = $result->fetchRow()) {
+    $album_name = $r['album_name'];
+    $size=OC_Gallery_Album::getAlbumSize($r['album_id']);
+
+    $a[] = array('name' => utf8_encode($album_name), 'numOfItems' => min($size, 10),'path'=>$r['album_path']);
+  }
+  
+  $result = OC_Gallery_Photo::find($album_details['album_id']);
+
+  $p = array();
+
+  while ($r = $result->fetchRow()) {
+    $p[] = utf8_encode($r['file_path']);
+  }
+
+  OC_JSON::success(array('albums'=>$a, 'photos'=>$p));
+}
+
 if ($_GET['operation']) {
   switch($_GET['operation']) {
   case 'rename':
@@ -112,6 +143,12 @@ if ($_GET['operation']) {
   case 'store_settings':
     handleStoreSettings($_GET['root'], $_GET['order']);
     break;
+  case 'get_galleries':
+    handleGetGalleries($_GET['path']);
+    break;
+  case 'get_gallery':
+    handleGetGallery($_GET['path']);
+    break;
   default:
     OC_JSON::error(array('cause' => 'Unknown operation'));
   }
diff --git a/apps/gallery/ajax/getAlbums.php b/apps/gallery/ajax/getAlbums.php
deleted file mode 100644
index 9e9c6ef496c7b6a889db143a0ec1443da6d3341b..0000000000000000000000000000000000000000
--- a/apps/gallery/ajax/getAlbums.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
-* ownCloud - gallery application
-*
-* @author Bartek Przybylski
-* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.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 Lesser General Public 
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-* 
-*/
-
-require_once('../../../lib/base.php');
-OC_JSON::checkLoggedIn();
-OC_JSON::checkAppEnabled('gallery');
-
-$a = array();
-
-$result = OC_Gallery_Album::find(OC_User::getUser());
-
-while ($r = $result->fetchRow()) {
-  $album_name = $r['album_name'];
-  $tmp_res = OC_Gallery_Photo::find($r['album_id']);
-
-  $a[] = array('name' => $album_name, 'numOfItems' => min($tmp_res->numRows(), 10), 'bgPath' => OC::$WEBROOT.'/data/'.OC_User::getUser().'/gallery/'.$album_name.'.png');
-}
-
-OC_JSON::success(array('albums'=>$a));
-
-?>
diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php
index b8de32ea58795826c07f8df397e203791a96e435..1e5e27d408f4e6e0e1a321ac98f545f52c2b8703 100644
--- a/apps/gallery/appinfo/app.php
+++ b/apps/gallery/appinfo/app.php
@@ -40,8 +40,8 @@ OC_App::addNavigationEntry( array(
  'icon' => OC_Helper::imagePath('core', 'places/picture.svg'),
  'name' => $l->t('Gallery')));
 
- class OC_GallerySearchProvider extends OC_Search_Provider{
-	function search($query){
+ class OC_GallerySearchProvider implements OC_Search_Provider{
+	static function search($query){
 		$stmt = OC_DB::prepare('SELECT * FROM *PREFIX*gallery_albums WHERE uid_owner = ? AND album_name LIKE ?');
 		$result = $stmt->execute(array(OC_User::getUser(),'%'.$query.'%'));
 		$results=array();
@@ -52,7 +52,7 @@ OC_App::addNavigationEntry( array(
 	}
 }
 
-new OC_GallerySearchProvider();
+OC_Search::registerProvider('OC_GallerySearchProvider');
 
 require_once('apps/gallery/lib/hooks_handlers.php');
 ?>
diff --git a/apps/gallery/appinfo/database.xml b/apps/gallery/appinfo/database.xml
index db88e4c1b5a875d0130894a16643806db423b1a4..62fdbee9cd872bc6dadfa87457a4fef0056e1671 100644
--- a/apps/gallery/appinfo/database.xml
+++ b/apps/gallery/appinfo/database.xml
@@ -11,9 +11,9 @@
 				<name>album_id</name>
 				<type>integer</type>
 				<default>0</default>
-			    <notnull>true</notnull>
-    			<autoincrement>1</autoincrement>
-			    <length>4</length>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
 			</field>
 			<field>
 				<name>uid_owner</name>
@@ -27,12 +27,18 @@
 				<notnull>true</notnull>
 				<length>100</length>
 			</field>
-      <field>
-        <name>album_path</name>
-        <type>text</type>
-        <notnull>true</notnull>
-        <length>100</length>
-      </field>
+			<field>
+				<name>album_path</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>100</length>
+			</field>
+			<field>
+				<name>parent_path</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>100</length>
+			</field>
 		</declaration>
 	</table>
 	<table>
@@ -42,16 +48,16 @@
 				<name>photo_id</name>
 				<type>integer</type>
 				<default>0</default>
-			    <notnull>true</notnull>
-			    <autoincrement>1</autoincrement>
-			    <length>4</length>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
 			</field>
 			<field>
 				<name>album_id</name>
 				<type>integer</type>
 				<default>0</default>
-			    <notnull>true</notnull>
-			    <length>4</length>
+				<notnull>true</notnull>
+				<length>4</length>
 			</field>
 			<field>
 				<name>file_path</name>
diff --git a/apps/gallery/appinfo/info.xml b/apps/gallery/appinfo/info.xml
index 9aecb0c781d2f5acfe868c3ce3ea8bd09668da10..19c5dc8b25eddd4e326b17e179faf7e21a7104a6 100644
--- a/apps/gallery/appinfo/info.xml
+++ b/apps/gallery/appinfo/info.xml
@@ -2,7 +2,7 @@
 <info>
 	<id>gallery</id>
 	<name>Gallery</name>
-	<version>0.3</version>
+	<version>0.4</version>
 	<licence>AGPL</licence>
 	<author>Bartek Przybylski</author>
 	<require>2</require>
diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css
index c039cd5ec02b47a20978248b5f15c44a1dad46a6..013cd1b262dc16e2e2aee50200402798161fca7d 100644
--- a/apps/gallery/css/styles.css
+++ b/apps/gallery/css/styles.css
@@ -1,13 +1,14 @@
-div#gallery_list { margin: 70pt 20pt 0 20pt; }
+div#gallery_list { margin: 4.5em 2em 0 2em; }
 div#gallery_list.leftcontent { padding-top: 15pt; margin: 0; position: absolute; bottom:0px; text-align: center; overflow: auto; }
-div.gallery_album_box { width: 200px; position:relative; text-align: center; border: 0; display: inline-block; margin: 5pt; vertical-align: top; padding: 20px 5px 5px 5px; position: relative; -webkit-transition: color 0.5s ease-in-out; -o-transition: color 0.5s ease-in-out; -moz-transition: color 0.5s ease-in-out;color: #BBB;}
-div.gallery_album_box h1 { font-size: 9pt; font-family: Verdana; }
-div.gallery_album_decoration { width: 200px; position: absolute; border: 0; height: 20px; top: 20px; text-align:right; vertical-align:middle; background-color: #eee; opacity: 0; -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; border-bottom-right-radius: 7px; border-bottom-left-radius: 7px; -moz-border-radius-bottomright: 7px; -moz-border-radius-bottomleft:7px;}
-div.gallery_album_box:hover { color: black; }
-div.gallery_album_box:hover div.gallery_album_decoration { opacity: 0.7;}
+div.gallery_box { width: 200px; position:relative; text-align: center; border: 0; display: inline-block; margin: 5pt; vertical-align: top; padding: 5px 5px 5px 5px; position: relative; -webkit-transition: color 0.5s ease-in-out; -o-transition: color 0.5s ease-in-out; -moz-transition: color 0.5s ease-in-out;color: #BBB;}
+div.album {border: 1px solid #e0e0e0; border-radius: 7px;}
+div.gallery_box h1 { font-size: 9pt; font-family: Verdana; }
+div.gallery_album_decoration { width: 200px; position: absolute; border: 0; height: 20px; top: 5px; text-align:right; vertical-align:middle; background-color: #eee; opacity: 0; -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; }
+div.gallery_box:hover { color: black; }
+div.gallery_box:hover div.gallery_album_decoration { opacity: 0.7;}
 div.gallery_album_decoration a {padding: 0 4pt; cursor: pointer;}
 div.gallery_album_cover { width: 200px; height: 200px; border: 0; padding: 0; position:relative;}
-div.gallery_album_box:hover div.gallery_control_overlay { opacity:0.5 }
+div.gallery_box:hover div.gallery_control_overlay { opacity:0.5 }
 div.gallery_control_overlay a { color:white; }
 #gallery_images.rightcontent { padding:10px 5px; bottom: 0px; overflow: auto; right:0px}
 #scan { position:absolute; right:13.5em; top:0em; }
diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js
index 4ddac2f211131c379a50c6a095a554c128ee7881..71de1adae11b242d8cf8965776aea216020d5eb1 100644
--- a/apps/gallery/js/album_cover.js
+++ b/apps/gallery/js/album_cover.js
@@ -1,190 +1,187 @@
 var actual_cover;
-$(document).ready(function() {
-  $.getJSON('ajax/getAlbums.php', function(r) {
-    if (r.status == 'success') {
-      for (var i in r.albums) {
-        var a = r.albums[i];
-        Albums.add(a.name, a.numOfItems);
-      }
-      var targetDiv = document.getElementById('gallery_list');
-      if (targetDiv) {
-        $(targetDiv).html('');
-        Albums.display(targetDiv);
-        $('#gallery_list').sortable({revert:true});
-        $('.gallery_album_box').each(function(i, e) {
-          $(e).draggable({connectToSortable: '#gallery_list', handle: '.dummy'})
-        });
-      } else {
-        alert('Error occured: no such layer `gallery_list`');
-      }
-    } else {
-      alert('Error occured: ' + r.message);
-    }
-  });
-});
+var paths = [];
+var crumbCount = 0;
+$(document).ready(returnToElement(0));
+
+function returnToElement(num) {
+	while (crumbCount != num) {
+		$('#g-album-navigation .last').remove();
+		$('#g-album-navigation .crumb :last').parent().addClass('last');
+		crumbCount--;
+		paths.pop();
+	}
+	path=paths[paths.length-1];
+	$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'get_gallery', path: path }, albumClickHandler);
+}
+
+function albumClick(title,path) {
+	paths.push(path);
+	crumbCount++;
+	$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'get_gallery', path: path }, albumClickHandler);
+	if ($('#g-album-navigation :last-child'))
+		$('#g-album-navigation :last-child').removeClass('last');
+	$('#g-album-navigation').append('<div class="crumb last real" style="background-image:url(\''+OC.imagePath('core','breadcrumb')+'\')"><a href=\"javascript:returnToElement('+crumbCount+');\">'+title+'</a></div>');
+}
+
+function albumClickHandler(r) {
+	Albums.photos = [];
+	Albums.albums = [];
+	if (r.status == 'success') {
+		for (var i in r.albums) {
+		var a = r.albums[i];
+		Albums.add(a.name, a.numOfItems,a.path);
+		}
+		for (var i in r.photos) {
+		Albums.photos.push(r.photos[i]);
+		}
+		var targetDiv = document.getElementById('gallery_list');
+		if (targetDiv) {
+			$(targetDiv).html('');
+			Albums.display(targetDiv);
+			//$('#gallery_list').sortable({revert:true});
+			$('.album').each(function(i, el) {
+				$(el).click(albumClick.bind(null,$(el).attr('title'),$(el).data('path')));
+				//$(el).draggable({connectToSortable: '#gallery_list', handle: '.dummy'});
+			});
+		} else {
+			OC.dialogs.alert(t('gallery', 'Error: no such layer `gallery_list`'), t('gallery', 'Internal error'));
+		}
+	} else {
+		OC.dialogs.alert(t('gallery', 'Error: ') + r.message, t('gallery', 'Internal error'));
+	}
+}
 
 function createNewAlbum() {
-  var name = prompt("album name", "");
-  if (name != null && name != "") {
-    $.getJSON("ajax/createAlbum.php", {album_name: name}, function(r) {
-      if (r.status == "success") {
-        var v = '<div class="gallery_album_box"><a href="?view='+r.name+'"><img class="gallery_album_cover"/></a><h1>'+r.name+'</h1></div>';
-        $('div#gallery_list').append(v);
-      }
-    });
-  }
+	var name = prompt("album name", "");
+	if (name != null && name != "") {
+		$.getJSON(OC.filePath('gallery','ajax','createAlbum.php'), {album_name: name}, function(r) {
+			if (r.status == "success") {
+				var v = '<div class="gallery_box"><a href="?view='+r.name+'"><img class="gallery_album_cover"/></a><h1>'+r.name+'</h1></div>';
+				$('div#gallery_list').append(v);
+			}
+		});
+	}
 }
 
 var albumCounter = 0;
 var totalAlbums = 0;
 
 function scanForAlbums(cleanup) {
-  cleanup = cleanup?true:false;
-  var albumCounter = 0;
-  var totalAlbums = 0;
-  $('#g-scan-button').attr('disabled', 'true');
-  $.getJSON('ajax/galleryOp.php?operation=filescan', {cleanup: cleanup}, function(r) {
-
-    if (r.status == 'success') {
-      totalAlbums = r.paths.length;
-      if (totalAlbums == 0) {
-        $('#notification').text(t('gallery', "No photos found")).fadeIn().slideDown().delay(3000).fadeOut().slideUp();
-        return;
-      }
-      $('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 }).fadeIn();
-      for(var a in r.paths) {
-        $.getJSON('ajax/galleryOp.php?operation=partial_create&path='+r.paths[a], function(r) {
+	cleanup = cleanup?true:false;
+	var albumCounter = 0;
+	var totalAlbums = 0;
+	$('#g-scan-button').attr('disabled', 'true');
+	$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {cleanup: cleanup,operation:'filescan'}, function(r) {
 
-          if (r.status == 'success') {
-            Albums.add(r.album_details.albumName, r.album_details.imagesCount);
-          }
+		if (r.status == 'success') {
+			totalAlbums = r.paths.length;
+			if (totalAlbums == 0) {
+				$('#notification').text(t('gallery', "No photos found")).fadeIn().slideDown().delay(3000).fadeOut().slideUp();
+				return;
+			}
+			$('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 }).fadeIn();
+			for(var a in r.paths) {
+				$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'),{operation:'partial_create','path':r.paths[a]}, function(r) {
 
-          albumCounter++;
-          $('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 });
-          if (albumCounter == totalAlbums) {
-            $('#scanprogressbar').fadeOut();
-            var targetDiv = document.getElementById('gallery_list');
-            if (targetDiv) {
-              targetDiv.innerHTML = '';
-              Albums.display(targetDiv);
-            } else {
-              alert('Error occured: no such layer `gallery_list`');
-            }
-            $('#g-scan-button').attr('disabled', null);
-          }
-        });
-      }
-    } else {
-      alert('Error occured: ' + r.message);
-    }
-  });
+				albumCounter++;
+				$('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 });
+				if (albumCounter == totalAlbums) {
+					$('#scanprogressbar').fadeOut();
+					var targetDiv = document.getElementById('gallery_list');
+					if (targetDiv) {
+						targetDiv.innerHTML = '';
+						Albums.photos = [];
+						Albums.albums = [];
+						returnToElement(0);
+					} else {
+						alert('Error occured: no such layer `gallery_list`');
+					}
+					$('#g-scan-button').attr('disabled', null);
+				}
+				});
+			}
+		} else {
+			alert('Error occured: ' + r.message);
+		}
+	});
 }
 
 function galleryRemove(albumName) {
-  // a workaround for a flaw in the demo system (http://dev.jqueryui.com/ticket/4375), ignore!
-  $( "#dialog:ui-dialog" ).dialog( "destroy" );
-  $('#albumName', $("#dialog-confirm")).text(albumName);
-
-  $( '#dialog-confirm' ).dialog({
-    resizable: false,
-    height:150,
-    buttons: [{
-        text: t('gallery', 'OK'),
-        click: function() {
-          $.getJSON("ajax/galleryOp.php", {operation: "remove", name: albumName}, function(r) {
-            if (r.status == "success") {
-            $(".gallery_album_box").filterAttr('data-album',albumName).remove();
-              Albums.remove(albumName);
-            } else {
-              alert("Error: " + r.cause);
-            }
-            $('#dialog-confirm').dialog('close');
-          });
-        }},
-        {
-          text: t('gallery', 'Cancel'),
-          click: function() {
-            $( this ).dialog( 'close' );
-        }}]
-  });
+	OC.dialogs.confirm(t('gallery', 'Do you want to remove album ') + decodeURIComponent(escape(albumName)),
+		t('gallery', 'Remove confirmation'),
+		function(decision) {
+			if (decision) {
+				$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: "remove", name: decodeURIComponent(escape(albumName))}, function(r) {
+					if (r.status == "success") {
+						$(".gallery_box").filterAttr('data-album',albumName).remove();
+						Albums.remove(albumName);
+					} else {
+						OC.dialogs.alert(r.cause, "Error");
+					}
+				});
+			}
+		}
+	);
 }
 
 function galleryRename(name) {
-  $('#name', $('#dialog-form')).val(name);
-  $( "#dialog-form" ).dialog({
-        height: 140,
-        width: 350,
-        modal: false,
-        buttons: [{
-            text: t('gallery', 'Change name'),
-            click: function() {
-              var newname = $('#name', $('#dialog-form')).val();
-              if (newname == name || newname == '') {
-                $(this).dialog("close");
-                return;
-              }
-              if (Albums.find(newname)) {
-                alert("Album ", newname, " exists");
-                $(this).dialog("close");
-                return;
-              }
-              $.getJSON('ajax/galleryOp.php', {operation: 'rename', oldname: name, newname: newname}, function(r) {
-                if (r.status == "success") {
-                  Albums.rename($(".gallery_album_box").filterAttr('data-album',name), newname);
-                } else {
-                  alert("Error: " + r.cause);
-                }
-                $('#dialog-form').dialog('close');
-              });
-
-            }
-          },
-          {
-            text: t('gallery', 'Cancel'),
-            click: function() {
-              $( this ).dialog('close');
-            }
-          }
-        ],
-  });
+	OC.dialogs.prompt(t('gallery', 'New album name'),
+		t('gallery', 'Change name'),
+		name,
+		function(newname) {
+			if (newname == name || newname == '') return;
+			if (Albums.find(newname)) {
+				OC.dialogs.alert('Album ' + newname + ' exists', 'Alert');
+				return;
+			}
+			$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'rename', oldname: name, newname: newname}, function(r) {
+				if (r.status == 'success') {
+					Albums.rename($(".gallery_box").filterAttr('data-album',name), newname);
+				} else {
+					OC.dialogs.alert('Error: ' + r.cause, 'Error');
+				}
+			});
+		}
+	);
 }
 
 function settings() {
-  $( '#g-dialog-settings' ).dialog({
-        height: 180,
-        width: 350,
-        modal: false,
-        buttons: [{
-            text: t('gallery', 'Apply'),
-            click: function() {
-              var scanning_root = $('#g-scanning-root').val();
-              var disp_order = $('#g-display-order option:selected').val();
-              if (scanning_root == '') {
-                alert('Scanning root cannot be empty');
-                return;
-              }
-              $.getJSON('ajax/galleryOp.php', {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) {
-                if (r.status == 'success') {
-                  if (r.rescan == 'yes') { 
-                    $('#g-dialog-settings').dialog('close');
-                    Albums.clear(document.getElementById('gallery_list'));
-                    scanForAlbums(true);
-                    return;
-                  }
-                } else {
-                  alert('Error: ' + r.cause);
-                  return;
-                }
-                $('#g-dialog-settings').dialog('close');
-              });
-            }
-          },
-          {
-            text: t('gallery', 'Cancel'),
-            click: function() {
-              $(this).dialog('close');
-            }
-          }
-        ],
-  });
+	$( '#g-dialog-settings' ).dialog({
+		height: 180,
+		width: 350,
+		modal: false,
+		buttons: [
+			{
+				text: t('gallery', 'Apply'),
+				click: function() {
+					var scanning_root = $('#g-scanning-root').val();
+					var disp_order = $('#g-display-order option:selected').val();
+					if (scanning_root == '') {
+						alert('Scanning root cannot be empty');
+						return;
+					}
+					$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) {
+						if (r.status == 'success') {
+						if (r.rescan == 'yes') {
+							$('#g-dialog-settings').dialog('close');
+							Albums.clear(document.getElementById('gallery_list'));
+							scanForAlbums(true);
+							return;
+						}
+						} else {
+						alert('Error: ' + r.cause);
+						return;
+						}
+						$('#g-dialog-settings').dialog('close');
+					});
+				}
+			},
+			{
+				text: t('gallery', 'Cancel'),
+				click: function() {
+				$(this).dialog('close');
+				}
+			}
+		],
+	});
 }
diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js
index 987412f28e0f0dbe2203bc845a5644ff46d3826f..afdfbd3cc83dd26fbce49b00f2aba12eaf68d260 100644
--- a/apps/gallery/js/albums.js
+++ b/apps/gallery/js/albums.js
@@ -1,89 +1,98 @@
 Albums={
-  // album item in this array should look as follow
-  // {name: string,
-  //  numOfCovers: int}
-  //
-  // previews array should be an array of base64 decoded images
-  // to display to user as preview picture when scrolling throught
-  // the album cover
-  albums:new Array(),
-  // add simply adds new album to internal structure
-  // however albums names must be unique so other
-  // album with the same name wont be insered,
-  // and false will be returned
-  // true on success
-  add: function(album_name, num) {
-    if (Albums.albums[album_name] != undefined) return false;
-    Albums.albums[album_name] = {name: album_name, numOfCovers: num};
-    return true;
-  },
-  // remove element with given name
-  // returns remove element or undefined if no such element was present
-  remove: function(name) {
-    var i = -1, tmp = 0;
-    for (var a in Albums.albums) {
-      if (a.name == name) {
-        i = tmp;
-        break;
-      }
-      tmp++;
-    }
-    if (i != -1) {
-      return Albums.albums.splice(i,1);
-    }
-    return undefined;
-  },
-  // return element which match given name
-  // of undefined if such element do not exist
-  find: function(name) {
-    return Albums.albums[name];
-  },
-  // displays gallery in linear representation
-  // on given element, and apply default styles for gallery
-  display: function(element) {
-    var displayTemplate = '<div class="gallery_album_box"><div class="dummy"></div><a class="view"><div class="gallery_album_cover"></div></a><h1></h1><div class="gallery_album_decoration"><a><img src="img/share.png" title="Share"></a><a class="rename"><img src="img/rename.png" title="Rename"></a><a class="remove"><img src="img/delete.png" title="Delete"></a></div></div>';
-    for (var i in Albums.albums) {
-      var a = Albums.albums[i];
-	  var local=$(displayTemplate);
-	  local.attr('data-album',a.name);
-	  $(".gallery_album_decoration a.rename", local).bind('click', {name: a.name},function(event){
-			event.preventDefault();
-			galleryRename(event.data.name);
-    });
-	  $(".gallery_album_decoration a.remove", local).bind('click', {name: a.name},function(event){
-		  event.preventDefault();
-		  galleryRemove(event.data.name);
-    });
-    $("a.view", local).attr('href','?view='+escape(a.name));
-	  $('h1',local).text(a.name);
-	  $(".gallery_album_cover", local).attr('title',a.name);
-      $(".gallery_album_cover", local).css('background-repeat', 'no-repeat');
-      $(".gallery_album_cover", local).css('background-position', '0');
-      $(".gallery_album_cover", local).css('background-image','url("ajax/galleryOp.php?operation=get_covers&albumname='+escape(a.name)+'")');
-      $(".gallery_album_cover", local).mousemove(function(e) {
-
-        var albumMetadata = Albums.find(this.title);
-        if (albumMetadata == undefined) {
-          return;
-        }
-        var x = Math.floor((e.layerX - this.offsetLeft)/(this.offsetWidth/albumMetadata.numOfCovers));
-        x *= this.offsetWidth;
-        if (x < 0) x=0;
-        $(this).css('background-position', -x+'px 0');
-      });
-      $(element).append(local);
-    }
-  },
-  rename: function(element, new_name) {
-    if (new_name) {
-		$(element).attr("data-album", new_name);
-		$("a.view", element).attr("href", "?view="+new_name);
-		$("h1", element).text(new_name);
+	// album item in this array should look as follow
+	// {name: string,
+	//  numOfCovers: int}
+	//
+	// previews array should be an array of base64 decoded images
+	// to display to user as preview picture when scrolling throught
+	// the album cover
+	albums:new Array(),
+	photos:new Array(),
+	// add simply adds new album to internal structure
+	// however albums names must be unique so other
+	// album with the same name wont be insered,
+	// and false will be returned
+	// true on success
+	add: function(album_name, num,path) {
+		if (Albums.albums[album_name] != undefined) return false;
+		Albums.albums[album_name] = {name: album_name, numOfCovers: num, path:path};
+		return true;
+	},
+	// remove element with given name
+	// returns remove element or undefined if no such element was present
+	remove: function(name) {
+		var i = -1, tmp = 0;
+		for (var a in Albums.albums) {
+		if (a.name == name) {
+			i = tmp;
+			break;
+		}
+		tmp++;
+		}
+		if (i != -1) {
+		return Albums.albums.splice(i,1);
+		}
+		return undefined;
+	},
+	// return element which match given name
+	// of undefined if such element do not exist
+	find: function(name) {
+		return Albums.albums[name];
+	},
+	// displays gallery in linear representation
+	// on given element, and apply default styles for gallery
+	display: function(element) {
+		var displayTemplate = '<div class="gallery_box album"><div class="dummy"></div><a class="view"><div class="gallery_album_cover"></div></a><h1></h1><div class="gallery_album_decoration"><a><img src="img/share.png" title="Share"></a><a class="rename"><img src="img/rename.png" title="Rename"></a><a class="remove"><img src="img/delete.png" title="Delete"></a></div></div>';
+		for (var i in Albums.albums) {
+			var a = Albums.albums[i];
+			var local=$(displayTemplate);
+			local.attr('title', a.name);
+			local.attr('data-path', a.path);
+			local.attr('data-album',a.name);
+			$(".gallery_album_decoration a.rename", local).bind('click', {name: a.name},function(name,event){
+				event.preventDefault();
+				event.stopPropagation();
+				galleryRename(name);
+			}.bind(null,a.name));
+			$(".gallery_album_decoration a.remove", local).bind('click', {name: a.name},function(name,event){
+				event.preventDefault();
+				event.stopPropagation();
+				galleryRemove(name);
+			}.bind(null,a.name));
+			$('h1',local).text(decodeURIComponent(escape(a.name)));
+			$(".gallery_album_cover", local).attr('title',decodeURIComponent(escape(a.name)));
+			$(".gallery_album_cover", local).css('background-repeat', 'no-repeat');
+			$(".gallery_album_cover", local).css('background-position', '0');
+			$(".gallery_album_cover", local).css('background-image','url("'+OC.filePath('gallery','ajax','galleryOp.php')+'?operation=get_covers&albumname='+escape(a.name)+'")');
+			$(".gallery_album_cover", local).mousemove(function(e) {
+				var albumMetadata = Albums.find(this.title);
+				if (albumMetadata == undefined) {
+					return;
+				}
+				var x = Math.floor((e.layerX - this.offsetLeft)/(this.offsetWidth/albumMetadata.numOfCovers));
+				x *= this.offsetWidth;
+				if (x < 0) x=0;
+				$(this).css('background-position', -x+'px 0');
+			});
+			$(element).append(local);
+		}
+		var photoDisplayTemplate = '<div class="gallery_box"><div class="dummy"></div><div><a rel="images" href="'+OC.linkTo('files','download.php')+'?file=URLPATH"><img src="'+OC.filePath('gallery','ajax','thumbnail.php')+'?img=IMGPATH"></a></div></div>';
+		for (var i in Albums.photos) {
+			$(element).append(photoDisplayTemplate.replace("IMGPATH", escape(Albums.photos[i])).replace("URLPATH", escape(Albums.photos[i])));
+		}
+		$("a[rel=images]").fancybox({
+			'titlePosition': 'inside'
+		});
+	},
+	rename: function(element, new_name) {
+		if (new_name) {
+			$(element).attr("data-album", new_name);
+			$("a.view", element).attr("href", "?view="+new_name);
+			$("h1", element).text(new_name);
+		}
+	},
+	clear: function(element) {
+		Albums.albums = new Array();
+		element.innerHTML = '';
 	}
-  },
-  clear: function(element) {
-    Albums.albums = new Array();
-    element.innerHTML = '';
-  }
-
 }
diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php
index d1405333ac775d3b396adac93c33be0150ce5aab..5adb57b554dfedb1eb1e1c5203c1a02d769f09b6 100644
--- a/apps/gallery/lib/album.php
+++ b/apps/gallery/lib/album.php
@@ -23,10 +23,11 @@
 
 class OC_Gallery_Album {
 	public static function create($owner, $name, $path){
-		$stmt = OC_DB::prepare('INSERT INTO *PREFIX*gallery_albums (uid_owner, album_name, album_path) VALUES (?, ?, ?)');
-		$stmt->execute(array($owner, $name, $path));
+    $id = self::getParentPath($path);
+		$stmt = OC_DB::prepare('INSERT INTO *PREFIX*gallery_albums (uid_owner, album_name, album_path, parent_path) VALUES (?, ?, ?, ?)');
+		$stmt->execute(array($owner, $name, $path, $id));
 	}
-	
+
 	public static function rename($oldname, $newname, $owner) {
 	    $stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_albums SET album_name=? WHERE uid_owner=? AND album_name=?');
 		$stmt->execute(array($newname, $owner, $oldname));
@@ -39,7 +40,14 @@ class OC_Gallery_Album {
       self::remove(OC_User::getUser(), $r['album_name']);
     }
   }
-	
+
+  public static function getParentPath($path) {
+    if (strlen($path)==1) return '';
+    $path = substr($path, 0, strrpos($path, '/'));
+    if ($path == '') $path = '/';
+    return $path;
+  }
+
 	public static function remove($owner, $name=null) {
 		$sql = 'DELETE FROM *PREFIX*gallery_albums WHERE uid_owner = ?';
 		$args = array($owner);
@@ -66,7 +74,7 @@ class OC_Gallery_Album {
     }
   }
 	
-  public static function find($owner, $name=null, $path=null){
+  public static function find($owner, $name=null, $path=null, $parent=null){
 		$sql = 'SELECT * FROM *PREFIX*gallery_albums WHERE uid_owner = ?';
 		$args = array($owner);
 		if (!is_null($name)){
@@ -77,6 +85,10 @@ class OC_Gallery_Album {
       $sql .= ' AND album_path = ?';
       $args[] = $path;
     }
+    if (!is_null($parent)){
+      $sql .= ' AND parent_path = ?';
+      $args[] = $parent;
+    }
     $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');
     $sql .= ' ORDER BY album_name ' . $order;
 
@@ -95,6 +107,13 @@ class OC_Gallery_Album {
     rename($thumbpath.$oldname.'.png', $thumbpath.$newname.'.png');
   }
 
+  public static function getAlbumSize($id){
+	$sql = 'SELECT COUNT(*) as size FROM *PREFIX*gallery_photos WHERE album_id = ?';
+	$stmt = OC_DB::prepare($sql);
+	$result=$stmt->execute(array($id))->fetchRow();
+	return $result['size'];
+  }
+
 }
 
 ?>
diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php
index 046866e5c5dbc2ab853b8842c9750c88b78899e5..3c101b1f8a1c07f83c3628c8fb49682cb5fa9ad4 100644
--- a/apps/gallery/lib/hooks_handlers.php
+++ b/apps/gallery/lib/hooks_handlers.php
@@ -37,7 +37,7 @@ class OC_Gallery_Hooks_Handlers {
   }
 
   private static function directoryContainsPhotos($dirpath) {
-    $dirhandle = opendir(OC::$CONFIG_DATADIRECTORY.$dirpath);
+    $dirhandle = OC_Filesystem::opendir($dirpath.'/');
     if ($dirhandle != FALSE) {
       while (($filename = readdir($dirhandle)) != FALSE) {
         if ($filename[0] == '.') continue;
@@ -76,9 +76,9 @@ class OC_Gallery_Hooks_Handlers {
 
   public static function removePhoto($params) {
     $path = $params[OC_Filesystem::signal_param_path];
-    if (OC_Filesystem::is_dir($path) && self::directoryContainsPhotos($path)) {
+    if (OC_Filesystem::is_dir($path.'/') && self::directoryContainsPhotos($path)) {
       if(!self::pathInRoot($path)) return;
-      OC_Gallery_Album::removeByPath($path.'/', OC_User::getUser());
+      OC_Gallery_Album::removeByPath($path, OC_User::getUser());
     } elseif (self::isPhoto($path)) {
       OC_Gallery_Photo::removeByPath($path);
     }
@@ -87,11 +87,11 @@ class OC_Gallery_Hooks_Handlers {
   public static function renamePhoto($params) {
     $oldpath = $params[OC_Filesystem::signal_param_oldpath];
     $newpath = $params[OC_Filesystem::signal_param_newpath];
-    if (OC_Filesystem::is_dir($newpath) && self::directoryContainsPhotos($newpath)) {
+    if (OC_Filesystem::is_dir($newpath.'/') && self::directoryContainsPhotos($newpath)) {
       OC_Gallery_Album::changePath($oldpath, $newpath, OC_User::getUser());
-    } elseif (!self::isPhoto($newpath)) {
-      $olddir = substr($oldpath, 0, strrpos($oldpath, '/'));
-      $newdir = substr($newpath, 0, strrpos($newpath, '/'));
+    } elseif (self::isPhoto($newpath)) {
+      $olddir = dirname($oldpath);
+      $newdir = dirname($newpath);
       if ($olddir == '') $olddir = '/';
       if ($newdir == '') $newdir = '/';
       if (!self::isPhoto($newpath)) return;
@@ -101,25 +101,26 @@ class OC_Gallery_Hooks_Handlers {
       $oldAlbumId;
       if ($olddir == $newdir) {
         // album changing is not needed
-        $album = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir);
-        if ($album->numRows() == 0) {
-          $album = self::createAlbum($newdir);
+        $albums = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir);
+        $album = $albums->fetchRow();
+        if (!$album) {
+          $albums = self::createAlbum($newdir);
+          $album = $albums->fetchRow();
         }
-        $album = $album->fetchRow();
         $newAlbumId = $oldAlbumId = $album['album_id'];
       } else {
         $newalbum = OC_Gallery_Album::find(OC_User::getUser(), null, $newdir);
         $oldalbum = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir);
 
-        if ($newalbum->numRows() == 0) {
+        if (!($newalbum = $newalbum->fetchRow())) {
           $newalbum = self::createAlbum($newdir);
+          $newalbum = $newalbum->fetchRow();
         }
-        $newalbum = $newalbum->fetchRow();
-        if ($oldalbum->numRows() == 0) {
+        $oldalbum = $oldalbum->fetchRow();
+        if (!$oldalbum) {
           OC_Gallery_Photo::create($newalbum['album_id'], $newpath);
           return;
         }
-        $oldalbum = $oldalbum->fetchRow();
         $newAlbumId = $newalbum['album_id'];
         $oldAlbumId = $oldalbum['album_id'];
 
diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php
index 4eb313bfc334cb98038cad2642331a5a05b81eca..872ecc9488afa7f15b30993c70c776f10d4878d3 100644
--- a/apps/gallery/lib/photo.php
+++ b/apps/gallery/lib/photo.php
@@ -69,7 +69,6 @@ class OC_Gallery_Photo {
 	public static function getThumbnail($image_name) {
 		$save_dir = OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/';
 		$save_dir .= dirname($image_name). '/';
-		$image_name = basename($image_name);
 		$thumb_file = $save_dir . $image_name;
 		if (file_exists($thumb_file)) {
 			$image = new OC_Image($thumb_file);
diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php
index 64efb006ad191bcef3a1977010258550295a67ce..dbe1abff10e95141c39ad98839097f0835af1669 100644
--- a/apps/gallery/lib/scanner.php
+++ b/apps/gallery/lib/scanner.php
@@ -21,7 +21,7 @@
 * 
 */
 
-require_once('base.php'); // base lib
+require_once('base.php');
 require_once('images_utils.php');
 
 class OC_Gallery_Scanner {
@@ -40,20 +40,14 @@ class OC_Gallery_Scanner {
   }
 
   public static function createName($name) {
-    $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/');
-    $name = str_replace('/', '.', str_replace(OC::$CONFIG_DATADIRECTORY, '', $name));
-    if (substr($name, 0, strlen($root)) == str_replace('/','.',$root)) {
-      $name = substr($name, strlen($root));
-    }
-    $name = ($name==='.') ? 'main' : trim($name,'.');
-    return $name;
+    return basename($name);
   }
 
   public static function scanDir($path, &$albums) {
     $current_album = array('name'=> $path, 'imagesCount' => 0, 'images' => array());
     $current_album['name'] = self::createName($current_album['name']);
 
-    if ($dh = OC_Filesystem::opendir($path)) {
+    if ($dh = OC_Filesystem::opendir($path.'/')) {
       while (($filename = readdir($dh)) !== false) {
         $filepath = ($path[strlen($path)-1]=='/'?$path:$path.'/').$filename;
         if (substr($filename, 0, 1) == '.') continue;
@@ -64,19 +58,19 @@ class OC_Gallery_Scanner {
     }
     $current_album['imagesCount'] = count($current_album['images']);
     $albums['imagesCount'] = $current_album['imagesCount'];
-    $albums['albumName'] = $current_album['name'];
+    $albums['albumName'] = utf8_encode($current_album['name']);
 
     $result = OC_Gallery_Album::find(OC_User::getUser(), /*$current_album['name']*/ null, $path);
     // don't duplicate galleries with same path (bug oc-33)
-    if ($result->numRows() == 0 && count($current_album['images'])) {
-      OC_Gallery_Album::create(OC_User::getUser(), $current_album['name'], $path);
+    if (!($albumId = $result->fetchRow()) && count($current_album['images'])) {
+        OC_Gallery_Album::create(OC_User::getUser(), $current_album['name'], $path);
 	    $result = OC_Gallery_Album::find(OC_User::getUser(), $current_album['name']);
+	    $albumId = $result->fetchRow();
     }
-    $albumId = $result->fetchRow();
     $albumId = $albumId['album_id'];
     foreach ($current_album['images'] as $img) {
       $result = OC_Gallery_Photo::find($albumId, $img);
-      if ($result->numRows() == 0) {
+      if (!$result->fetchRow()) {
 	      OC_Gallery_Photo::create($albumId, $img);
       }
     }
@@ -103,21 +97,15 @@ class OC_Gallery_Scanner {
   }
 
   public static function find_paths($path) {
-    $ret = array();
-    $dirres;
-    $addpath = FALSE;
-    if (($dirres = OC_Filesystem::opendir($path)) == FALSE) return $ret;
-
-    while (($file = readdir($dirres)) != FALSE) {
-      if ($file[0] == '.') continue;
-      if (OC_Filesystem::is_dir($path.$file))
-        $ret = array_merge($ret, self::find_paths($path.$file.'/'));
-      if (self::isPhoto($path.$file)) $addpath = TRUE;
-    }
-
-    if ($addpath) $ret[] = urlencode($path);
-
-    return $ret;
+	$images=OC_FileCache::searchByMime('image');
+	$paths=array();
+	foreach($images as $image){
+		$path=dirname($image);
+		if(array_search($path,$paths)===false){
+			$paths[]=$path;
+		}
+	}
+	return $paths;
   }
 }
 ?>
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index 7cc7dad3ac683720520644e7a739f4397708712c..7f0281e6a39d0545cc4bb3a8a59e6055892e231a 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -2,18 +2,24 @@
 OC_Util::addStyle('gallery', 'styles');
 OC_Util::addScript('gallery', 'albums');
 OC_Util::addScript('gallery', 'album_cover');
+OC_Util::addStyle('files', 'files');
+OC_Util::addScript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack');
+OC_Util::addScript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
+OC_Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
 $l = new OC_L10N('gallery');
 ?>
 
-<div id="notification"><div id="gallery_notification_text">Creating thumbnails</div></div>
 <div id="controls">
   <div id="scan">
     <div id="scanprogressbar"></div>
     <input type="button" id="g-scan-button" value="<?php echo $l->t('Rescan');?>" onclick="javascript:scanForAlbums();" />
-  </div>
-  <div id="g-settings">
     <input type="button" id="g-settings-button" value="<?php echo $l->t('Settings');?>" onclick="javascript:settings();"/>
   </div>
+  <div id="g-album-navigation">
+    <div class="crumb last" style="background-image:url('/owncloud/core/img/breadcrumb.png')">
+      <a href="javascript:returnToElement(0);">main</a>
+    </div>
+  </div>
 </div>
 <div id="gallery_list">
 </div>
diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php
index bb4502690b5cb54d8ba560feca79e5dfcaab506f..9d9c14deb1705d02977e0b92d9f04a3314fba2ca 100644
--- a/apps/media/ajax/api.php
+++ b/apps/media/ajax/api.php
@@ -120,7 +120,10 @@ if($arguments['action']){
 			OC_Filesystem::readfile($arguments['path']);
 			exit;
 		case 'find_music':
-			OC_JSON::encodedPrint(OC_FileCache::searchByMime('audio'));
+			$music=OC_FileCache::searchByMime('audio');
+			$ogg=OC_FileCache::searchByMime('application','ogg');
+			$music=array_merge($music,$ogg);
+			OC_JSON::encodedPrint($music);
 			exit;
 	}
 }
diff --git a/apps/media/appinfo/app.php b/apps/media/appinfo/app.php
index 475a33500f4bdf4b15745de34bb0164b034183f7..651067fbbe1139cf7504fb6dd37c8f426b38a2ad 100644
--- a/apps/media/appinfo/app.php
+++ b/apps/media/appinfo/app.php
@@ -30,4 +30,5 @@ OC_APP::registerPersonal('media','settings');
 OC_App::register( array( 'order' => 3, 'id' => 'media', 'name' => 'Media' ));
 
 OC_App::addNavigationEntry(array('id' => 'media_index', 'order' => 2, 'href' => OC_Helper::linkTo('media', 'index.php'), 'icon' => OC_Helper::imagePath('core', 'places/music.svg'), 'name' => $l->t('Music')));
-?>
+
+OC_Search::registerProvider('OC_MediaSearchProvider');
diff --git a/apps/media/js/collection.js b/apps/media/js/collection.js
index 2249acf3cc40164170befd142cc3feafc4ff2f97..753785f77b0b0d2933f04a7fc4a8e597360a6a55 100644
--- a/apps/media/js/collection.js
+++ b/apps/media/js/collection.js
@@ -1,3 +1,5 @@
+var initScanned = false;
+
 Collection={
 	artists:[],
 	albums:[],
@@ -68,10 +70,10 @@ Collection={
 					for(var i=0;i<Collection.loadedListeners.length;i++){
 						Collection.loadedListeners[i]();
 					}
-					if(data.songs.length==0){
+					if(data.songs.length==0 && initScanned == false){
 						$('#scan input.start').click();
+						initScanned = true;
 					}
-					
 				}
 			});
 		}
@@ -81,13 +83,11 @@ Collection={
 			Collection.parent.show();
 		}
 		if(!Collection.loaded){
-			Collection.load(Collection.display)
+			Collection.load(Collection.display);
 		}else{
 			if(Collection.parent){
 				Collection.parent.find('tr:not(.template)').remove();
 				var template=Collection.parent.find('tr.template');
-				var lastArtist='';
-				var lastAlbum='';
 				$.each(Collection.artists,function(i,artist){
 					if(artist.name && artist.songs.length>0){
 						var tr=template.clone().removeClass('template');
@@ -108,7 +108,7 @@ Collection={
 							$('tr[data-artist="'+artist.name+'"]').addClass('active');
 						});
 						if(artist.songs.length>1){
-							var expander=$('<a class="expander">&gt;</a>');
+							expander=$('<a class="expander">&gt;</a>');
 							expander.data('expanded',false);
 							expander.click(function(event){
 								var tr=$(this).parent().parent();
@@ -136,10 +136,11 @@ Collection={
 		var first=true;
 		$.each(artist.albums,function(j,album){
 			$.each(album.songs,function(i,song){
+				var newRow;
 				if(first){
 					newRow=tr;
 				}else{
-					var newRow=tr.clone();
+					newRow=tr.clone();
 					newRow.find('td.artist').text('');
 					newRow.find('.expander').remove();
 				}
@@ -221,13 +222,14 @@ Collection={
 		tr.find('td.album-expander a.expander').addClass('expanded');
 		tr.find('td.album-expander a.expander').text('v');
 		$.each(albumData.songs,function(i,song){
+			var newRow;
 			if(i>0){
-				var newRow=tr.clone();
+				newRow=tr.clone();
 				newRow.find('a.expander').remove();
 				newRow.find('td.album a').text('');
 				newRow.find('td.artist a').text('');
 			}else{
-				var newRow=tr;
+				newRow=tr;
 			}
 			newRow.find('td.title a').text(song.name);
 			newRow.find('td.title a').click(function(event){
@@ -339,11 +341,11 @@ Collection={
 			path:song.song_path,
 			playCount:song.song_playcount,
 		};
-		album.songs.push(songData)
+		album.songs.push(songData);
 		artist.songs.push(songData);
 		Collection.songs.push(songData);
 	}
-}
+};
 
 $(document).ready(function(){
 	Collection.parent=$('#collection');
diff --git a/apps/media/js/loader.js b/apps/media/js/loader.js
index 055f858ae1648143949666bece34f862d5d0f45c..a832180d1e3a189ae1d8675ffc6218238ba204e5 100644
--- a/apps/media/js/loader.js
+++ b/apps/media/js/loader.js
@@ -1,7 +1,7 @@
 function musicTypeFromFile(file){
 	var extention=file.substr(file.indexOf('.')+1);
 	if(extention=='ogg'){
-		return 'oga'
+		return 'oga';
 	}
 	//TODO check for more specific cases
 	return extention;
@@ -39,7 +39,7 @@ function loadPlayer(type,ready){
 }
 
 $(document).ready(function() {
-	loadPlayer.done=false
+	loadPlayer.done=false;
 
 // 	FileActions.register('audio','Add to playlist','',addAudio);
 // 	FileActions.register('application/ogg','Add to playlist','',addAudio);
diff --git a/apps/media/js/music.js b/apps/media/js/music.js
index 3373cbac2579242584bc6089613c6c9705e5e363..1ffe4e10087bec762abbcdc477244356e427e451 100644
--- a/apps/media/js/music.js
+++ b/apps/media/js/music.js
@@ -16,7 +16,7 @@ $(document).ready(function(){
 			PlayList.render();
 		});
 		var button=$('<input type="button" title="'+t('media','Add album to playlist')+'" class="add"></input>');
-		button.css('background-image','url('+OC.imagePath('core','actions/play-add')+')')
+		button.css('background-image','url('+OC.imagePath('core','actions/play-add')+')');
 		button.click(function(event){
 			event.stopPropagation();
 			PlayList.add(media);
@@ -24,7 +24,7 @@ $(document).ready(function(){
 		});
 		row.find('div.name').append(button);
 		button.tipsy({gravity:'n', fade:true, delayIn: 400, live:true});
-	}
+	};
 	Collection.display();
 
 	Collection.load(function(){
@@ -34,11 +34,9 @@ $(document).ready(function(){
 			PlayList.add(song);
 			PlayList.play(0);
 		}
-	})
+	});
 });
 
-
-
 function getUrlVars(){
 	var vars = {}, hash;
 	var hashes = window.location.hash.substr(1).split('&');
@@ -52,8 +50,8 @@ function getUrlVars(){
 function musicTypeFromFile(file){
 	var extention=file.split('.').pop();
 	if(extention=='ogg'){
-		return 'oga'
+		return 'oga';
 	}
 	//TODO check for more specific cases
 	return extention;
-}
\ No newline at end of file
+}
diff --git a/apps/media/js/playlist.js b/apps/media/js/playlist.js
index 089065989aef2c3ba9f163ede2190a31d0357f7b..8e9e2a9153791061afd5eff7fe4f506c8d84fbd5 100644
--- a/apps/media/js/playlist.js
+++ b/apps/media/js/playlist.js
@@ -26,19 +26,19 @@ PlayList.render=function(){
 		li.click(function(event){
 			PlayList.play($(this).data('index'));
 		});
-		li.append(img)
+		li.append(img);
 		li.data('index',i);
 		li.addClass('song');
 		PlayList.parent.append(li);
 	}
         $(".jp-playlist-" + PlayList.current).addClass("collection_playing");
-}
+};
 PlayList.getSelected=function(){
 	return $('tbody td.name input:checkbox:checked').parent().parent();
-}
+};
 PlayList.hide=function(){
 	$('#playlist').hide();
-}
+};
 
 $(document).ready(function(){
 	PlayList.parent=$('#leftcontent');
diff --git a/apps/media/js/scanner.js b/apps/media/js/scanner.js
index 0baa9db419a14acf451d4b6ec211e063cc7a147d..6c991b60d52c81d770f76c87a43bc99edaf00641 100644
--- a/apps/media/js/scanner.js
+++ b/apps/media/js/scanner.js
@@ -6,7 +6,7 @@ Scanner={
 		$.getJSON(OC.linkTo('media','ajax/api.php')+'?action=find_music',function(songs){
 			Scanner.songsFound=songs.length;
 			if(ready){
-				ready(songs)
+				ready(songs);
 			}
 		});
 	},
@@ -24,13 +24,13 @@ Scanner={
 			Scanner.songsScanned=data.count;
 			$('#scan span.songCount').text(Scanner.songsScanned);
 			var progress=(Scanner.songsScanned/Scanner.songsFound)*100;
-			$('#scanprogressbar').progressbar('value',progress)
+			$('#scanprogressbar').progressbar('value',progress);
 		});
 		Scanner.eventSource.listen('done',function(count){
 			$('#scan input.start').show();
 			$('#scan input.stop').hide();
 			$('#scanprogressbar').hide();
-			Collection.load(Collection.display)
+			Collection.load(Collection.display);
 			if(ready){
 				ready();
 			}
@@ -41,4 +41,4 @@ Scanner={
 		Scanner.close();
 	},
 
-}
+};
diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php
index 1bcd0f08c80d228e09535f3e5bc11f08fa8a769c..9de291e8da2ca4620cbd0c0b67823cde7ba392ef 100644
--- a/apps/media/lib_media.php
+++ b/apps/media/lib_media.php
@@ -56,6 +56,7 @@ class OC_MEDIA{
 	 */
 	public static function updateFile($params){
 		$path=$params['path'];
+		if(!$path) return;
 		require_once 'lib_scanner.php';
 		require_once 'lib_collection.php';
 		//fix a bug where there were multiply '/' in front of the path, it should only be one
@@ -81,8 +82,8 @@ class OC_MEDIA{
 	}
 }
 
-class OC_MediaSearchProvider extends OC_Search_Provider{
-	function search($query){
+class OC_MediaSearchProvider implements OC_Search_Provider{
+	static function search($query){
 		require_once('lib_collection.php');
 		$artists=OC_MEDIA_COLLECTION::getArtists($query);
 		$albums=OC_MEDIA_COLLECTION::getAlbums(0,$query);
@@ -106,5 +107,3 @@ class OC_MediaSearchProvider extends OC_Search_Provider{
 	}
 }
 
-new OC_MediaSearchProvider();
-?>
diff --git a/apps/media/lib_scanner.php b/apps/media/lib_scanner.php
index ea594ee8e3c2550b7d01a9fe267b32a6a7e2bc5c..39658b27ba9135685965141d6e3f008fb79b0ee2 100644
--- a/apps/media/lib_scanner.php
+++ b/apps/media/lib_scanner.php
@@ -38,6 +38,8 @@ class OC_MEDIA_SCANNER{
 	 */
 	public static function scanCollection($eventSource=null){
 		$music=OC_FileCache::searchByMime('audio');
+		$ogg=OC_FileCache::searchByMime('application','ogg');
+		$music=array_merge($music,$ogg);
 		$eventSource->send('count',count($music));
 		$songs=0;
 		foreach($music as $file){
diff --git a/apps/remoteStorage/BearerAuth.php b/apps/remoteStorage/BearerAuth.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebcf189dfb94bae9f0d227ec88516a3f23d0768a
--- /dev/null
+++ b/apps/remoteStorage/BearerAuth.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * HTTP Bearer Authentication handler
+ *
+ * Use this class for easy http authentication setup
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_BearerAuth extends Sabre_HTTP_AbstractAuth {
+
+    /**
+     * Returns the supplied username and password.
+     *
+     * The returned array has two values:
+     *   * 0 - username
+     *   * 1 - password
+     *
+     * If nothing was supplied, 'false' will be returned
+     *
+     * @return mixed 
+     */
+    public function getUserPass() {
+
+        // Apache and mod_php
+        if (($user = $this->httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) {
+
+            return array($user,$pass);
+
+        }
+
+        // Most other webservers 
+        $auth = $this->httpRequest->getHeader('Authorization');
+
+        if (!$auth) return false;
+
+        if (strpos(strtolower($auth),'bearer')!==0) return false; 
+
+        return explode(':', base64_decode(substr($auth, 7)));
+
+    }
+
+    /**
+     * Returns an HTTP 401 header, forcing login
+     *
+     * This should be called when username and password are incorrect, or not supplied at all
+     *
+     * @return void
+     */
+    public function requireLogin() {
+
+        $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"');
+        $this->httpResponse->sendStatus(401);
+
+    }
+
+}
diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/WebDAV.php
index e048d19e8f2f8f665bf21951369a8579512ff7cd..cad465181a985488f5a70369f673e06c39b3988b 100644
--- a/apps/remoteStorage/WebDAV.php
+++ b/apps/remoteStorage/WebDAV.php
@@ -33,6 +33,7 @@ require_once('../../lib/base.php');
 OC_Util::checkAppEnabled('remoteStorage');
 require_once('Sabre/autoload.php');
 require_once('lib_remoteStorage.php');
+require_once('BearerAuth.php');
 require_once('oauth_ro_auth.php');
 
 ini_set('default_charset', 'UTF-8');
@@ -68,7 +69,10 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') {
 	$server->setBaseUri(OC::$WEBROOT."/apps/remoteStorage/WebDAV.php/$ownCloudUser");
 
 	// Auth backend
-	$authBackend = new OC_Connector_Sabre_Auth_ro_oauth(OC_remoteStorage::getValidTokens($ownCloudUser, $category));
+	$authBackend = new OC_Connector_Sabre_Auth_ro_oauth(
+      OC_remoteStorage::getValidTokens($ownCloudUser, $category),
+      $category
+      );
 
 	$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud');//should use $validTokens here
 	$server->addPlugin($authPlugin);
@@ -81,5 +85,6 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') {
 	// And off we go!
 	$server->exec();
 } else {
-	die('not the right address format '.var_export($pathParts, true));
+	//die('not the right address format '.var_export($pathParts, true));
+	die('not the right address format');
 }
diff --git a/apps/remoteStorage/ajax/revokeToken.php b/apps/remoteStorage/ajax/revokeToken.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca56cf560ecdc868ee3f2e21a09d92673298b255
--- /dev/null
+++ b/apps/remoteStorage/ajax/revokeToken.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+* ownCloud
+*
+* Original:
+* @author Frank Karlitschek
+* @copyright 2010 Frank Karlitschek karlitschek@kde.org
+* 
+* Adapted:
+* @author Michiel de Jong, 2012
+*
+* 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/>.
+*
+*/
+
+
+// Do not load FS ...
+$RUNTIME_NOSETUPFS = true;
+
+require_once('../../../lib/base.php');
+OC_Util::checkAppEnabled('remoteStorage');
+require_once('Sabre/autoload.php');
+require_once('../lib_remoteStorage.php');
+
+ini_set('default_charset', 'UTF-8');
+#ini_set('error_reporting', '');
+@ob_clean();
+
+echo OC_remoteStorage::deleteToken(file_get_contents("php://input"));
diff --git a/apps/remoteStorage/appinfo/app.php b/apps/remoteStorage/appinfo/app.php
index ac1ecde50824b2a9181152b4ccf24cb73c3c10a9..a1fbebc42f57c424cb8d241930032d8ccff12e02 100644
--- a/apps/remoteStorage/appinfo/app.php
+++ b/apps/remoteStorage/appinfo/app.php
@@ -3,3 +3,4 @@ OC_App::register( array(
   'order' => 10,
   'id' => 'remoteStorage',
   'name' => 'remoteStorage compatibility' ));
+OC_APP::registerPersonal('remoteStorage','settings');
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index 8179ca99112ef158b73ff8b67504f0f3cfa4b677..121587795dbf7162838ecdd38493f89395be4b52 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -3,8 +3,8 @@
 	<id>remoteStorage</id>
 	<name>remoteStorage compatibility</name>
 	<description>Enables your users to use ownCloud as their remote storage for unhosted applications.</description>
-	<version>0.2</version>
-	<licence>AGPL</licence>
+	<version>0.5</version>
+	<licence>AGPL or MIT</licence>
 	<author>Michiel de Jong</author>
 	<require>2</require>
 </info>
diff --git a/apps/remoteStorage/auth.css b/apps/remoteStorage/auth.css
new file mode 100644
index 0000000000000000000000000000000000000000..2dedad5dd0f5b42861bd0c82215f8b04618580af
--- /dev/null
+++ b/apps/remoteStorage/auth.css
@@ -0,0 +1,8 @@
+h2 { font-size:2em; font-weight:bold; margin-bottom:1em; white-space:nowrap; }
+ul.scopes { list-style:disc; }
+ul.scopes li { white-space:nowrap; }
+h2 img { width: 50% }
+#oauth { margin:4em auto 2em; width:20em; }
+#allow-auth { background-color:#5c3; text-shadow:#5e3 0 1px 0; color:#fff;
+-webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #5f3 inset; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #5f3 inset; box-shadow:0 1px 1px #fff, 0 1px 1px #5f3 inset; }
+#deny-auth { padding:0; margin:.7em; border:0; background:none; font-size:1.2em; -moz-box-shadow: 0 0 0 #fff, 0 0 0 #fff inset; -webkit-box-shadow: 0 0 0 #fff, 0 0 0 #fff inset; box-shadow: 0 0 0 #fff, 0 0 0 #fff inset; }
diff --git a/apps/remoteStorage/auth.php b/apps/remoteStorage/auth.php
index 85421ba3d888f92c3e4d0e6b49fd4a184f436efc..c00f9d5555cbe8c4f25a2697cca2ab17d4a5c496 100644
--- a/apps/remoteStorage/auth.php
+++ b/apps/remoteStorage/auth.php
@@ -8,7 +8,7 @@
 * @copyright 2010 Frank Karlitschek karlitschek@kde.org
 * 
 * Adapted:
-* @author Michiel de Jong, 2011
+* @author Michiel de Jong, 2012
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -17,11 +17,11 @@
 *
 * 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
+* 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/>.
+* License along with this library.	If not, see <http://www.gnu.org/licenses/>.
 *
 */
 
@@ -39,24 +39,8 @@ ini_set('default_charset', 'UTF-8');
 #ini_set('error_reporting', '');
 @ob_clean();
 
-//allow use as remote storage for other websites
-if(isset($_SERVER['HTTP_ORIGIN'])) {
-	header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
-	header('Access-Control-Max-Age: 3600');
-	header('Access-Control-Allow-Methods: OPTIONS, GET, PUT, DELETE, PROPFIND');
-  	header('Access-Control-Allow-Headers: Authorization, Content-Type');
-} else {
-	header('Access-Control-Allow-Origin: *');
-}
-
 $path = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"]));
-$pathParts =  explode('/', $path);
-// for webdav:
-// 0/     1       /   2    /   3  /   4     /    5     /   6     / 7
-//  /$ownCloudUser/remoteStorage/webdav/$userHost/$userName/$dataScope/$key
-// for oauth:
-// 0/      1      /  2     /  3  / 4
-//  /$ownCloudUser/remoteStorage/oauth/auth
+$pathParts =	explode('/', $path);
 
 if(count($pathParts) == 2 && $pathParts[0] == '') {
 	//TODO: input checking. these explodes may fail to produces the desired arrays:
@@ -66,19 +50,69 @@ if(count($pathParts) == 2 && $pathParts[0] == '') {
 		if($k=='user_address'){
 			$userAddress=$v;
 		} else if($k=='redirect_uri'){
-			$appUrl=$v;
+			$appUrlParts=explode('/', $v);
+		$appUrl = $appUrlParts[2];//bit dodgy i guess
 		} else if($k=='scope'){
-			$category=$v;
+			$categories=$v;
 		}
 	}
 	$currUser = OC_User::getUser();
 	if($currUser == $ownCloudUser) {
 		if(isset($_POST['allow'])) {
 			//TODO: check if this can be faked by editing the cookie in firebug!
-			$token=OC_remoteStorage::createCategory($appUrl, $category);
+			$token=OC_remoteStorage::createCategories($appUrl, $categories);
 			header('Location: '.$_GET['redirect_uri'].'#access_token='.$token.'&token_type=bearer');
 		} else {
-			echo '<form method="POST"><input name="allow" type="submit" value="Allow this web app to store stuff on your owncloud."></form>';
+?>
+<!DOCTYPE html>
+<html>
+	<head>
+	<title>ownCloud</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<link rel="shortcut icon" href="../../../core/img/favicon.png" /><link rel="apple-touch-icon-precomposed" href="../../../core/img/favicon-touch.png" />
+			<link rel="stylesheet" href="../../../core/css/styles.css" type="text/css" media="screen" />
+			<link rel="stylesheet" href="../auth.css" type="text/css" media="screen" />
+		</head>
+	<body id="body-login">
+	<div id="login">
+		<header>
+		<div id="header">
+			<img src="../../../core/img/owncloud-logo-medium-white.png" alt="ownCloud" />
+		</div>
+		</header>
+		<section id="main">
+		<div id="oauth">
+      <h2><img src="../remoteStorage-big.png" alt="remoteStorage" /></h2>
+      <p><strong><?php $appUrlParts = explode('/', $_GET['redirect_uri']); echo htmlentities($appUrlParts[2]); ?></strong>
+      requests read &amp; write access to your 
+      <?php
+        $categories = explode(',', htmlentities($_GET['scope']));
+        if(!count($categories)) {
+          echo htmlentities($_GET['scope']);
+        } else {
+          echo '<em>'.$categories[0].'</em>';
+          if(count($categories)==2) {
+            echo ' and <em>'.$categories[1].'</em>';
+          } else if(count($categories)>2) {
+            for($i=1; $i<count($categories)-1; $i++) {
+              echo ', <em>'.$categories[$i].'</em>';
+            }
+            echo ', and <em>'.$categories[$i].'</em>';
+          }
+        }
+      ?>.
+			</p>
+			<form accept-charset="UTF-8" method="post">
+				<input id="allow-auth" name="allow" type="submit" value="Allow" />
+				<input id="deny-auth" name="deny" type="submit" value="Deny" />
+			</form>
+		</div>
+		</section>
+	</div>
+	<footer><p class="info"><a href="http://owncloud.org/">ownCloud</a> &ndash; web services under your control</p></footer>
+	</body>
+</html>
+<?php
 		}
 	} else {
 		if((isset($_SERVER['HTTPS'])) && ($_SERVER['HTTPS'])) {
@@ -88,13 +122,13 @@ if(count($pathParts) == 2 && $pathParts[0] == '') {
 		}
 		$url .= $_SERVER['SERVER_NAME'];
 		$url .= substr($_SERVER['SCRIPT_NAME'], 0, -strlen('apps/remoteStorage/compat.php'));
-		die('You are '.($currUser?'logged in as '.$currUser.' instead of '.$ownCloudUser:'not logged in').'. Please '
-			.'<input type="submit" onclick="'
-			."window.open('$url','Close me!','height=600,width=300');"
-			.'" value="log in">'
-			.', close the pop-up, and '
-			.'<form method="POST"><input name="allow" type="submit" value="Click here"></form>');
+		if($currUser) {
+			die('You are logged in as '.$currUser.' instead of '.$ownCloudUser);
+		} else {
+			header('Location: /?redirect_url='.urlencode('/apps/remoteStorage/auth.php'.$_SERVER['PATH_INFO'].'?'.$_SERVER['QUERY_STRING']));
+		}
 	}
 } else {
-	die('please use auth.php/username?params. '.var_export($pathParts, true));
+	//die('please use auth.php/username?params. '.var_export($pathParts, true));
+	die('please use auth.php/username?params.');
 }
diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php
index 4f19310904eaf8a613c52136ac898a9959e6f1ae..6e6a19c739f8fc8ef1b7086bcc861e1ca1436bb5 100644
--- a/apps/remoteStorage/lib_remoteStorage.php
+++ b/apps/remoteStorage/lib_remoteStorage.php
@@ -2,11 +2,13 @@
 
 class OC_remoteStorage {
 	public static function getValidTokens($ownCloudUser, $category) {
-		$query=OC_DB::prepare("SELECT token,appUrl FROM *PREFIX*authtoken WHERE user=? AND category=? LIMIT 100");
-		$result=$query->execute(array($ownCloudUser,$category));
+		$query=OC_DB::prepare("SELECT token,appUrl,category FROM *PREFIX*authtoken WHERE user=? LIMIT 100");
+		$result=$query->execute(array($ownCloudUser));
 		$ret = array();
 		while($row=$result->fetchRow()){
-			$ret[$row['token']]=true;
+			if(in_array($category, explode(',', $row['category']))) {
+				$ret[$row['token']]=true;
+			}
 		}
 		return $ret;
 	}
@@ -18,8 +20,8 @@ class OC_remoteStorage {
 		$ret = array();
 		while($row=$result->fetchRow()){
 			$ret[$row['token']] = array(
-				'appUrl' => $row['appurl'],
-				'category' => $row['category'],
+				'appUrl' => $row['appUrl'],
+				'categories' => $row['category'],
 			);
 		}
 		return $ret;
@@ -29,22 +31,25 @@ class OC_remoteStorage {
 		$user=OC_User::getUser();
 		$query=OC_DB::prepare("DELETE FROM *PREFIX*authtoken WHERE token=? AND user=?");
 		$result=$query->execute(array($token,$user));
+		return 'unknown';//how can we see if any rows were affected?
 	}
-	private static function addToken($token, $appUrl, $category){
+	private static function addToken($token, $appUrl, $categories){
 		$user=OC_User::getUser();
 		$query=OC_DB::prepare("INSERT INTO *PREFIX*authtoken (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)");
-		$result=$query->execute(array($token,$appUrl,$user,$category));
+		$result=$query->execute(array($token,$appUrl,$user,$categories));
 	}
-	public static function createCategory($appUrl, $category) {
+	public static function createCategories($appUrl, $categories) {
 		$token=uniqid();
-		self::addToken($token, $appUrl, $category);
-		//TODO: input checking on $category
 		OC_Util::setupFS(OC_User::getUser());
-		$scopePathParts = array('remoteStorage', $category);
-		for($i=0;$i<=count($scopePathParts);$i++){
-			$thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i));
-			if(!OC_Filesystem::file_exists($thisPath)) {
-				OC_Filesystem::mkdir($thisPath);
+		self::addToken($token, $appUrl, $categories);
+		foreach(explode(',', $categories) as $category) {
+			//TODO: input checking on $category
+			$scopePathParts = array('remoteStorage', $category);
+			for($i=0;$i<=count($scopePathParts);$i++){
+				$thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i));
+				if(!OC_Filesystem::file_exists($thisPath)) {
+					OC_Filesystem::mkdir($thisPath);
+				}
 			}
 		}
 		return base64_encode('remoteStorage:'.$token);
diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php
index 5403fbe20c9b530d76f16e5273551db5cc70c1c6..085a54699207a657ba9eef31d0b5bb580edc1f99 100644
--- a/apps/remoteStorage/oauth_ro_auth.php
+++ b/apps/remoteStorage/oauth_ro_auth.php
@@ -16,9 +16,10 @@
 
 class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBasic {
 	private $validTokens;
-
-	public function __construct($validTokensArg) {
+  private $category;
+	public function __construct($validTokensArg, $categoryArg) {
 		$this->validTokens = $validTokensArg;
+    $this->category = $categoryArg;
 	}
 
 	/**
@@ -31,29 +32,31 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa
 	 */
 	protected function validateUserPass($username, $password){
 		//always give read-only:
-		if(in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS'))) {
-			OC_Util::setUpFS();
-			return true;
-		} else if(isset($this->validTokens[$password]) && $this->validTokens[$password] == $username) {
+		if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') 
+		    || (isset($this->validTokens[$password]))
+        || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+        ) {
 			OC_Util::setUpFS();
 			return true;
 		} else {
-var_export($_SERVER);
-var_export($this->validTokens);
-die('not getting in with "'.$username.'"/"'.$password.'"!');
+      //var_export($_SERVER);
+      //var_export($this->validTokens);
+      //die('not getting in with "'.$username.'"/"'.$password.'"!');
 			return false;	
 		}
 	}
 
 	//overwriting this to make it not automatically fail if no auth header is found:
 	public function authenticate(Sabre_DAV_Server $server,$realm) {
-		$auth = new Sabre_HTTP_BasicAuth();
+		$auth = new Sabre_HTTP_BearerAuth();
 		$auth->setHTTPRequest($server->httpRequest);
 		$auth->setHTTPResponse($server->httpResponse);
 		$auth->setRealm($realm);
 		$userpass = $auth->getUserPass();
 		if (!$userpass) {
-			if(in_array($_SERVER['REQUEST_METHOD'], array('OPTIONS'))) {
+			if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
+	        ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+          ) {
 				$userpass = array('', '');
 			} else {
 				$auth->requireLogin();
diff --git a/apps/remoteStorage/remoteStorage-big.png b/apps/remoteStorage/remoteStorage-big.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c429a6a738ddb19638a8f7ee540866e0d501e04
Binary files /dev/null and b/apps/remoteStorage/remoteStorage-big.png differ
diff --git a/apps/remoteStorage/remoteStorage.png b/apps/remoteStorage/remoteStorage.png
new file mode 100644
index 0000000000000000000000000000000000000000..6b751c09997e82ee88ef5a78fc7ef1bee3df5b0f
Binary files /dev/null and b/apps/remoteStorage/remoteStorage.png differ
diff --git a/apps/remoteStorage/settings.php b/apps/remoteStorage/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa85f77a535dae8bf9608c1297fcec2e464916cd
--- /dev/null
+++ b/apps/remoteStorage/settings.php
@@ -0,0 +1,7 @@
+<?php
+
+require_once('lib_remoteStorage.php');
+$tmpl = new OC_Template( 'remoteStorage', 'settings');
+
+return $tmpl->fetchPage();
+?>
diff --git a/apps/remoteStorage/templates/settings.php b/apps/remoteStorage/templates/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..9b5c3b6229c2eda37a518ec6e61cddc075648c15
--- /dev/null
+++ b/apps/remoteStorage/templates/settings.php
@@ -0,0 +1,28 @@
+	<fieldset class="personalblock">
+		<?php
+			echo '<img src="/apps/remoteStorage/remoteStorage.png" style="width:16px"> '
+				.'<strong>'.$l->t('remoteStorage').'</strong> user address: '
+				.OC_User::getUser().'@'.$_SERVER['SERVER_NAME']
+				.' (<a href="http://unhosted.org/">more info</a>)';
+		?>
+		<p><em>Apps that currently have access to your ownCloud:</em></p>
+		<script>
+			function revokeToken(token) {
+				var xhr = new XMLHttpRequest();
+				xhr.open('POST', '/apps/remoteStorage/ajax/revokeToken.php', true);
+				xhr.send(token);
+			}
+		</script>
+		<ul>
+		<?php
+			foreach(OC_remoteStorage::getAllTokens() as $token => $details) {
+				echo '<li onmouseover="'
+					.'document.getElementById(\'revoke_'.$token.'\').style.display=\'inline\';"'
+					.'onmouseout="document.getElementById(\'revoke_'.$token.'\').style.display=\'none\';"'
+					.'> <strong>'.$details['appUrl'].'</strong>: '.$details['categories']
+					.' <a href="#" title="Revoke" class="action" style="display:none" id="revoke_'.$token.'" onclick="'
+					.'revokeToken(\''.$token.'\');this.parentNode.style.display=\'none\';"'
+					.'><img src="/core/img/actions/delete.svg"></a></li>'."\n";
+			}
+		?></ul>
+	</fieldset>
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
index 1f2d8ed9af312e9261456c4d788aa50402d7e876..ad44fe95f9ecf3ef151387a3b58348c3be22a49b 100644
--- a/apps/user_ldap/settings.php
+++ b/apps/user_ldap/settings.php
@@ -20,7 +20,7 @@
  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_password', 'ldap_base', 'ldap_filter', 'ldap_display_name', 'ldap_tls', 'ldap_nocase');
+$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_password', 'ldap_base', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_display_name', 'ldap_tls', 'ldap_nocase'. 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr');
 
 if ($_POST) {
 	foreach($params as $param){
@@ -29,12 +29,12 @@ if ($_POST) {
 		}
 		elseif('ldap_tls' == $param) {
 			// unchecked checkboxes are not included in the post paramters
-				OC_Appconfig::setValue('user_ldap', $param, 0);		
+				OC_Appconfig::setValue('user_ldap', $param, 0);
 		}
 		elseif('ldap_nocase' == $param) {
 			OC_Appconfig::setValue('user_ldap', $param, 0);
 		}
-		
+
 	}
 }
 
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 5bbd5d4008d70181c0191c6c94825c6bac796ee9..828c72cba97e4187427a0c5099aa7de21f68d12f 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -6,15 +6,16 @@
 		<p><label for="ldap_dn"><?php echo $l->t('Name');?></label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" />
 		<label for="ldap_password"><?php echo $l->t('Password');?></label><input type="password" id="ldap_password" name="ldap_password" value="<?php echo $_['ldap_password']; ?>" />
 		<small><?php echo $l->t('Leave both empty for anonymous bind for search, then bind with users credentials.');?></small></p>
-		<p><label for="ldap_base"><?php echo $l->t('Base');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" />
-		<label for="ldap_filter"><?php echo $l->t('Filter (use %%uid placeholder)');?></label><input type="text" id="ldap_filter" name="ldap_filter" value="<?php echo $_['ldap_filter']; ?>" /></p>
+		<p><label for="ldap_base"><?php echo $l->t('Base');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" /></p>
+		<p><label for="ldap_login_filter"><?php echo $l->t('User Login Filter');?></label><input type="text" id="ldap_login_filter" name="ldap_login_filter" value="<?php echo $_['ldap_login_filter']; ?>" /><small><?php echo $l->t('use %%uid placeholder, e.g. uid=%%uid');?></small></p>
+		<p><label for="ldap_userlist_filter"><?php echo $l->t('User List Filter');?></label><input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter" value="<?php echo $_['ldap_userlist_filter']; ?>" /><small><?php echo $l->t('without any placeholder, e.g. "objectClass=person".');?>	</p>
 		<p><label for="ldap_display_name"><?php echo $l->t('Display Name Field');?></label><input type="text" id="ldap_display_name" name="ldap_display_name" value="<?php echo $_['ldap_display_name']; ?>" />
 		<small><?php echo $l->t('Currently the display name field needs to be the same you matched %%uid against in the filter above, because ownCloud doesn\'t distinguish between user id and user name.');?></small></p>
 		<p><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1"<?php if ($_['ldap_tls']) echo ' checked'; ?>><label for="ldap_tls"><?php echo $l->t('Use TLS');?></label></p>
 		<p><input type="checkbox" id="ldap_nocase" name="ldap_nocase" value="1"<?php if ($_['ldap_nocase']) echo ' checked'; ?>><label for="ldap_nocase"><?php echo $l->t('Case insensitve LDAP server (Windows)');?></label></p>
-		<p><label for="ldap_quota">Quota Attribute</label><input type="text" id="ldap_quota" name="ldap_quota" value="<?php echo $_['ldap_quota']; ?>" />
+		<p><label for="ldap_quota_attr">Quota Attribute</label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" value="<?php echo $_['ldap_quota_attr']; ?>" />
                 <label for="ldap_quota_def">Quota Default</label><input type="text" id="ldap_quota_def" name="ldap_quota_def" value="<?php echo $_['ldap_quota_def']; ?>" />bytes</p>
-                <p><label for="ldap_email">Email Attribute</label><input type="text" id="ldap_email" name="ldap_email" value="<?php echo $_['ldap_email']; ?>" /></p>
+                <p><label for="ldap_email_attr">Email Attribute</label><input type="text" id="ldap_email_attr" name="ldap_email_attr" value="<?php echo $_['ldap_email_attr']; ?>" /></p>
 		<input type="submit" value="Save" />
 	</fieldset>
 </form>
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index 670d938ea95125aeae743e03249985ca3073aa9e..3521a9d90cf678e5735f8fe1614337802a4b49ab 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -32,7 +32,8 @@ class OC_USER_LDAP extends OC_User_Backend {
 	protected $ldap_dn;
 	protected $ldap_password;
 	protected $ldap_base;
-	protected $ldap_filter;
+	protected $ldap_login_filter;
+	protected $ldap_userlist_filter;
 	protected $ldap_tls;
 	protected $ldap_nocase;
 	protected $ldap_display_name;
@@ -49,7 +50,8 @@ class OC_USER_LDAP extends OC_User_Backend {
 		$this->ldap_dn = OC_Appconfig::getValue('user_ldap', 'ldap_dn','');
 		$this->ldap_password = OC_Appconfig::getValue('user_ldap', 'ldap_password','');
 		$this->ldap_base = OC_Appconfig::getValue('user_ldap', 'ldap_base','');
-		$this->ldap_filter = OC_Appconfig::getValue('user_ldap', 'ldap_filter','');
+		$this->ldap_login_filter = OC_Appconfig::getValue('user_ldap', 'ldap_login_filter','');
+		$this->ldap_userlist_filter = OC_Appconfig::getValue('user_ldap', 'ldap_userlist_filter','objectClass=person');
 		$this->ldap_tls = OC_Appconfig::getValue('user_ldap', 'ldap_tls', 0);
 		$this->ldap_nocase = OC_Appconfig::getValue('user_ldap', 'ldap_nocase', 0);
 		$this->ldap_display_name = OC_Appconfig::getValue('user_ldap', 'ldap_display_name', OC_USER_BACKEND_LDAP_DEFAULT_DISPLAY_NAME);
@@ -61,7 +63,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 			&& !empty($this->ldap_port)
 			&& ((!empty($this->ldap_dn) && !empty($this->ldap_password)) || (empty($this->ldap_dn) && empty($this->ldap_password)))
 			&& !empty($this->ldap_base)
-			&& !empty($this->ldap_filter)
+			&& !empty($this->ldap_login_filter)
 			&& !empty($this->ldap_display_name)
 		)
 		{
@@ -79,9 +81,13 @@ class OC_USER_LDAP extends OC_User_Backend {
 		if( !$this->ldap_dc )
 			return false;
 
-		$quota = $this->ldap_dc[$this->ldap_quota_attr][0];
+		if(!empty($this->ldap_quota_attr)) {
+			$quota = $this->ldap_dc[strtolower($this->ldap_quota_attr)][0];
+		} else {
+			$quota = false;
+		}
 		$quota = $quota != -1 ? $quota : $this->ldap_quota_def;
-		OC_Preferences::setValue($uid, 'files', 'quota', $quota);
+		OC_Preferences::setValue($uid, 'files', 'quota', OC_Helper::computerFileSize($quota));
 	}
 
 	private function setEmail( $uid ) {
@@ -127,7 +133,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 			return false;
 
 		// get dn
-		$filter = str_replace('%uid', $uid, $this->ldap_filter);
+		$filter = str_replace('%uid', $uid, $this->ldap_login_filter);
 		$sr = ldap_search( $this->getDs(), $this->ldap_base, $filter );
 		$entries = ldap_get_entries( $this->getDs(), $sr );
 
@@ -152,7 +158,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 			return false;
 		}
 
-		if(!empty($this->ldap_quota) && !empty($this->ldap_quota_def)) {
+		if(!empty($this->ldap_quota_attr) || !empty($this->ldap_quota_def)) {
 			$this->setQuota($uid);
 		}
 
@@ -161,7 +167,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 		}
 
 		if($this->ldap_nocase) {
-			$filter = str_replace('%uid', $uid, $this->ldap_filter);
+			$filter = str_replace('%uid', $uid, $this->ldap_login_filter);
 			$sr = ldap_search( $this->getDs(), $this->ldap_base, $filter );
 			$entries = ldap_get_entries( $this->getDs(), $sr );
 			if( $entries['count'] == 1 ) {
@@ -187,7 +193,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 		if(!$this->configured){
 			return false;
 		}
-		$dn = $this->getDn($uid);
+		$dn = $this->getDc($uid);
 		return !empty($dn);
 	}
 
@@ -202,8 +208,7 @@ class OC_USER_LDAP extends OC_User_Backend {
 			return false;
 
 		// get users
-		$filter = 'objectClass=person';
-		$sr = ldap_search( $this->getDs(), $this->ldap_base, $filter );
+		$sr = ldap_search( $this->getDs(), $this->ldap_base, $this->ldap_userlist_filter );
 		$entries = ldap_get_entries( $this->getDs(), $sr );
 		if( $entries['count'] == 0 )
 			return false;
diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml
index a4071dae1725e7e124dc6a765ecf7556a39d0362..55cf2cf2201d08e448a9e419782db41bc98d7e62 100644
--- a/apps/user_webfinger/appinfo/info.xml
+++ b/apps/user_webfinger/appinfo/info.xml
@@ -3,8 +3,8 @@
 	<id>user_webfinger</id>
 	<name>Webfinger</name>
 	<description>Provide WebFinger for all users so they get a user address like user@owncloudinstance which can be used for unhosted applications. If you don't run ownCloud in the root of your domain, for instance if you run it on example.com/owncloud/, then make sure you link example.com/.well-known/ to example.com/owncloud/apps/user_webfinger/ - by running something like "ln -s /var/www/owncloud/apps/user_webfinger /var/www/.well-known". Only enable this app if you run this ownCloud installation on a public web address, not if you run it on an intranet or on localhost.</description>
-	<version>0.1</version>
-	<licence>AGPL</licence>
+	<version>0.2</version>
+	<licence>AGPL or MIT</licence>
 	<author>Michiel de Jong</author>
 	<require>2</require>
 </info>
diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php
index ceb15f22da4457a223056083b5f475e51f1de96e..ac577cf9a0c4526dc7315061fd5247f18803f608 100644
--- a/apps/user_webfinger/host-meta.php
+++ b/apps/user_webfinger/host-meta.php
@@ -4,13 +4,13 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/host-meta.php') {
 } else {
 	header('Please-first: activate');
 }
-header("Content-Type: application/xml+xrd");
+header("Content-Type: application/xrd+xml");
 echo "<";
 ?>
 ?xml version="1.0" encoding="UTF-8"?>
 <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0">
 	<hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME'] ?></hm:Host>
-	<Link rel="lrdd" template="http<?php echo ($_SERVER['HTTPS']?'s':''); ?>://<?php echo $_SERVER['SERVER_NAME'] ?>/.well-known/webfinger.php?q={uri}">
+	<Link rel="lrdd" template="http<?php echo (isset($_SERVER['HTTPS'])?'s':''); ?>://<?php echo $_SERVER['SERVER_NAME'] ?>/.well-known/webfinger.php?q={uri}">
 	</Link>
 </XRD>
 
diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php
index d695a833f319048633fe461d234a0298e7e738c8..5c2a24aa0707fd8bfb0aeddfcfa7b98d986f0d74 100644
--- a/apps/user_webfinger/webfinger.php
+++ b/apps/user_webfinger/webfinger.php
@@ -4,7 +4,7 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') {
 } else {
 	header('Please-first: activate');
 }
-// header("Content-Type: application/xml+xrd");
+header("Content-Type: application/xrd+xml");
 
 // calculate the documentroot
 // modified version of the one in lib/base.php that takes the .well-known symlink into account
@@ -22,7 +22,7 @@ if($_GET['q']) {
 if(substr($userName, 0, 5) == 'acct:') {
 	$userName = substr($userName, 5);
 }
-if($_SERVER['HTTPS']) {
+if(isset($_SERVER['HTTPS'])) {
 	$baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/';
 } else {
 	$baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/';
diff --git a/config/config.sample.php b/config/config.sample.php
index cd5253587136454b91fffc2f11d0ca365453582a..5206737455599aeef73ba6f779fbebb9d3aee889 100755
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -13,6 +13,8 @@ $CONFIG = array(
 "forcessl" => false,
 "enablebackup" => false,
 "theme" => "",
+"3rdpartyroot" => "",
+"3rdpartyurl" => "",
 // "datadirectory" => ""
 );
 ?>
diff --git a/core/ajax/appconfig.php b/core/ajax/appconfig.php
new file mode 100644
index 0000000000000000000000000000000000000000..50984ac32b92d4da6eaf6af711413a97a36200a7
--- /dev/null
+++ b/core/ajax/appconfig.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once ("../../lib/base.php");
+OC_JSON::checkLoggedIn();
+$action=isset($_POST['action'])?$_POST['action']:$_GET['action'];
+$result=false;
+switch($action){
+	case 'getValue':
+		$result=OC_Appconfig::getValue($_GET['app'],$_GET['key'],$_GET['default']);
+		break;
+	case 'setValue':
+		$result=OC_Appconfig::setValue($_POST['app'],$_POST['key'],$_POST['value']);
+		break;
+	case 'getApps':
+		$result=OC_Appconfig::getApps();
+		break;
+	case 'getKeys':
+		$result=OC_Appconfig::getKeys($_GET['app']);
+		break;
+	case 'hasKey':
+		$result=OC_Appconfig::hasKey($_GET['app'],$_GET['key']);
+		break;
+	case 'deleteKey':
+		$result=OC_Appconfig::deleteKey($_POST['app'],$_POST['key']);
+		break;
+	case 'deleteApp':
+		$result=OC_Appconfig::deleteApp($_POST['app']);
+		break;
+}
+OC_JSON::success(array('data'=>$result));
diff --git a/core/css/styles.css b/core/css/styles.css
index 53af8b383e3aab039e2273ad8cef6eeaf2bd913d..f5a181c4529f42721acb0df5bf4ceaea0648b7e2 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -3,6 +3,7 @@
  See the COPYING-README file. */
 
 html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin:0; padding:0; border:0; outline:0; font-weight:inherit; font-size:100%; font-family:inherit; vertical-align:baseline; cursor:default; }
+html, body { height: 100%; }
 article, aside, dialog, figure, footer, header, hgroup, nav, section { display:block; }
 body { line-height:1.5; }
 table { border-collapse:separate; border-spacing:0; white-space:nowrap; }
@@ -39,8 +40,8 @@ input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,
 input[type="password"]:hover, input[type="password"]:focus, input[type="password"]:active,
 .searchbox input[type="search"]:hover, .searchbox input[type="search"]:focus, .searchbox input[type="search"]:active { background-color:#fff; color:#333; opacity:1; }
 
-input[type="submit"], input[type="button"], button, .button, #quota, div.jp-progress, .pager li a { width:auto; padding:.4em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; }
-input[type="submit"]:hover, input[type="submit"]:focus, input[type="button"]:hover, input[type="button"]:focus, .button:hover { background:#fff; color:#333; }
+input[type="submit"], input[type="button"], button, .button, #quota, div.jp-progress, select, .pager li a { width:auto; padding:.4em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; }
+input[type="submit"]:hover, input[type="submit"]:focus, input[type="button"]:hover, select:hover, select:focus, select:active, input[type="button"]:focus, .button:hover { background:#fff; color:#333; }
 input[type="checkbox"] { width:auto; }
 #quota { cursor:default; }
 
@@ -55,11 +56,11 @@ input[type="submit"].highlight{ background:#ffc100; border:1px solid #db0; text-
 /* CONTENT ------------------------------------------------------------------ */
 #controls { padding: 0 0.5em; width:100%; top:3.5em; height:2.8em; margin:0; background:#f7f7f7; border-bottom:1px solid #eee; position:fixed; z-index:50; -moz-box-shadow:0 -3px 7px #000; -webkit-box-shadow:0 -3px 7px #000; box-shadow:0 -3px 7px #000; }
 #controls .button { display:inline-block; }
-#content { margin:3.5em 0 0 12.5em; }
+#content { top: 3.5em; left: 12.5em; position: absolute; }
 #leftcontent, .leftcontent { position:fixed; overflow: auto; top:6.4em; width:20em; background:#f8f8f8; border-right:1px solid #ddd; }
 #leftcontent li, .leftcontent li { background:#f8f8f8; padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; }
 #leftcontent li:hover, #leftcontent li:active, #leftcontent li.active, .leftcontent li:hover, .leftcontent li:active, .leftcontent li.active { background:#eee; }
-#rightcontent, .rightcontent { position:absolute; top:6.4em; left:33em; }
+#rightcontent, .rightcontent { position:fixed; top: 6.4em; left: 32.5em; overflow: auto }
 
 
 /* LOG IN & INSTALLATION ------------------------------------------------------------ */
@@ -113,8 +114,8 @@ table:not(.nostyle) tr { -webkit-transition:background-color 500ms; -moz-transit
 tbody tr:hover, tr:active { background-color:#f8f8f8; }
 
 #body-settings .personalblock, #body-settings .helpblock { padding:.5em 1em; margin:1em; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; }
-#body-settings .personalblock#quota { position:relative; margin-top:4.5em; padding:0; }
-#body-settings #controls+.helpblock { position:relative; margin-top:7.3em; }
+#body-settings .personalblock#quota { position:relative; padding:0; }
+#body-settings #controls+.helpblock { position:relative; margin-top: 3em; }
 .personalblock > legend { margin-top:2em; }
 
 #quota div, div.jp-play-bar, div.jp-seek-bar { padding:0; background:#e6e6e6; font-weight:normal; white-space:nowrap; -moz-border-radius-bottomleft:.4em; -webkit-border-bottom-left-radius:.4em; border-bottom-left-radius:.4em; -moz-border-radius-topleft:.4em; -webkit-border-top-left-radius:.4em; border-top-left-radius:.4em; }
@@ -127,4 +128,6 @@ div.jp-play-bar, div.jp-seek-bar { padding:0; }
 li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ffe .8em .8em no-repeat; color: #FF3B3B; border:1px solid #ccc; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; }
 .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { overflow: hidden; text-overflow: ellipsis; }
 .hint { background-image: url('/core/img/actions/info.png'); background-repeat:no-repeat; color: #777777; padding-left: 25px; background-position: 0 0.3em;}
-.separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; }
\ No newline at end of file
+.separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; }
+
+a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px }
diff --git a/core/js/config.js b/core/js/config.js
new file mode 100644
index 0000000000000000000000000000000000000000..500fe072a64976eebb02b66561027993163c613f
--- /dev/null
+++ b/core/js/config.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OC.AppConfig={
+	url:OC.filePath('core','ajax','appconfig.php'),
+	getCall:function(action,data,callback){
+		data.action=action;
+		$.getJSON(OC.AppConfig.url,data,function(result){
+			if(result.status='success'){
+				if(callback){
+					callback(result.data);
+				}
+			}
+		});
+	},
+	postCall:function(action,data,callback){
+		data.action=action;
+		$.post(OC.AppConfig.url,data,function(result){
+			if(result.status='success'){
+				if(callback){
+					callback(result.data);
+				}
+			}
+		},'json');
+	},
+	getValue:function(app,key,defaultValue,callback){
+		if(typeof defaultValue=='function'){
+			callback=defaultValue;
+			defaultValue=null;
+		}
+		OC.AppConfig.getCall('getValue',{app:app,key:key,default:defaultValue},callback);
+	},
+	setValue:function(app,key,value){
+		OC.AppConfig.postCall('setValue',{app:app,key:key,value:value});
+	},
+	getApps:function(callback){
+		OC.AppConfig.getCall('getApps',{},callback);
+	},
+	getKeys:function(app,callback){
+		OC.AppConfig.getCall('getKeys',{app:app},callback);
+	},
+	hasKey:function(app,key,callback){
+		OC.AppConfig.getCall('hasKey',{app:app,key:key},callback);
+	},
+	deleteKey:function(app,key){
+		OC.AppConfig.postCall('deleteKey',{app:app,key:key});
+	},
+	deleteApp:function(app){
+		OC.AppConfig.postCall('deleteApp',{app:app});
+	},
+}
+//TODO OC.Preferences
diff --git a/core/js/js.js b/core/js/js.js
index c6cde3cea95d3ee4b0a0f2cbe9f912e8d9cab085..df1b5c6ce769f19c58a26035ba3d948176da2a34 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -13,7 +13,7 @@ function t(app,text){
 			success:function(jsondata){
 				t.cache[app] = jsondata.data;
 			},
-		})
+		});
 
 		// Bad answer ...
 		if( !( app in t.cache )){
@@ -58,7 +58,7 @@ OC={
 		}
 		link+=app;
 		if(type){
-			link+=type+'/'
+			link+=type+'/';
 		}
 		link+=file;
 		return link;
@@ -73,7 +73,7 @@ OC={
 	 */ 
 	imagePath:function(app,file){
 		if(file.indexOf('.')==-1){//if no extention is given, use png or svg depending on browser support
-			file+=(SVGSupport())?'.svg':'.png'
+			file+=(SVGSupport())?'.svg':'.png';
 		}
 		return OC.filePath(app,'img',file);
 	},
@@ -125,8 +125,9 @@ OC={
 				OC.search.showResults(results);
 			});
 		}
-	}
-}
+	},
+	dialogs:OCdialogs
+};
 OC.search.customResults={};
 OC.search.currentResult=-1;
 OC.search.lastQuery='';
@@ -147,7 +148,7 @@ if(typeof localStorage !='undefined'){
 		getItem:function(name){
 			return JSON.parse(localStorage.getItem(OC.localStorage.namespace+name));
 		}
-	}
+	};
 }else{
 	//dummy localstorage
 	OC.localStorage={
@@ -160,7 +161,7 @@ if(typeof localStorage !='undefined'){
 		getItem:function(name){
 			return null;
 		}
-	}
+	};
 }
 
 /**
@@ -182,7 +183,7 @@ if (!Array.prototype.filter) {
 			}
 		}
 		return res;
-	}
+	};
 }
 /**
  * implement Array.indexOf for browsers without native support
@@ -235,11 +236,11 @@ SVGSupport.checkMimeType=function(){
 			});
 			if(headers["Content-Type"]!='image/svg+xml'){
 				replaceSVG();
-				SVGSupport.checkMimeType.correct=false
+				SVGSupport.checkMimeType.correct=false;
 			}
 		}
 	});
-}
+};
 SVGSupport.checkMimeType.correct=true;
 
 //replace all svg images with png for browser compatibility
@@ -252,16 +253,22 @@ function replaceSVG(){
 	$('.svg').each(function(index,element){
 		element=$(element);
 		var background=element.css('background-image');
-		if(background && background!='none'){
-			background=background.substr(0,background.length-4)+'png)';
-			element.css('background-image',background);
+		if(background){
+			var i=background.lastIndexOf('.svg');
+			if(i>=0){
+				background=background.substr(0,i)+'.png'+background.substr(i+4);
+				element.css('background-image',background);
+			}
 		}
 		element.find('*').each(function(index,element) {
 			element=$(element);
 			var background=element.css('background-image');
-			if(background && background!='none'){
-				background=background.substr(0,background.length-4)+'png)';
-				element.css('background-image',background);
+			if(background){
+				var i=background.lastIndexOf('.svg');
+				if(i>=0){
+					background=background.substr(0,i)+'.png'+background.substr(i+4);
+					element.css('background-image',background);
+				}
 			}
 		});
 	});
@@ -305,11 +312,12 @@ $(document).ready(function(){
 
 	$(window).resize(function () {
 		fillHeight($('#leftcontent'));
+		fillWindow($('#content'));
 		fillWindow($('#rightcontent'));
 	});
 	$(window).trigger('resize');
 	
-	if(!SVGSupport()){//replace all svg images with png images for browser that dont support svg
+	if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg
 		replaceSVG();
 	}else{
 		SVGSupport.checkMimeType();
@@ -379,7 +387,6 @@ $(document).ready(function(){
 		}
 	});
 
-	if($('body').attr("id")=="body-user") { $('#settings #expanddiv').hide(); }
 	$('#settings #expand').click(function(event) {
 		$('#settings #expanddiv').slideToggle();
 		event.stopPropagation();
@@ -444,4 +451,33 @@ $.fn.filterAttr = function(attr_name, attr_value) {
    return this.filter(function() { return $(this).attr(attr_name) === attr_value; });
 };
 
+function humanFileSize(size) {
+	humanList = ['B', 'kB', 'MB', 'GB', 'TB'];
+	// Calculate Log with base 1024: size = 1024 ** order
+	order = Math.floor(Math.log(size) / Math.log(1024));
+	// Stay in range of the byte sizes that are defined
+	order = Math.min(humanList.length - 1, order);
+	readableFormat = humanList[order];
+	relativeSize = (size / Math.pow(1024, order)).toFixed(1);
+	if(relativeSize.substr(relativeSize.length-2,2)=='.0'){
+		relativeSize=relativeSize.substr(0,relativeSize.length-2);
+	}
+	return relativeSize + ' ' + readableFormat;
+}
+
+function simpleFileSize(bytes) {
+	mbytes = Math.round(bytes/(1024*1024/10))/10;
+	if(bytes == 0) { return '0'; }
+	else if(mbytes < 0.1) { return '< 0.1'; }
+	else if(mbytes > 1000) { return '> 1000'; }
+	else { return mbytes.toFixed(1); }
+}
 
+function formatDate(date){
+	if(typeof date=='number'){
+		date=new Date(date);
+	}
+	var monthNames = [ t('files','January'), t('files','February'), t('files','March'), t('files','April'), t('files','May'), t('files','June'),
+	t('files','July'), t('files','August'), t('files','September'), t('files','October'), t('files','November'), t('files','December') ];
+	return monthNames[date.getMonth()]+' '+date.getDate()+', '+date.getFullYear()+', '+((date.getHours()<10)?'0':'')+date.getHours()+':'+date.getMinutes();
+}
diff --git a/core/js/multiselect.js b/core/js/multiselect.js
index 96fc09a0759d57edf63ba32d9fd1fa2a3a22e3da..541dddf0f708eb36d44562cdecac0f47dce0cc78 100644
--- a/core/js/multiselect.js
+++ b/core/js/multiselect.js
@@ -12,6 +12,11 @@
 			'minWidth': 'default;',
 		};
 		$.extend(settings,options);
+		$.each(this.children(),function(i,option){
+			if($(option).attr('selected') && settings.checked.indexOf($(option).val())==-1){
+				settings.checked.push($(option).val());
+			}
+		});
 		var button=$('<div class="multiselect button"><span>'+settings.title+'</span><span>â–¾</span></div>');
 		var span=$('<span/>');
 		span.append(button);
@@ -46,9 +51,11 @@
 			});
 			button.addClass('active');
 			event.stopPropagation();
-			var options=$(this).parent().next().children().map(function(){return $(this).val();});
+			var options=$(this).parent().next().children();
 			var list=$('<ul class="multiselectoptions"/>').hide().appendTo($(this).parent());
-			function createItem(item,checked){
+			function createItem(element,checked){
+				element=$(element);
+				var item=element.val();
 				var id='ms'+multiSelectId+'-option-'+item;
 				var input=$('<input id="'+id+'" type="checkbox"/>');
 				var label=$('<label for="'+id+'">'+item+'</label>');
@@ -61,6 +68,7 @@
 				input.change(function(){
 					var groupname=$(this).next().text();
 					if($(this).is(':checked')){
+						element.attr('selected','selected');
 						if(settings.oncheck){
 							if(settings.oncheck(groupname)===false){
 								$(this).attr('checked', false);
@@ -70,6 +78,7 @@
 						settings.checked.push(groupname);
 					}else{
 						var index=settings.checked.indexOf(groupname);
+						element.attr('selected',null);
 						if(settings.onuncheck){
 							if(settings.onuncheck(groupname)===false){
 								$(this).attr('checked',true);
@@ -119,11 +128,11 @@
 							var li=$(this).parent();
 							$(this).remove();
 							li.text('+ '+settings.createText);
-							li.before(createItem($(this).val()));
+							li.before(createItem(this));
+							var select=button.parent().next();
+							select.append($('<option selected="selected" value="'+$(this).val()+'">'+$(this).val()+'</option>'));
 							li.prev().children('input').trigger('click');
 							button.parent().data('preventHide',false);
-							var select=button.parent().next();
-							select.append($('<option value="'+$(this).val()+'">'+$(this).val()+'</option>'));
 							if(settings.createCallback){
 								settings.createCallback();
 							}
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ce2bae1642edd89ffb9eca0d1f6b0d8fc4ed90b
--- /dev/null
+++ b/core/js/oc-dialogs.js
@@ -0,0 +1,149 @@
+/**
+ * ownCloud
+ *
+ * @author Bartek Przybylski
+ * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.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/>.
+ *
+ * todo(bartek): add select option in form
+ */
+
+/**
+ * this class ease usage of jquery dialogs
+ */
+OCdialogs = {
+  /**
+   * displays alert dialog
+   * @param text content of dialog
+   * @param title dialog title
+   * @param callback which will be triggered when user press OK
+   */
+  alert:function(text, title, callback) {
+    var content = '<p><span class="ui-icon ui-icon-alert"></span>'+text+'</p>';
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback);
+  },
+  /**
+   * displays info dialog
+   * @param text content of dialog
+   * @param title dialog title
+   * @param callback which will be triggered when user press OK
+   */
+  info:function(text, title, callback) {
+    var content = '<p><span class="ui-icon ui-icon-info"></span>'+text+'</p>';
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback);
+  },
+  /**
+   * displays confirmation dialog
+   * @param text content of dialog
+   * @param title dialog title
+   * @param callback which will be triggered when user press YES or NO (true or false would be passed to callback respectively)
+   */
+  confirm:function(text, title, callback) {
+    var content = '<p><span class="ui-icon ui-icon-notice"></span>'+text+'</p>';
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback);
+  },
+  /**
+   * prompt for user input
+   * @param text content of dialog
+   * @param title dialog title
+   * @param callback which will be triggered when user press OK (input text will be passed to callback)
+   */
+  prompt:function(text, title, default_value, callback) {
+    var content = '<p><span class="ui-icon ui-icon-pencil"></span>'+text+':<br/><input type="text" id="oc-dialog-prompt-input" value="'+default_value+'" style="width:90%"></p>';
+    OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback);
+  },
+  /**
+   * prompt user for input with custom form
+   * fields should be passed in following format: [{text:'prompt text', name:'return name', type:'input type'},...]
+   * @param fields to display 
+   * @param title dialog title
+   * @param callback which will be triggered when user press OK (user answers will be passed to callback in following format: [{name:'return name', value: 'user value'},...])
+   */
+  form:function(fields, title, callback) {
+    var content = '<table>';
+    for (var a in fields) {
+      content += '<tr><td>'+fields[a].text+'</td><td>';
+      var type=fields[a].type;
+      if (type == 'text' || type == 'checkbox' || type == 'password')
+          content += '<input type="'+type+'" name="'+fields[a].name+'">';
+      content += "</td></tr>"
+    }
+    content += "</table>";
+    OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback);
+  },
+  message:function(content, title, dialog_type, buttons, callback) {
+    var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content';
+    var c_id = '#'+c_name;
+    var d = '<div id="'+c_name+'" title="'+title+'">'+content+'</div>';
+    $('body').append(d);
+    var b = [];
+    switch (buttons) {
+      case OCdialogs.YES_NO_BUTTONS:
+        b[1] = {text: t('dialogs', 'No'), click: function(){ if (callback != undefined) callback(false); $(c_id).dialog('close'); }};
+        b[0] = {text: t('dialogs', 'Yes'), click: function(){ if (callback != undefined) callback(true); $(c_id).dialog('close');}};
+      break;
+      case OCdialogs.OK_CANCEL_BUTTONS:
+        b[1] = {text: t('dialogs', 'Cancel'), click: function(){$(c_id).dialog('close'); }};
+      case OCdialogs.OK_BUTTON: // fallthrough
+        var f;
+        switch(dialog_type) {
+          case OCdialogs.ALERT_DIALOG:
+            f = function(){$(c_id).dialog('close'); };
+          break;
+          case OCdialogs.PROMPT_DIALOG:
+            f = function(){OCdialogs.prompt_ok_handler(callback, c_id)};
+          break;
+          case OCdialogs.FORM_DIALOG:
+            f = function(){OCdialogs.form_ok_handler(callback, c_id)};
+          break;
+        }
+        b[0] = {text: t('dialogs', 'Ok'), click: f};
+      break;
+    }
+    $(c_id).dialog({width: 4*$(document).width()/9, height: $(d).height() + 150, modal: false, buttons: b});
+    OCdialogs.dialogs_counter++;
+  },
+  // dialogs buttons types
+  YES_NO_BUTTONS: 70,
+  OK_BUTTONS: 71,
+  OK_CANCEL_BUTTONS: 72,
+  // dialogs types
+  ALERT_DIALOG: 80,
+  INFO_DIALOG: 81,
+  PROMPT_DIALOG: 82,
+  FORM_DIALOG: 83,
+  dialogs_counter: 0,
+  determineValue: function(element) {
+    switch ($(element).attr('type')) {
+      case 'checkbox': return $(element).attr('checked') != undefined;
+    }
+    return $(element).val();
+  },
+  prompt_ok_handler: function(callback, c_id) { $(c_id).dialog('close'); if (callback != undefined) callback($(c_id + " input#oc-dialog-prompt-input").val()); },
+  form_ok_handler: function(callback, c_id) {
+    if (callback != undefined) {
+      var r = [];
+      var c = 0;
+      $(c_id + ' input').each(function(i, elem) {
+        r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)};
+        c++;
+      });
+      $(c_id).dialog('close');
+      callback(r);
+    } else {
+      $(c_id).dialog('close');
+    }
+  }
+};
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 64353d4d4f45101863b0c610a2b0a6b8bdaea3e9..c048653cce967509503c493e35864a876cc97360 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -44,8 +44,8 @@
 
 			<ul id="settings" class="svg">
 				<img id="expand" class="svg" alt="<?php echo $l->t('Settings');?>" src="<?php echo image_path('', 'actions/settings.svg'); ?>" />
-				<span style="display:none"><?php echo $l->t('Settings');?></span>
-				<div id="expanddiv">
+				<span style="display:none;"><?php echo $l->t('Settings');?></span>
+				<div id="expanddiv" <?php if($_['bodyid'] == 'body-user') echo 'style="display:none;"'; ?>>
 				<?php foreach($_['settingsnavigation'] as $entry):?>
 					<li><a style="background-image:url(<?php echo $entry['icon']; ?>)" href="<?php echo $entry['href']; ?>" title="" <?php if( $entry["active"] ): ?> class="active"<?php endif; ?>><?php echo $entry['name'] ?></a></li>
 				<?php endforeach; ?>
diff --git a/dav.php b/dav.php
new file mode 100644
index 0000000000000000000000000000000000000000..78e2711aec84495b36a8837ac0d44a66f43cdb3c
--- /dev/null
+++ b/dav.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Jakob Sack
+* @copyright 2012 Jakob Sack owncloud@jakobsack.de
+*
+* 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('lib/base.php');
+
+// Backends we always need (auth, principal and files)
+$backends = array(
+	'auth' => new OC_Connector_Sabre_Auth(),
+	'principal' => new OC_Connector_Sabre_Principal()
+);
+
+// Root nodes
+$nodes = array(
+	new Sabre_CalDAV_Principal_Collection($backends['principal'])
+);
+
+// Plugins
+$plugins = array(
+	new Sabre_DAV_Auth_Plugin($backends['auth'],'ownCloud'),
+	new Sabre_DAVACL_Plugin(),
+	new Sabre_DAV_Browser_Plugin(false) // Show something in the Browser, but no upload
+);
+
+// Load the plugins etc we need for usual file sharing
+$backends['lock'] = new OC_Connector_Sabre_Locks();
+$plugins[] = new Sabre_DAV_Locks_Plugin($backends['lock']);
+// Add a RESTful user directory
+// /files/$username/
+if( OC_User::isLoggedIn()){
+	$currentuser = OC_User::getUser();
+	$files = new Sabre_DAV_SimpleCollection('files');
+	foreach( OC_User::getUsers() as $username ){
+		if( $username == $currentuser ){
+			$public = new OC_Connector_Sabre_Directory('.');
+			$files->addChild( new Sabre_DAV_SimpleCollection( $username, $public->getChildren()));
+		}
+		else{
+			$files->addChild(new Sabre_DAV_SimpleCollection( $username ));
+		}
+	}
+	$nodes[] = $files;
+}
+
+// Get the other plugins and nodes
+OC_Hook::emit( 'OC_DAV', 'initialize', array( 'backends' => &$backends, 'nodes' => &$nodes, 'plugins' => &$plugins ));
+
+// Fire up server
+$server = new Sabre_DAV_Server($nodes);
+$server->setBaseUri(OC::$WEBROOT.'/dav.php');
+
+// Load additional plugins
+foreach( $plugins as &$plugin ){
+	$server->addPlugin( $plugin );
+} unset( $plugin ); // Always do this after foreach with references!
+
+// And off we go!
+$server->exec();
diff --git a/db_structure.xml b/db_structure.xml
index 8e59a59c6e51e354c16d56499fdc9219a4820458..5eef44d8e845a3bf589d3fc4869b7924aba0045e 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -31,12 +31,12 @@
 
    <field>
     <name>configvalue</name>
-    <type>text</type>
-    <default></default>
+    <type>clob</type>
     <notnull>true</notnull>
-    <length>255</length>
    </field>
 
+
+
   </declaration>
 
  </table>
@@ -55,7 +55,7 @@
 	   <notnull>true</notnull>
 	   <length>4</length>
    </field>
-   
+
    <field>
     <name>path</name>
     <type>text</type>
@@ -70,7 +70,7 @@
 	   <default>
 	   </default>
 	   <notnull>true</notnull>
-	   <length>4</length>
+	   <length>8</length>
    </field>
 
    <field>
@@ -90,13 +90,13 @@
 	   <notnull>true</notnull>
 	   <length>64</length>
    </field>
-   
+
    <field>
     <name>size</name>
     <type>integer</type>
     <default></default>
     <notnull>true</notnull>
-    <length>4</length>
+    <length>8</length>
    </field>
 
    <field>
@@ -105,7 +105,7 @@
 	   <default>
 	   </default>
 	   <notnull>true</notnull>
-	   <length>4</length>
+	   <length>8</length>
    </field>
 
    <field>
@@ -114,7 +114,7 @@
 	   <default>
 	   </default>
 	   <notnull>true</notnull>
-	   <length>4</length>
+	   <length>8</length>
    </field>
 
    <field>
@@ -291,7 +291,7 @@
     <type>integer</type>
     <default></default>
     <notnull>false</notnull>
-    <length>4</length>
+    <length>8</length>
    </field>
 
    <field>
@@ -427,102 +427,6 @@
 
  </table>
 
- <table>
-
-  <name>*dbprefix*principalgroups</name>
-
-  <declaration>
-
-   <field>
-    <name>id</name>
-    <type>integer</type>
-    <default>0</default>
-    <notnull>true</notnull>
-    <autoincrement>1</autoincrement>
-    <unsigned>true</unsigned>
-    <length>4</length>
-   </field>
-
-   <field>
-    <name>principal_id</name>
-    <type>integer</type>
-    <default></default>
-    <notnull>true</notnull>
-    <unsigned>true</unsigned>
-    <length>4</length>
-   </field>
-
-   <field>
-    <name>member_id</name>
-    <type>integer</type>
-    <default></default>
-    <notnull>true</notnull>
-    <unsigned>true</unsigned>
-    <length>4</length>
-   </field>
-
-   <index>
-    <name>principals_members_index</name>
-    <unique>true</unique>
-    <field>
-     <name>principal_id</name>
-     <sorting>ascending</sorting>
-    </field>
-    <field>
-     <name>member_id</name>
-     <sorting>ascending</sorting>
-    </field>
-   </index>
-
-  </declaration>
-
- </table>
-
- <table>
-
-  <name>*dbprefix*principals</name>
-
-  <declaration>
-
-   <field>
-    <name>id</name>
-    <type>integer</type>
-    <default>0</default>
-    <notnull>true</notnull>
-    <autoincrement>1</autoincrement>
-    <unsigned>true</unsigned>
-    <length>4</length>
-   </field>
-
-   <field>
-    <name>uri</name>
-    <type>text</type>
-    <default></default>
-    <notnull>false</notnull>
-    <length>255</length>
-   </field>
-
-   <field>
-    <name>displayname</name>
-    <type>text</type>
-    <default></default>
-    <notnull>false</notnull>
-    <length>255</length>
-   </field>
-
-   <index>
-    <name>uri</name>
-    <unique>true</unique>
-    <field>
-     <name>uri</name>
-     <sorting>ascending</sorting>
-    </field>
-   </index>
-
-  </declaration>
-
- </table>
- 
  <table>
 
   <name>*dbprefix*properties</name>
diff --git a/files/ajax/newfile.php b/files/ajax/newfile.php
index afc444bc0acadbcb35247ac4691d44c255106bc9..2d1372f06ee5fb8d40c57233fd6b964d9a9d6d5e 100644
--- a/files/ajax/newfile.php
+++ b/files/ajax/newfile.php
@@ -6,15 +6,35 @@ require_once('../../lib/base.php');
 OC_JSON::checkLoggedIn();
 
 // Get the params
-$dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : '';
-$filename = isset( $_GET['filename'] ) ? stripslashes($_GET['filename']) : '';
-$content = isset( $_GET['content'] ) ? $_GET['content'] : '';
+$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : '';
+$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : '';
+$content = isset( $_POST['content'] ) ? $_POST['content'] : '';
+$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : '';
 
 if($filename == '') {
 	OC_JSON::error(array("data" => array( "message" => "Empty Filename" )));
 	exit();
 }
 
+if($source){
+	if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){
+		OC_JSON::error(array("data" => array( "message" => "Not a valid source" )));
+		exit();
+	}
+	$sourceStream=fopen($source,'rb');
+	$target=$dir.'/'.$filename;
+	$result=OC_Filesystem::file_put_contents($target,$sourceStream);
+	if($result){
+		$mime=OC_Filesystem::getMimetype($target);
+		OC_JSON::success(array("data" => array('mime'=>$mime)));
+		exit();
+	}else{
+		OC_JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target )));
+		exit();
+	}
+}
+
+
 if(OC_Files::newFile($dir, $filename, 'file')) {
 	if($content){
 		OC_Filesystem::file_put_contents($dir.'/'.$filename,$content);
diff --git a/files/ajax/newfolder.php b/files/ajax/newfolder.php
index 6db045c4e1728fc7d555f5c8bd73fe09d798f46c..228e369fbef95c336dfd6426470a9a0951f2bbb9 100644
--- a/files/ajax/newfolder.php
+++ b/files/ajax/newfolder.php
@@ -6,8 +6,8 @@ require_once('../../lib/base.php');
 OC_JSON::checkLoggedIn();
 
 // Get the params
-$dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : '';
-$foldername = isset( $_GET['foldername'] ) ? stripslashes($_GET['foldername']) : '';
+$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : '';
+$foldername = isset( $_POST['foldername'] ) ? stripslashes($_POST['foldername']) : '';
 
 if(trim($foldername) == '') {
 	OC_JSON::error(array("data" => array( "message" => "Empty Foldername" )));
diff --git a/files/ajax/upload.php b/files/ajax/upload.php
index 241edc216ff428479c073cd94ec6b91f82ed964e..f8b8f0e2e5fcda79ef0f557390736b85d99cfa3b 100644
--- a/files/ajax/upload.php
+++ b/files/ajax/upload.php
@@ -16,12 +16,13 @@ foreach ($_FILES['files']['error'] as $error) {
 	if ($error != 0) {
 		$l=new OC_L10N('files');
 		$errors = array(
-			0=>$l->t("There is no error, the file uploaded with success"),
-			1=>$l->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
-			2=>$l->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
-			3=>$l->t("The uploaded file was only partially uploaded"),
-			4=>$l->t("No file was uploaded"),
-			6=>$l->t("Missing a temporary folder")
+			UPLOAD_ERR_OK=>$l->t("There is no error, the file uploaded with success"),
+			UPLOAD_ERR_INI_SIZE=>$l->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
+			UPLOAD_ERR_FORM_SIZE=>$l->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
+			UPLOAD_ERR_PARTIAL=>$l->t("The uploaded file was only partially uploaded"),
+			UPLOAD_ERR_NO_FILE=>$l->t("No file was uploaded"),
+			UPLOAD_ERR_NO_TMP_DIR=>$l->t("Missing a temporary folder"),
+			UPLOAD_ERR_CANT_WRITE=>$l->t('Failed to write to disk'),
 		);
 		OC_JSON::error(array("data" => array( "message" => $errors[$error] )));
 		exit();
@@ -48,7 +49,8 @@ if(strpos($dir,'..') === false){
 	for($i=0;$i<$fileCount;$i++){
 		$target=stripslashes($dir) . $files['name'][$i];
 		if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i],$target)){
-			$result[]=array( "status" => "success", 'mime'=>OC_Filesystem::getMimeType($target),'size'=>OC_Filesystem::filesize($target),'name'=>$files['name'][$i]);
+			$meta=OC_FileCache::getCached($target);
+			$result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'],'name'=>$files['name'][$i]);
 		}
 	}
 	OC_JSON::encodedPrint($result);
diff --git a/files/appinfo/app.php b/files/appinfo/app.php
index e434296b2501daa627c27e47586e01d5f9c09e67..0bf73d9a07e24474525d04cd066cf395a0c5ec8a 100644
--- a/files/appinfo/app.php
+++ b/files/appinfo/app.php
@@ -7,4 +7,4 @@ OC_App::register( array( "order" => 2, "id" => "files", "name" => "Files" ));
 
 OC_App::addNavigationEntry( array( "id" => "files_index", "order" => 1, "href" => OC_Helper::linkTo( "files", "index.php" ), "icon" => OC_Helper::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") ));
 
-?>
+OC_Search::registerProvider('OC_Search_Provider_File');
diff --git a/files/index.php b/files/index.php
index a29d3fb7e1e5a65ac9dc0788549a08120b5bfcbb..79261e495be0bbd77552ba263d7e467737adcc9c 100644
--- a/files/index.php
+++ b/files/index.php
@@ -40,7 +40,7 @@ OC_App::setActiveNavigationEntry( "files_index" );
 // Load the files
 $dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : '';
 // Redirect if directory does not exist
-if(!OC_Filesystem::is_dir($dir)) {
+if(!OC_Filesystem::is_dir($dir.'/')) {
 	header("Location: ".$_SERVER['PHP_SELF']."");
 }
 
diff --git a/files/js/files.js b/files/js/files.js
index bebcf4e97a4da42287b1fce66e193ce1fade526a..a678e12cc2d42aaac6fbf9fd7c3a14b0c5781954 100644
--- a/files/js/files.js
+++ b/files/js/files.js
@@ -127,20 +127,6 @@ $(document).ready(function() {
 		procesSelection();
 	});
 	
-	$('#file_newfolder_form').submit(function(event) {
-		event.preventDefault();
-		$.ajax({
-			url: 'ajax/newfolder.php',
-			data: "dir="+$('#dir').val()+"&foldername="+$('#file_newfolder_name').val(),
-			complete: function(data){boolOperationFinished(data, function(){
-				var date=new Date();
-				FileList.addDir($('#file_newfolder_name').val(),0,date);
-				$('#file_newfolder_name').val('New Folder');
-				$('#file_newfolder_name').blur();
-			});}
-		});
-	});
-	
 	$('#file_newfolder_name').click(function(){
 		if($('#file_newfolder_name').val() == 'New Folder'){
 			$('#file_newfolder_name').val('');
@@ -166,6 +152,7 @@ $(document).ready(function() {
 
 	$('.file_upload_start').live('change',function(){
 		var form=$(this).closest('form');
+		var that=this;
 		var uploadId=form.attr('data-upload-id');
 		var files=this.files;
 		var target=form.children('iframe');
@@ -173,6 +160,12 @@ $(document).ready(function() {
 		if(files){
 			for(var i=0;i<files.length;i++){
 				totalSize+=files[i].size;
+				if(FileList.deleteFiles && FileList.deleteFiles.indexOf(files[i].name)!=-1){//finish delete if we are uploading a deleted file
+					FileList.finishDelete(function(){
+						$(that).change();
+					});
+					return;
+				}
 			}
 		}
 		if(totalSize>$('#max_upload').val()){
@@ -305,10 +298,10 @@ $(document).ready(function() {
 			var name=$(this).val();
 			switch(type){
 				case 'file':
-					$.ajax({
-						url: OC.filePath('files','ajax','newfile.php'),
-						data: "dir="+encodeURIComponent($('#dir').val())+"&filename="+encodeURIComponent(name)+'&content=%20%0A',
-						complete: function(data){boolOperationFinished(data, function(){
+					$.post(
+						OC.filePath('files','ajax','newfile.php'),
+						{dir:$('#dir').val(),filename:name,content:" \n"},
+						function(data){
 							var date=new Date();
 							FileList.addFile(name,0,date);
 							var tr=$('tr').filterAttr('data-file',name);
@@ -316,18 +309,49 @@ $(document).ready(function() {
 							getMimeIcon('text/plain',function(path){
 								tr.find('td.filename').attr('style','background-image:url('+path+')');
 							});
-						});}
-					});
+						}
+					);
 					break;
 				case 'folder':
-					$.ajax({
-						url: OC.filePath('files','ajax','newfolder.php'),
-						data: "dir="+encodeURIComponent($('#dir').val())+"&foldername="+encodeURIComponent(name),
-						complete: function(data){boolOperationFinished(data, function(){
+					$.post(
+						OC.filePath('files','ajax','newfolder.php'),
+						{dir:$('#dir').val(),foldername:name},
+						function(data){
 							var date=new Date();
 							FileList.addDir(name,0,date);
-						});}
-					});
+						}
+					);
+					break;
+				case 'web':
+					if(name.substr(0,8)!='https://' && name.substr(0,7)!='http://'){
+						name='http://'.name;
+					}
+					var localName=name;
+					if(localName.substr(localName.length-1,1)=='/'){//strip /
+						localName=localName.substr(0,localName.length-1)
+					}
+					if(localName.indexOf('/')){//use last part of url
+						localName=localName.split('/').pop();
+					}else{//or the domain
+						localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.','');
+					}
+					$.post(
+						OC.filePath('files','ajax','newfile.php'),
+						{dir:$('#dir').val(),source:name,filename:localName},
+						function(result){
+							if(result.status == 'success'){
+								var date=new Date();
+								FileList.addFile(localName,0,date);
+								var tr=$('tr').filterAttr('data-file',localName);
+								tr.data('mime',result.data.mime);
+								getMimeIcon(result.data.mime,function(path){
+									tr.find('td.filename').attr('style','background-image:url('+path+')');
+								});
+							}else{
+								
+							}
+						}
+					);
 					break;
 			}
 			var li=$(this).parent();
@@ -380,39 +404,6 @@ function updateBreadcrumb(breadcrumbHtml) {
 	$('p.nav').empty().html(breadcrumbHtml);
 }
 
-function humanFileSize(bytes){
-	if( bytes < 1024 ){
-		return bytes+' B';
-	}
-	bytes = Math.round(bytes / 1024, 1 );
-	if( bytes < 1024 ){
-		return bytes+' kB';
-	}
-	bytes = Math.round( bytes / 1024, 1 );
-	if( bytes < 1024 ){
-		return bytes+' MB';
-	}
-	
-	// Wow, heavy duty for owncloud
-	bytes = Math.round( bytes / 1024, 1 );
-	return bytes+' GB';
-}
-
-function simpleFileSize(bytes) {
-	mbytes = Math.round(bytes/(1024*1024/10))/10;
-	if(bytes == 0) { return '0'; }
-	else if(mbytes < 0.1) { return '< 0.1'; }
-	else if(mbytes > 1000) { return '> 1000'; }
-	else { return mbytes.toFixed(1); }
-}
-
-function formatDate(date){
-	var monthNames = [ t('files','January'), t('files','February'), t('files','March'), t('files','April'), t('files','May'), t('files','June'),
-	t('files','July'), t('files','August'), t('files','September'), t('files','October'), t('files','November'), t('files','December') ];
-	return monthNames[date.getMonth()]+' '+date.getDate()+', '+date.getFullYear()+', '+((date.getHours()<10)?'0':'')+date.getHours()+':'+date.getMinutes();
-}
-
-
 //options for file drag/dropp
 var dragOptions={
 	distance: 20, revert: 'invalid', opacity: 0.7,
diff --git a/files/templates/index.php b/files/templates/index.php
index 7e9505dec2f936435791c8eea86457c0c91bd143..7fc51c288e1c7402c6edbb3df47281d71f9939f6 100644
--- a/files/templates/index.php
+++ b/files/templates/index.php
@@ -7,6 +7,7 @@
 				<ul class="popup popupTop">
 					<li style="background-image:url('<?php echo mimetype_icon('text/plain') ?>')" data-type='file'><p><?php echo $l->t('Text file');?></p></li>
 					<li style="background-image:url('<?php echo mimetype_icon('dir') ?>')" data-type='folder'><p><?php echo $l->t('Folder');?></p></li>
+					<li style="background-image:url('<?php echo image_path('core','actions/public.png') ?>')" data-type='web'><p><?php echo $l->t('From url');?></p></li>
 				</ul>
 			</div>
 			<div class="file_upload_wrapper svg">
diff --git a/lib/app.php b/lib/app.php
index 1879a89cee34d6228ad938dd0b65455d825f99ce..f841180ebad299446c9e57147e70c5c80aed9050 100644
--- a/lib/app.php
+++ b/lib/app.php
@@ -58,8 +58,8 @@ class OC_App{
 		$apps = OC_Appconfig::getApps();
 		foreach( $apps as $app ){
 			if( self::isEnabled( $app )){
-				if(is_file(OC::$SERVERROOT.'/apps/'.$app.'/appinfo/app.php')){
-					require( 'apps/'.$app.'/appinfo/app.php' );
+				if(is_file(OC::$APPSROOT.'/apps/'.$app.'/appinfo/app.php')){
+					require( $app.'/appinfo/app.php' );
 				}
 			}
 		}
@@ -268,7 +268,7 @@ class OC_App{
 		if(is_file($appid)){
 			$file=$appid;
 		}else{
-			$file=OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/info.xml';
+			$file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
 			if(!is_file($file)){
 				return array();
 			}
@@ -363,9 +363,9 @@ class OC_App{
 	 */
 	public static function getAllApps(){
 		$apps=array();
-		$dh=opendir(OC::$SERVERROOT.'/apps');
+		$dh=opendir(OC::$APPSROOT.'/apps');
 		while($file=readdir($dh)){
-			if(is_file(OC::$SERVERROOT.'/apps/'.$file.'/appinfo/app.php')){
+			if(substr($file,0,1)!='.' and is_file(OC::$APPSROOT.'/apps/'.$file.'/appinfo/app.php')){
 				$apps[]=$file;
 			}
 		}
@@ -396,11 +396,11 @@ class OC_App{
 	 * @param string appid
 	 */
 	public static function updateApp($appid){
-		if(file_exists(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml')){
-			OC_DB::updateDbFromStructure(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml');
+		if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml')){
+			OC_DB::updateDbFromStructure(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml');
 		}
-		if(file_exists(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/update.php')){
-			include OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/update.php';
+		if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php')){
+			include OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php';
 		}
 	}
 
diff --git a/lib/base.php b/lib/base.php
index 880645ff79d124c70b45ebf0dcb552a05da0adfc..9995544f14e7854aa3924a9c0dfb7c15ddd4ee7e 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -54,6 +54,22 @@ class OC{
 	 * the folder that stores the data for the root filesystem (e.g. /srv/http/owncloud/data)
 	 */
 	public static $CONFIG_DATADIRECTORY_ROOT = '';
+	/**
+	 * The installation path of the 3rdparty folder on the server (e.g. /srv/http/owncloud/3rdparty)
+	 */
+	public static $THIRDPARTYROOT = '';
+	/**
+	 * the root path of the 3rdparty folder for http requests (e.g. owncloud/3rdparty)
+	 */
+	public static $THIRDPARTYWEBROOT = '';
+        /**
+         * The installation path of the apps folder on the server (e.g. /srv/http/owncloud)
+         */
+        public static $APPSROOT = '';
+        /**
+         * the root path of the apps folder for http requests (e.g. owncloud)
+         */
+        public static $APPSWEBROOT = '';
 
 	/**
 	 * SPL autoload
@@ -128,6 +144,12 @@ class OC{
 			$_SERVER['PHP_AUTH_PW'] = strip_tags($password);
 		}
 
+		// register the stream wrappers
+		require_once('streamwrappers.php');
+		stream_wrapper_register("fakedir", "OC_FakeDirStream");
+		stream_wrapper_register('static', 'OC_StaticStreamWrapper');
+		stream_wrapper_register('close', 'OC_CloseStreamWrapper');
+		
 		// calculate the documentroot
 		OC::$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']);
 		OC::$SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-13));
@@ -135,15 +157,60 @@ class OC{
 		$scriptName=$_SERVER["SCRIPT_NAME"];
 		if(substr($scriptName,-1)=='/'){
 			$scriptName.='index.php';
+			//make sure suburi follows the same rules as scriptName
+			if(substr(OC::$SUBURI,-9)!='index.php'){
+				if(substr(OC::$SUBURI,-1)!='/'){
+					OC::$SUBURI=OC::$SUBURI.'/';
+				}
+				OC::$SUBURI=OC::$SUBURI.'index.php';
+			}
 		}
-		OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI));
+                OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI));
+		// try a new way to detect the WEBROOT which is simpler and also works with the app directory outside the owncloud folder. let´s see if this works for everybody
+//		OC::$WEBROOT=substr(OC::$SERVERROOT,strlen(OC::$DOCUMENTROOT));
+
 
 		if(OC::$WEBROOT!='' and OC::$WEBROOT[0]!=='/'){
 			OC::$WEBROOT='/'.OC::$WEBROOT;
 		}
 
+		// search the 3rdparty folder
+		if(OC_Config::getValue('3rdpartyroot', '')<>'' and OC_Config::getValue('3rdpartyurl', '')<>''){
+			OC::$THIRDPARTYROOT=OC_Config::getValue('3rdpartyroot', '');
+			OC::$THIRDPARTYWEBROOT=OC_Config::getValue('3rdpartyurl', '');
+		}elseif(file_exists(OC::$SERVERROOT.'/3rdparty')){
+			OC::$THIRDPARTYROOT=OC::$SERVERROOT;
+			OC::$THIRDPARTYWEBROOT=OC::$WEBROOT;
+		}elseif(file_exists(OC::$SERVERROOT.'/../3rdparty')){
+			OC::$THIRDPARTYWEBROOT=rtrim(dirname(OC::$WEBROOT), '/');
+			OC::$THIRDPARTYROOT=rtrim(dirname(OC::$SERVERROOT), '/');
+		}else{
+			echo("3rdparty directory not found! Please put the ownCloud 3rdparty folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file.");
+			exit;
+		}
+
+		// search the apps folder
+		if(file_exists(OC::$SERVERROOT.'/apps')){
+			OC::$APPSROOT=OC::$SERVERROOT;
+			OC::$APPSWEBROOT=OC::$WEBROOT;
+		}elseif(file_exists(OC::$SERVERROOT.'/../apps')){
+			OC::$APPSWEBROOT=rtrim(dirname(OC::$WEBROOT), '/');
+			OC::$APPSROOT=rtrim(dirname(OC::$SERVERROOT), '/');
+		}else{
+			echo("apps directory not found! Please put the ownCloud apps folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file.");
+			exit;
+		}
+
 		// set the right include path
-		set_include_path(OC::$SERVERROOT.'/lib'.PATH_SEPARATOR.OC::$SERVERROOT.'/config'.PATH_SEPARATOR.OC::$SERVERROOT.'/3rdparty'.PATH_SEPARATOR.get_include_path().PATH_SEPARATOR.OC::$SERVERROOT);
+		set_include_path(
+			OC::$SERVERROOT.'/lib'.PATH_SEPARATOR.
+			OC::$SERVERROOT.'/config'.PATH_SEPARATOR.
+			OC::$THIRDPARTYROOT.'/3rdparty'.PATH_SEPARATOR.
+			OC::$APPSROOT.PATH_SEPARATOR.
+			OC::$APPSROOT.'/apps'.PATH_SEPARATOR.
+			get_include_path().PATH_SEPARATOR.
+			OC::$SERVERROOT
+		);
 
 		// Redirect to installer if not installed
 		if (!OC_Config::getValue('installed', false) && OC::$SUBURI != '/index.php') {
@@ -203,8 +270,10 @@ class OC{
 		OC_Util::addScript( "jquery-showpassword" );
 		OC_Util::addScript( "jquery.infieldlabel.min" );
 		OC_Util::addScript( "jquery-tipsy" );
+		OC_Util::addScript( "oc-dialogs" );
 		OC_Util::addScript( "js" );
 		OC_Util::addScript( "eventsource" );
+		OC_Util::addScript( "config" );
 		//OC_Util::addScript( "multiselect" );
 		OC_Util::addScript('search','result');
 		OC_Util::addStyle( "styles" );
@@ -230,6 +299,7 @@ class OC{
 			$_SESSION['user_id'] = '';
 		}
 
+
 		OC_User::useBackend( OC_Config::getValue( "userbackend", "database" ));
 		OC_Group::setBackend( OC_Config::getValue( "groupbackend", "database" ));
 
@@ -246,9 +316,8 @@ class OC{
 			OC_App::loadApps();
 		}
 
-		// Last part: connect some hooks
-		OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Connector_Sabre_Principal', 'addPrincipal');
-		OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Connector_Sabre_Principal', 'deletePrincipal');
+		//make sure temporary files are cleaned up
+		register_shutdown_function(array('OC_Helper','cleanTmp'));
 	}
 }
 
@@ -271,15 +340,10 @@ if(!function_exists('get_temp_dir')) {
 			unlink($temp);
 			return dirname($temp);
 		}
+		if( $temp=sys_get_temp_dir())    return $temp;
+		
 		return null;
 	}
 }
 
 OC::init();
-
-require_once('fakedirstream.php');
-
-
-
-// FROM search.php
-new OC_Search_Provider_File();
diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php
index 1e87c7cee08422484a229fc2e1b93b02c43447a9..8964ef7d0de45d21ec55e47edf5d913486a6d70b 100644
--- a/lib/connector/sabre/auth.php
+++ b/lib/connector/sabre/auth.php
@@ -23,6 +23,7 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
 	 * @return bool
 	 */
 	protected function validateUserPass($username, $password){
+		OC_Util::setUpFS();//login hooks may need early access to the filesystem
 		if(OC_User::login($username,$password)){
 			OC_Util::setUpFS();
 			return true;
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
index bb03851e39d7484db4c6a0411bdcc6b9528ef261..cc37bf22d5dae2e15257e893b8b0a348ebcfda80 100644
--- a/lib/connector/sabre/directory.php
+++ b/lib/connector/sabre/directory.php
@@ -73,8 +73,8 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
 
 		$nodes = array();
 		// foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
-		if( OC_Filesystem::is_dir($this->path)){
-			$dh = OC_Filesystem::opendir($this->path);
+		if( OC_Filesystem::is_dir($this->path . '/')){
+			$dh = OC_Filesystem::opendir($this->path . '/');
 			while(( $node = readdir($dh)) !== false ){
 				if($node!='.' && $node!='..'){
 					$nodes[] = $this->getChild($node);
diff --git a/lib/connector/sabre/principal.php b/lib/connector/sabre/principal.php
index 72e180c65c0d6ab0e7306884b015a34670aee436..28a36438e8767d5c4d1fb4dd9fdcb5ec441a6b4c 100644
--- a/lib/connector/sabre/principal.php
+++ b/lib/connector/sabre/principal.php
@@ -8,50 +8,6 @@
  */
 
 class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend {
-	/**
-	 * TODO: write doc
-	 */
-	public static function addPrincipal($params){
-		// Add the user
-		$uri = 'principals/'.$params['uid'];
-		$displayname = $params['uid'];
-		$query = OC_DB::prepare('INSERT INTO *PREFIX*principals (uri,displayname) VALUES(?,?)');
-		$query->execute(array($uri,$displayname));
-		
-		// Add calendar and addressbook read and write support (sharing calendars)
-		$uri = 'principals/'.$params['uid'].'/calendar-proxy-read';
-		$displayname = null;
-		$query->execute(array($uri,$displayname));
-		$uri = 'principals/'.$params['uid'].'/calendar-proxy-write';
-		$query->execute(array($uri,$displayname));
-		$uri = 'principals/'.$params['uid'].'/addressbook-proxy-read';
-		$query->execute(array($uri,$displayname));
-		$uri = 'principals/'.$params['uid'].'/addressbook-proxy-write';
-		$query->execute(array($uri,$displayname));
-
-		return true;
-	}
-	
-	/**
-	 * TODO: write doc
-	 */
-	public static function deletePrincipal($params){
-		$query = OC_DB::prepare('SELECT * FROM *PREFIX*principals');
-		$result = $query->execute();
-
-		$deleteprincipal = OC_DB::prepare('DELETE FROM *PREFIX*principals WHERE id = ?');
-		$deletegroup = OC_DB::prepare('DELETE FROM *PREFIX*principalgroups WHERE principal_id = ? OR member_id = ?');
-		// We have to delete the principals and relations! Principals include 
-		while($row = $result->fetchRow()){
-			// Checking if the principal is in the prefix
-			$array = explode('/',$row['uri']);
-			if ($array[1] != $params['uid']) continue;
-			$deleteprincipal->execute(array($row['id']));
-			$deletegroup->execute(array($row['id'],$row['id']));
-		}
-		return true;
-	}
-
 	/**
 	 * Returns a list of principals based on a prefix.
 	 *
diff --git a/lib/db.php b/lib/db.php
index 9d3c20e0145873808af24e2ba01f880bc61b2235..9fab51edfcd5420c7f860ec4aeecb46e97c8dec0 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -316,8 +316,11 @@ class OC_DB {
 		// read file
 		$content = file_get_contents( $file );
 		
-		// Make changes and save them to a temporary file
-		$file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' );
+		// Make changes and save them to an in-memory file
+		$file2 = 'static://db_scheme';
+		if($file2 == ''){
+			die('could not create tempfile in get_temp_dir() - aborting');
+		}
 		$content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
 		$content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
 		if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't
@@ -328,7 +331,7 @@ class OC_DB {
 		// Try to create tables
 		$definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
 		
-		// Delete our temporary file
+		//clean up memory
 		unlink( $file2 );
 
 		// Die in case something went wrong
@@ -368,8 +371,8 @@ class OC_DB {
 			return false;
 		}
 
-		// Make changes and save them to a temporary file
-		$file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' );
+		// Make changes and save them to an in-memory file
+		$file2 = 'static://db_scheme';
 		$content = str_replace( '*dbname*', $previousSchema['name'], $content );
 		$content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
 		if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't
@@ -378,7 +381,7 @@ class OC_DB {
 		file_put_contents( $file2, $content );
 		$op = self::$schema->updateDatabase($file2, $previousSchema, array(), false);
 		
-		// Delete our temporary file
+		//clean up memory
 		unlink( $file2 );
 		
 		if (PEAR::isError($op)) {
@@ -505,6 +508,21 @@ class OC_DB {
 		self::$connection->commit();
 		self::$inTransaction=false;
 	}
+
+	/**
+	 * check if a result is an error, works with MDB2 and PDOException
+	 * @param mixed $result
+	 * @return bool
+	 */
+	public static function isError($result){
+		if(!$result){
+			return true;
+		}elseif(self::$backend==self::BACKEND_MDB2 and PEAR::isError($result)){
+			return true;
+		}else{
+			return false;
+		}
+	}
 }
 
 /**
@@ -524,11 +542,15 @@ class PDOStatementWrapper{
 	public function execute($input=array()){
 		$this->lastArguments=$input;
 		if(count($input)>0){
-			$this->statement->execute($input);
+			$result=$this->statement->execute($input);
+		}else{
+			$result=$this->statement->execute();
+		}
+		if($result){
+			return $this;
 		}else{
-			$this->statement->execute();
+			return false;
 		}
-		return $this;
 	}
 	
 	/**
diff --git a/lib/fakedirstream.php b/lib/fakedirstream.php
deleted file mode 100644
index fa3e64da62c0bd4e44c2141113c79c8d158a7a75..0000000000000000000000000000000000000000
--- a/lib/fakedirstream.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-global $FAKEDIRS;
-$FAKEDIRS=array();
-
-class fakeDirStream{
-	private $name;
-	private $data;
-	private $index;
-
-	public function dir_opendir($path,$options){
-		global $FAKEDIRS;
-		$url=parse_url($path);
-		$this->name=substr($path,strlen('fakedir://'));
-		$this->index=0;
-		if(isset($FAKEDIRS[$this->name])){
-			$this->data=$FAKEDIRS[$this->name];
-		}else{
-			$this->data=array();
-		}
-		return true;
-	}
-
-	public function dir_readdir(){
-		if($this->index>=count($this->data)){
-			return false;
-		}
-		$filename=$this->data[$this->index];
-		$this->index++;
-		return $filename;
-	}
-
-	public function dir_closedir() {
-		$this->data=false;
-		$this->name='';
-		return true;
-	}
-
-	public function dir_rewinddir() {
-		$this->index=0;
-		return true;
-	}
-}
- 
-stream_wrapper_register("fakedir", "fakeDirStream");
-
diff --git a/lib/filecache.php b/lib/filecache.php
index 6ae2f8253dbaa3a31851eafd6fe8d9c813e70408..be2a00a19a0a94e82f913e2e835f1a264be46d0f 100644
--- a/lib/filecache.php
+++ b/lib/filecache.php
@@ -28,6 +28,8 @@
  * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache
  */
 class OC_FileCache{
+	private static $savedData=array();
+	
 	/**
 	 * get the filesystem info from the cache
 	 * @param string path
@@ -93,6 +95,14 @@ class OC_FileCache{
 			self::update($id,$data);
 			return;
 		}
+		if(isset(self::$savedData[$path])){
+			$data=array_merge($data,self::$savedData[$path]);
+			unset(self::$savedData[$path]);
+		}
+		if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it
+			self::$savedData[$path]=$data;
+			return;
+		}
 		if(!isset($data['encrypted'])){
 			$data['encrypted']=false;
 		}
@@ -101,9 +111,11 @@ class OC_FileCache{
 		}
 		$mimePart=dirname($data['mimetype']);
 		$user=OC_User::getUser();
-		$query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable) VALUES(?,?,?,?,?,?,?,?,?,?)');
-		$query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable']));
-		
+		$query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)');
+		$result=$query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
+		if(OC_DB::isError($result)){
+			OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR);
+		}
 	}
 
 	/**
@@ -128,7 +140,10 @@ class OC_FileCache{
 		
 		$sql = 'UPDATE *PREFIX*fscache SET '.implode(' , ',$queryParts).' WHERE id=?';
 		$query=OC_DB::prepare($sql);
-		$query->execute($arguments);
+		$result=$query->execute($arguments);
+		if(OC_DB::isError($result)){
+			OC_Log::write('files','error while updating file('.$path.') in cache',OC_Log::ERROR);
+		}
 	}
 
 	/**
@@ -153,19 +168,28 @@ class OC_FileCache{
 
 	/**
 	 * delete info from the cache
-	 * @param string $path
+	 * @param string/int $file
 	 * @param string root (optional)
 	 */
-	public static function delete($path,$root=''){
-		if(!$root){
-			$root=OC_Filesystem::getRoot();
-		}
-		if($root=='/'){
-			$root='';
+	public static function delete($file,$root=''){
+		if(!is_numeric($file)){
+			if(!$root){
+				$root=OC_Filesystem::getRoot();
+			}
+			if($root=='/'){
+				$root='';
+			}
+			$path=$root.$file;
+			self::delete(self::getFileId($path));
+		}elseif($file!=-1){
+			$query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE parent=?');
+			$query->execute(array($file));
+			while($child=$query->fetchRow()){
+				self::delete(intval($child['id']));
+			}
+			$query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE id=?');
+			$query->execute(array($file));
 		}
-		$path=$root.$path;
-		$query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path=?');
-		$query->execute(array($path));
 	}
 	
 	/**
@@ -262,11 +286,20 @@ class OC_FileCache{
 	 */
 	private static function getFileId($path){
 		$query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?');
-		$result=$query->execute(array($path))->fetchRow();
+		if(OC_DB::isError($query)){
+			OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
+			return -1;
+		}
+		$result=$query->execute(array($path));
+		if(OC_DB::isError($result)){
+			OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
+			return -1;
+		}
+		$result=$result->fetchRow();
 		if(is_array($result)){
 			return $result['id'];
 		}else{
-			OC_Log::write('getFieldId(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
+			OC_Log::write('getFileId(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
 			return -1;
 		}
 	}
@@ -299,10 +332,11 @@ class OC_FileCache{
 		$path=$params['path'];
 		$fullPath=$view->getRoot().$path;
 		$mimetype=$view->getMimeType($path);
+		$dir=$view->is_dir($path.'/');
 		//dont use self::get here, we don't want inifinte loops when a file has changed
 		$cachedSize=self::getCachedSize($path,$root);
 		$size=0;
-		if($mimetype=='httpd/unix-directory'){
+		if($dir){
 			if(self::inCache($path,$root)){
 				$parent=self::getFileId($fullPath);
 				$query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE parent=?');
@@ -323,7 +357,29 @@ class OC_FileCache{
 		}
 		self::increaseSize(dirname($fullPath),$size-$cachedSize);
 	}
-
+	
+	public static function getCached($path,$root=''){
+		if(!$root){
+			$root=OC_Filesystem::getRoot();
+		}else{
+			if($root=='/'){
+				$root='';
+			}
+		}
+		$path=$root.$path;
+		$query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?');
+		$result=$query->execute(array($path))->fetchRow();
+		if(is_array($result)){
+			if(isset(self::$savedData[$path])){
+				$result=array_merge($result,self::$savedData[$path]);
+			}
+			return $result;
+		}else{
+			OC_Log::write('get(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
+			return false;
+		}
+	}
+	
 	private static function getCachedSize($path,$root){
 		if(!$root){
 			$root=OC_Filesystem::getRoot();
@@ -332,6 +388,7 @@ class OC_FileCache{
 				$root='';
 			}
 		}
+		$path=$root.$path;
 		$query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?');
 		$result=$query->execute(array($path));
 		if($row=$result->fetchRow()){
@@ -418,13 +475,13 @@ class OC_FileCache{
 			$view=new OC_FilesystemView(($root=='/')?'':$root);
 		}
 		self::scanFile($path,$root);
-		$dh=$view->opendir($path);
+		$dh=$view->opendir($path.'/');
 		$totalSize=0;
 		if($dh){
 			while (($filename = readdir($dh)) !== false) {
 				if($filename != '.' and $filename != '..'){
 					$file=$path.'/'.$filename;
-					if($view->is_dir($file)){
+					if($view->is_dir($file.'/')){
 						if($eventSource){
 							$eventSource->send('scanning',array('file'=>$file,'count'=>$count));
 						}
@@ -515,6 +572,9 @@ class OC_FileCache{
 			}
 			$view=new OC_FilesystemView($root);
 		}
+		if(!$view->file_exists($path)){
+			return false;
+		}
 		$mtime=$view->filemtime($path);
 		$isDir=$view->is_dir($path);
 		$path=$root.$path;
diff --git a/lib/fileproxy.php b/lib/fileproxy.php
index 235fc8bf284f7707a009608147e364b86baae3e8..46fc2f49c50a39e92768b15c6f2d310a51edb368 100644
--- a/lib/fileproxy.php
+++ b/lib/fileproxy.php
@@ -34,11 +34,12 @@
  * A post-proxy recieves 2 arguments, the filepath and the result of the operation.
  * The return calue of the post-proxy will be used as the new result of the operation
  * The operations that have a post-proxy are
- *      file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search
+ * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, fopen, free_space and search
  */
 
 class OC_FileProxy{
 	private static $proxies=array();
+	public static $enabled=true;
 	
 	/**
 	 * check if this proxy implments a specific proxy operation
@@ -83,16 +84,19 @@ class OC_FileProxy{
 		return $proxies;
 	}
 
-	public static function runPreProxies($operation,$filepath,$filepath2=null){
+	public static function runPreProxies($operation,&$filepath,&$filepath2=null){
+		if(!self::$enabled){
+			return true;
+		}
 		$proxies=self::getProxies($operation,false);
 		$operation='pre'.$operation;
 		foreach($proxies as $proxy){
-			if($filepath2){
-				if(!$proxy->$operation($filepath,$filepath2)){
+			if(!is_null($filepath2)){
+				if($proxy->$operation($filepath,$filepath2)===false){
 					return false;
 				}
 			}else{
-				if(!$proxy->$operation($filepath)){
+				if($proxy->$operation($filepath)===false){
 					return false;
 				}
 			}
@@ -101,6 +105,9 @@ class OC_FileProxy{
 	}
 
 	public static function runPostProxies($operation,$path,$result){
+		if(!self::$enabled){
+			return $result;
+		}
 		$proxies=self::getProxies($operation,true);
 		$operation='post'.$operation;
 		foreach($proxies as $proxy){
diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php
index 94a49176ee6fbbe2424081fe61332bdcb1b6711f..9e4c2d0643e46201985d9341cddc2ec51e1683f9 100644
--- a/lib/fileproxy/quota.php
+++ b/lib/fileproxy/quota.php
@@ -26,11 +26,37 @@
  */
 
 class OC_FileProxy_Quota extends OC_FileProxy{
+	private $userQuota=-1;
+	
+	/**
+	 * get the quota for the current user
+	 * @return int
+	 */
+	private function getQuota(){
+		if($this->userQuota!=-1){
+			return $this->userQuota;
+		}
+		$userQuota=OC_Preferences::getValue(OC_User::getUser(),'files','quota','default');
+		if($userQuota=='default'){
+			$userQuota=OC_AppConfig::getValue('files','default_quota','none');
+		}
+		if($userQuota=='none'){
+			$this->userQuota=0;
+		}else{
+			$this->userQuota=OC_Helper::computerFileSize($userQuota);
+		}
+		return $this->userQuota;
+		
+	}
+	
+	/**
+	 * get the free space in the users home folder
+	 * @return int
+	 */
 	private function getFreeSpace(){
 		$rootInfo=OC_FileCache::get('');
 		$usedSpace=$rootInfo['size'];
-		$totalSpace=OC_Preferences::getValue(OC_User::getUser(),'files','quota',0);
-		$totalSpace=OC_Helper::computerFileSize($totalSpace);
+		$totalSpace=$this->getQuota();
 		if($totalSpace==0){
 			return 0;
 		}
diff --git a/lib/filestorage.php b/lib/filestorage.php
index 4523144f6f4230031ca805cfb3322f8a67285f3a..fd6497b9478387a6746e655dedf0d87af92de7ab 100644
--- a/lib/filestorage.php
+++ b/lib/filestorage.php
@@ -23,33 +23,31 @@
 /**
  * Privde a common interface to all different storage options
  */
-class OC_Filestorage{
+abstract class OC_Filestorage{
 	public function __construct($parameters){}
-	public function mkdir($path){}
-	public function rmdir($path){}
-	public function opendir($path){}
-	public function is_dir($path){}
-	public function is_file($path){}
-	public function stat($path){}
-	public function filetype($path){}
-	public function filesize($path){}
-	public function is_readable($path){}
-	public function is_writable($path){}
-	public function file_exists($path){}
-	public function readfile($path){}
-	public function filectime($path){}
-	public function filemtime($path){}
-	public function file_get_contents($path){}
-	public function file_put_contents($path,$data){}
-	public function unlink($path){}
-	public function rename($path1,$path2){}
-	public function copy($path1,$path2){}
-	public function fopen($path,$mode){}
-	public function toTmpFile($path){}//copy the file to a temporary file, used for cross-storage file actions
-	public function fromTmpFile($tmpPath,$path){}//copy a file from a temporary file, used for cross-storage file actions
-	public function getMimeType($path){}
-	public function hash($type,$path,$raw){}
-	public function free_space($path){}
-	public function search($query){}
-	public function getLocalFile($path){}// get a path to a local version of the file, whether the original file is local or remote
+	abstract public function mkdir($path);
+	abstract public function rmdir($path);
+	abstract public function opendir($path);
+	abstract public function is_dir($path);
+	abstract public function is_file($path);
+	abstract public function stat($path);
+	abstract public function filetype($path);
+	abstract public function filesize($path);
+	abstract public function is_readable($path);
+	abstract public function is_writable($path);
+	abstract public function file_exists($path);
+	abstract public function filectime($path);
+	abstract public function filemtime($path);
+	abstract public function file_get_contents($path);
+	abstract public function file_put_contents($path,$data);
+	abstract public function unlink($path);
+	abstract public function rename($path1,$path2);
+	abstract public function copy($path1,$path2);
+	abstract public function fopen($path,$mode);
+	abstract public function getMimeType($path);
+	abstract public function hash($type,$path,$raw);
+	abstract public function free_space($path);
+	abstract public function search($query);
+	abstract public function touch($path, $mtime=null);
+	abstract public function getLocalFile($path);// get a path to a local version of the file, whether the original file is local or remote
 }
diff --git a/lib/filestorage/common.php b/lib/filestorage/common.php
new file mode 100644
index 0000000000000000000000000000000000000000..f632474df01d62f9cae1812d96369f83eab65fab
--- /dev/null
+++ b/lib/filestorage/common.php
@@ -0,0 +1,159 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski GapczynskiM@gmail.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/>.
+*/
+
+abstract class OC_Filestorage_Common extends OC_Filestorage {
+
+	public function __construct($parameters){}
+// 	abstract public function mkdir($path);
+// 	abstract public function rmdir($path);
+// 	abstract public function opendir($path);
+	public function is_dir($path){
+		return $this->filetype($path)=='dir';
+	}
+	public function is_file($path){
+		return $this->filetype($path)=='file';
+	}
+// 	abstract public function stat($path);
+// 	abstract public function filetype($path);
+	public function filesize($path) {
+		if($this->is_dir($path)){
+			return 0;//by definition
+		}else{
+			$stat = $this->stat($path);
+			return $stat['size'];
+		}
+	}
+// 	abstract public function is_readable($path);
+// 	abstract public function is_writable($path);
+// 	abstract public function file_exists($path);
+	public function filectime($path) {
+		$stat = $this->stat($path);
+		return $stat['ctime'];
+	}
+	public function filemtime($path) {
+		$stat = $this->stat($path);
+		return $stat['mtime'];
+	}
+	public function fileatime($path) {
+		$stat = $this->stat($path);
+		return $stat['atime'];
+	}
+	public function file_get_contents($path) {
+		$handle = $this->fopen($path, "r");
+		if(!$handle){
+			return false;
+		}
+		$size=$this->filesize($path);
+		if($size==0){
+			return '';
+		}
+		return fread($handle, $size);
+	}
+	public function file_put_contents($path,$data) {
+		$handle = $this->fopen($path, "w");
+		return fwrite($handle, $data);
+	}
+// 	abstract public function unlink($path);
+	public function rename($path1,$path2){
+		if($this->copy($path1,$path2)){
+			return $this->unlink($path1);
+		}else{
+			return false;
+		}
+	}
+	public function copy($path1,$path2) {
+		$source=$this->fopen($path1,'r');
+		$target=$this->fopen($path2,'w');
+		$count=OC_Helper::streamCopy($source,$target);
+		return $count>0;
+	}
+// 	abstract public function fopen($path,$mode);
+	public function getMimeType($path){
+		if(!$this->file_exists($path)){
+			return false;
+		}
+		if($this->is_dir($path)){
+			return 'httpd/unix-directory';
+		}
+		$source=$this->fopen($path,'r');
+		if(!$source){
+			return false;
+		}
+		$head=fread($source,8192);//8kb should suffice to determine a mimetype
+		if($pos=strrpos($path,'.')){
+			$extention=substr($path,$pos);
+		}else{
+			$extention='';
+		}
+		$tmpFile=OC_Helper::tmpFile($extention);
+		file_put_contents($tmpFile,$head);
+		$mime=OC_Helper::getMimeType($tmpFile);
+		unlink($tmpFile);
+		return $mime;
+	}
+	public function hash($type,$path,$raw){
+		$tmpFile=$this->getLocalFile();
+		$hash=hash($type,$tmpFile,$raw);
+		unlink($tmpFile);
+		return $hash;
+	}
+// 	abstract public function free_space($path);
+	public function search($query){
+		return $this->searchInDir($query);
+	}
+	public function getLocalFile($path){
+		return $this->toTmpFile($path);
+	}
+	private function toTmpFile($path){//no longer in the storage api, still usefull here
+		$source=$this->fopen($path,'r');
+		if(!$source){
+			return false;
+		}
+		if($pos=strrpos($path,'.')){
+			$extention=substr($path,$pos);
+		}else{
+			$extention='';
+		}
+		$tmpFile=OC_Helper::tmpFile($extention);
+		$target=fopen($tmpFile,'w');
+		$count=OC_Helper::streamCopy($source,$target);
+		return $tmpFile;
+	}
+// 	abstract public function touch($path, $mtime=null);
+
+	protected function searchInDir($query,$dir=''){
+		$files=array();
+		$dh=$this->opendir($dir);
+		if($dh){
+			while($item=readdir($dh)){
+				if ($item == '.' || $item == '..') continue;
+				if(strstr(strtolower($item),strtolower($query))!==false){
+					$files[]=$dir.'/'.$item;
+				}
+				if($this->is_dir($dir.'/'.$item)){
+					$files=array_merge($files,$this->searchInDir($query,$dir.'/'.$item));
+				}
+			}
+		}
+		return $files;
+	}
+}
diff --git a/lib/filestorage/commontest.php b/lib/filestorage/commontest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b01ff856a358c945cc30b122d78108e1daed304
--- /dev/null
+++ b/lib/filestorage/commontest.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2012 Robin Appelman icewind@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/>.
+*
+*/
+
+/**
+ * test implementation for OC_FileStorage_Common with OC_FileStorage_Local
+ */
+
+class OC_Filestorage_CommonTest extends OC_Filestorage_Common{
+	/**
+	 * underlying local storage used for missing functions
+	 * @var OC_FileStorage_Local
+	 */
+	private $storage;
+	
+	public function __construct($params){
+		$this->storage=new OC_Filestorage_Local($params);
+	}
+	
+	public function mkdir($path){
+		return $this->storage->mkdir($path);
+	}
+	public function rmdir($path){
+		return $this->storage->rmdir($path);
+	}
+	public function opendir($path){
+		return $this->storage->opendir($path);
+	}
+	public function stat($path){
+		return $this->storage->stat($path);
+	}
+	public function filetype($path){
+		return $this->storage->filetype($path);
+	}
+	public function is_readable($path){
+		return $this->storage->is_readable($path);
+	}
+	public function is_writable($path){
+		return $this->storage->is_writable($path);
+	}
+	public function file_exists($path){
+		return $this->storage->file_exists($path);
+	}
+	public function unlink($path){
+		return $this->storage->unlink($path);
+	}
+	public function fopen($path,$mode){
+		return $this->storage->fopen($path,$mode);
+	}
+	public function free_space($path){
+		return $this->storage->free_space($path);
+	}
+	public function touch($path, $mtime=null){
+		return $this->storage->touch($path,$mtime);
+	}
+}
\ No newline at end of file
diff --git a/lib/filestorage/google.php b/lib/filestorage/google.php
index fc271f4e4baf812e204a943f754b80448768e02a..4998554838291ef8d639873ea0a73ab3c92d59bf 100644
--- a/lib/filestorage/google.php
+++ b/lib/filestorage/google.php
@@ -20,36 +20,322 @@
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+require_once 'common.inc.php';
+
 class OC_Filestorage_Google extends OC_Filestorage_Common {
+
+	private $datadir;
+	private $consumer;
+	private $oauth_token;
+	private $sig_method;
+	private $entries;
+
+	public function __construct($arguments) {
+		$this->datadir = $arguments['datadir'];
+		$consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous';
+		$consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous';
+		$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
+		$this->oauth_token = new OAuthToken($arguments['token'], $arguments['token_secret']);
+		$this->sig_method = new OAuthSignatureMethod_HMAC_SHA1();
+		$this->entries = array();
+	}
+
+	private function sendRequest($feedUri, $http_method, $postData = null) {
+		$feedUri = trim($feedUri);
+		// create an associative array from each key/value url query param pair.
+		$params = array();
+		$pieces = explode('?', $feedUri);
+		if (isset($pieces[1])) {
+			$params = explode_assoc('=', '&', $pieces[1]);
+		}
+		// urlencode each url parameter key/value pair
+		$tempStr = $pieces[0];
+		foreach ($params as $key => $value) {
+			$tempStr .= '&' . urlencode($key) . '=' . urlencode($value);
+		}
+		$feedUri = preg_replace('/&/', '?', $tempStr, 1);
+		$req = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $http_method, $feedUri, $params);
+		$req->sign_request($this->sig_method, $this->consumer, $this->oauth_token);
+		$auth_header = $req->to_header();
+		$result = send_signed_request($http_method, $feedUri, array($auth_header, 'Content-Type: application/atom+xml', 'GData-Version: 3.0'), $postData);
+		// TODO Return false if error is received
+		if (!$result) {
+			return false;
+		}
+		$result = explode('<', $result, 2);
+		$result = isset($result[1]) ? '<'.$result[1] : $result[0];
+		$dom = new DOMDocument();
+		$dom->loadXML($result);
+		return $dom;
+	}
+
+	private function getResource($path) {
+		if (array_key_exists($path, $this->entries)) {
+			return $this->entries[$path];
+		} else {
+			$title = basename($path);
+			$dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET');
+			// Check if request was successful and entry exists
+			if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
+				$this->entries[$path] = $entry;
+				return $entry;
+			}
+			return false;
+		}
+	}
+
+	private function getExtension($entry) {
+		$mimetype = $this->getMimeType('', $entry);
+		switch($mimetype) {
+			case 'httpd/unix-directory':
+				return '';
+			case 'application/vnd.oasis.opendocument.text':
+				return 'odt';
+			case 'application/vnd.oasis.opendocument.spreadsheet':
+				return 'ods';
+			case 'application/vnd.oasis.opendocument.presentation':
+				return 'pptx';
+			case 'text/html':
+				return 'html';
+			default:
+				return 'html';
+		}
+	}
 	
-	private $auth;
 
-	public function __construct($parameters) {
-		
+	public function mkdir($path) {
+		$dir = dirname($path);
+		// Check if path parent is root directory
+		if ($dir == '/' || $dir == '\.' || $dir == '.') {
+			$feedUri = 'https://docs.google.com/feeds/default/private/full';
+		// Get parent content link
+		} else if ($dom = $this->getResource(basename($dir))) {
+			$feedUri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src');
+		}
+		if (isset($feedUri)) {
+			$title = basename($path);
+			// Construct post data
+			$postData = '<?xml version="1.0" encoding="UTF-8"?>';
+			$postData .= '<entry xmlns="http://www.w3.org/2005/Atom">';
+			$postData .= '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#folder"/>';
+			$postData .= '<title>'.$title.'</title>';
+			$postData .= '</entry>';
+			if ($dom = $this->sendRequest($feedUri, 'POST', $postData)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public function rmdir($path) {
+		return $this->unlink($path);
+	}
+
+	public function opendir($path) {
+		if ($path == '' || $path == '/') {
+			$next = 'https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents';
+		} else {
+			if ($entry = $this->getResource($path)) {
+				$next = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
+			} else {
+				return false;
+			}
+		}
+		$files = array();
+		while ($next) {
+			$dom = $this->sendRequest($next, 'GET');
+			$links = $dom->getElementsByTagName('link');
+			foreach ($links as $link) {
+				if ($link->getAttribute('rel') == 'next') {
+					$next = $link->getAttribute('src');
+					break;
+				} else {
+					$next = false;
+				}
+			}
+			$entries = $dom->getElementsByTagName('entry');
+			foreach ($entries as $entry) {
+				$name = $entry->getElementsByTagName('title')->item(0)->nodeValue;
+				// Google Docs resources don't always include extensions in title
+				if (!strpos($name, '.')) {
+					$name .= '.'.$this->getExtension($entry);
+				}
+				$files[] = $name;
+				// Cache entry for future use
+				$this->entries[$name] = $entry;
+			}
+		}
+		OC_FakeDirStream::$dirs['google'] = $files;
+		return opendir('fakedir://google');
+	}
+
+	public function stat($path) {
+		if ($path == '' || $path == '/') {
+			$stat['size'] = $this->free_space($path);
+			$stat['atime'] = time();
+			$stat['mtime'] = time();
+			$stat['ctime'] = time();
+		} else if ($entry = $this->getResource($path)) {
+			// NOTE: Native resources don't have a file size
+			$stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
+			$stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue);
+			$stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue);
+			$stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue);
+		}
+		return $stat;
+	}
+
+	public function filetype($path) {
+		if ($path == '' || $path == '/') {
+			return 'dir';
+		} else if ($entry = $this->getResource($path)) {
+			$categories = $entry->getElementsByTagName('category');
+			foreach ($categories as $category) {
+				if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
+					$type = $category->getAttribute('label');
+					if (strlen(strstr($type, 'folder')) > 0) {
+						return 'dir';
+					} else {
+						return 'file';
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	public function is_readable($path) {
+		return true;
+	}
+
+	public function is_writable($path) {
+		if ($path == '' || $path == '/') {
+			return true;
+		} else if ($entry = $this->getResource($path)) {
+			// Check if edit or edit-media links exist
+			$links = $entry->getElementsByTagName('link');
+			foreach ($links as $link) {
+				if ($link->getAttribute('rel') == 'edit') {
+					return true;
+				} else if ($link->getAttribute('rel') == 'edit-media') {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
+	public function file_exists($path) {
+		if ($path == '' || $path == '/') {
+			return true;
+		} else if ($this->getResource($path)) {
+			return true;
+		}
+		return false;
+	}
+	
+	public function unlink($path) {
+		// Get resource self link to trash resource
+		if ($entry = $this->getResource($path)) {
+			$links = $entry->getElementsByTagName('link');
+			foreach ($links as $link) {
+				if ($link->getAttribute('rel') == 'self') {
+					$feedUri = $link->getAttribute('href');
+				}
+			}
+		}
+		if (isset($feedUri)) {
+			$this->sendRequest($feedUri, 'DELETE');
+			return true;
+		}
+		return false;
+	}
+
+	public function rename($path1, $path2) {
+		// TODO Add support for moving to different collections
+		// Get resource edit link to rename resource
+		if ($entry = $this->getResource($path1)) {
+			$etag = $entry->getElementsByTagName('entry')->item(0)->getAttribute('gd:etag');
+			$links = $entry->getElementsByTagName('link');
+			foreach ($links as $link) {
+				if ($link->getAttribute('rel') == 'edit') {
+					$feedUri = $link->getAttribute('href');
+				}
+			}
+		}
+		if (isset($etag) && isset($feedUri)) {
+			$title = basename($path2);
+			// Construct post data
+			$postData = '<?xml version="1.0" encoding="UTF-8"?>';
+			$postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>';
+			$postData .= '<title>'.$title.'</title>';
+			$postData .= '</entry>';
+			$this->sendRequest($feedUri, 'PUT', $postData);
+			return true;
+		}
+		return false;
+	}
+
+	public function fopen($path, $mode) {
+		if ($entry = $this->getResource($path)) {
+			$extension = $this->getExtension($path);
+			$downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
+			// TODO Non-native documents don't need these additional parameters
+			$downloadUri .= '&exportFormat='.$extension.'&format='.$extension;
+		}
+	}
+
+	public function getMimeType($path, $entry = null) {
+		if ($entry == null) {
+			if ($path == '' || $path == '/') {
+				return 'httpd/unix-directory';
+			} else {
+				$entry = $this->getResource($path);
+			}
+		}
+		if ($entry) {
+			$mimetype = $entry->getElementsByTagName('content')->item(0)->getAttribute('type');
+			// Native Google Docs resources often default to text/html, but it may be more useful to default to a corresponding ODF mimetype
+			// Collections get reported as application/atom+xml, make sure it actually is a folder and fix the mimetype
+			if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml') {
+				$categories = $entry->getElementsByTagName('category');
+				foreach ($categories as $category) {
+					if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
+						$type = $category->getAttribute('label');
+						if (strlen(strstr($type, 'folder')) > 0) {
+							return 'httpd/unix-directory';
+						} else if (strlen(strstr($type, 'document')) > 0) {
+							return 'application/vnd.oasis.opendocument.text';
+						} else if (strlen(strstr($type, 'spreadsheet')) > 0) {
+							return 'application/vnd.oasis.opendocument.spreadsheet';
+						} else if (strlen(strstr($type, 'presentation')) > 0) {
+							return 'application/vnd.oasis.opendocument.presentation';
+						} else if (strlen(strstr($type, 'drawing')) > 0) {
+							return 'application/vnd.oasis.opendocument.graphics';
+						} else {
+							// If nothing matches return text/html, all native Google Docs resources can be exported as text/html
+							return 'text/html';
+						}
+					}
+				}
+			}
+			return $mimetype;
+		}
+		return false;
 	}
 	
-	private function connect() {
-	  
-	}
-	public function mkdir($path){}
-	public function rmdir($path){}
-	public function opendir($path){}
-	public function is_dir($path){}
-	public function is_file($path){}
-	public function stat($path){}
-	public function filetype($path){}
-	public function is_readable($path){}
-	public function is_writable($path){}
-	public function file_exists($path){}
-	public function unlink($path){}
-	public function rename($path1,$path2){}
-	public function fopen($path,$mode){}
-	public function toTmpFile($path){}
-	public function fromTmpFile($tmpPath,$path){}
-	public function fromUploadedFile($tmpPath,$path){}
-	public function getMimeType($path){}
-	public function hash($type,$path,$raw){}
-	public function free_space($path){}
-	public function search($query){}
-	public function getLocalFile($path){}
+	public function free_space($path) {
+		if ($dom = $this->sendRequest('https://docs.google.com/feeds/metadata/default', 'GET')) {
+			// NOTE: Native Google Docs resources don't count towards quota
+			$total = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesTotal')->item(0)->nodeValue;
+			$used = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
+			return $total - $used;
+		}
+		return false;
+	}
+  
+	public function search($query) {
+		
+	}
+
 }
\ No newline at end of file
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
index dcb516a3afb7ff48f2a2fc64a3c5acc62a49674a..688501aee906ed23ade34222dd6b1b2778181586 100644
--- a/lib/filestorage/local.php
+++ b/lib/filestorage/local.php
@@ -21,7 +21,10 @@ class OC_Filestorage_Local extends OC_Filestorage{
 		return opendir($this->datadir.$path);
 	}
 	public function is_dir($path){
-		return (is_dir($this->datadir.$path) or substr($path,-1)=='/');
+		if(substr($path,-1)=='/'){
+			$path=substr($path,0,-1);
+		}
+		return is_dir($this->datadir.$path);
 	}
 	public function is_file($path){
 		return is_file($this->datadir.$path);
@@ -52,31 +55,32 @@ class OC_Filestorage_Local extends OC_Filestorage{
 	public function file_exists($path){
 		return file_exists($this->datadir.$path);
 	}
-	public function readfile($path){
-		return readfile($this->datadir.$path);
-	}
 	public function filectime($path){
 		return filectime($this->datadir.$path);
 	}
 	public function filemtime($path){
 		return filemtime($this->datadir.$path);
 	}
-	public function touch($path, $mtime){
+	public function touch($path, $mtime=null){
 		// sets the modification time of the file to the given value. 
 		// If mtime is nil the current time is set.
 		// note that the access time of the file always changes to the current time.
-		if( touch( $this->datadir.$path, $mtime ) ) {
+		if(!is_null($mtime)){
+			$result=touch( $this->datadir.$path, $mtime );
+		}else{
+			$result=touch( $this->datadir.$path);
+		}
+		if( $result ) {
 			clearstatcache( true, $this->datadir.$path );
 		}
 		
-		return touch($this->datadir.$path, $mtime);
+		return $result;
 	}
 	public function file_get_contents($path){
 		return file_get_contents($this->datadir.$path);
 	}
 	public function file_put_contents($path,$data){
-		if($return=file_put_contents($this->datadir.$path,$data)){
-		}
+		return file_put_contents($this->datadir.$path,$data);
 	}
 	public function unlink($path){
 		return $this->delTree($path);
@@ -99,9 +103,7 @@ class OC_Filestorage_Local extends OC_Filestorage{
 			$source=substr($path1,strrpos($path1,'/')+1);
 			$path2.=$source;
 		}
-		if($return=copy($this->datadir.$path1,$this->datadir.$path2)){
-		}
-		return $return;
+		return copy($this->datadir.$path1,$this->datadir.$path2);
 	}
 	public function fopen($path,$mode){
 		if($return=fopen($this->datadir.$path,$mode)){
@@ -122,72 +124,9 @@ class OC_Filestorage_Local extends OC_Filestorage{
 		return $return;
 	}
 
-	public function getMimeType($fspath){
-		if($this->is_readable($fspath)){
-			$mimeType='application/octet-stream';
-			if ($mimeType=='application/octet-stream') {
-				self::$mimetypes = include('mimetypes.fixlist.php');
-				$extention=strtolower(strrchr(basename($fspath), "."));
-				$extention=substr($extention,1);//remove leading .
-				$mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream';
-				
-			}
-			if (@is_dir($this->datadir.$fspath)) {
-				// directories are easy
-				return "httpd/unix-directory";
-			}
-			if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){
-				$mimeType =strtolower(finfo_file($finfo,$this->datadir.$fspath));
-				$mimeType=substr($mimeType,0,strpos($mimeType,';'));
-				finfo_close($finfo);
-			}
-			if ($mimeType=='application/octet-stream' && function_exists("mime_content_type")) {
-				// use mime magic extension if available
-				$mimeType = mime_content_type($this->datadir.$fspath);
-			}
-			if ($mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) {
-				// it looks like we have a 'file' command,
-				// lets see it it does have mime support
-				$fspath=str_replace("'","\'",$fspath);
-				$fp = popen("file -i -b '{$this->datadir}$fspath' 2>/dev/null", "r");
-				$reply = fgets($fp);
-				pclose($fp);
-
-				//trim the character set from the end of the response
-				$mimeType=substr($reply,0,strrpos($reply,' '));
-			}
-			if ($mimeType=='application/octet-stream') {
-				// Fallback solution: (try to guess the type by the file extension
-				if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){
-					self::$mimetypes=include('mimetypes.list.php');
-				}
-				$extention=strtolower(strrchr(basename($fspath), "."));
-				$extention=substr($extention,1);//remove leading .
-				$mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream';
-			}
-			return $mimeType;
-		}else{
-			return false;
-		}
-	}
-
-	public function toTmpFile($path){
-		$tmpFolder=get_temp_dir();
-		$filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.')));
-		$fileStats = stat($this->datadir.$path);
-		if(copy($this->datadir.$path,$filename)){
-			touch($filename, $fileStats['mtime'], $fileStats['atime']);
-			return $filename;
-		}else{
-			return false;
-		}
-	}
-
-	public function fromTmpFile($tmpFile,$path){
-		$fileStats = stat($tmpFile);
-		if(rename($tmpFile,$this->datadir.$path)){
-			touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']);
-			return true;
+	public function getMimeType($path){
+		if($this->is_readable($path)){
+			return OC_Helper::getMimeType($this->datadir.$path);
 		}else{
 			return false;
 		}
diff --git a/lib/filestoragecommon.php b/lib/filestoragecommon.php
deleted file mode 100644
index f522d15c4e9ee9bbd3cdf08e28571f5ef0a3ad2a..0000000000000000000000000000000000000000
--- a/lib/filestoragecommon.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-/**
-* ownCloud
-*
-* @author Michael Gapczynski
-* @copyright 2012 Michael Gapczynski GapczynskiM@gmail.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/>.
-*/
-
-class OC_Filestorage_Common extends OC_Filestorage {
-
-	public function __construct($parameters){}
-	public function mkdir($path){}
-	public function rmdir($path){}
-	public function opendir($path){}
-	public function is_dir($path){}
-	public function is_file($path){}
-	public function stat($path){}
-	public function filetype($path){}
-	public function filesize($path) {
-		$stat = $this->stat($path);
-		return $stat['size'];
-	}
-	public function is_readable($path){}
-	public function is_writable($path){}
-	public function file_exists($path){}
-	public function readfile($path) {
-		$handle = $this->fopen($path, "r");
-		$chunk = 1024;
-		while (!feof($handle)) {
-			echo fread($handle, $chunk);
-		}
-		return $this->filesize($path);
-	}
-	public function filectime($path) {
-		$stat = $this->stat($path);
-		return $stat['ctime'];
-	}
-	public function filemtime($path) {
-		$stat = $this->stat($path);
-		return $stat['mtime'];
-	}
-	public function fileatime($path) {
-		$stat = $this->stat($path);
-		return $stat['atime'];
-	}
-	public function file_get_contents($path) {
-		$handle = $this->fopen($path, "r");
-		return fread($handle, $this->filesize($path));
-	}
-	public function file_put_contents($path,$data) {
-		$handle = $this->fopen($path, "w");
-		return fwrite($handle, $data);
-	}
-	public function unlink($path){}
-	public function rename($path1,$path2){}
-	public function copy($path1,$path2) {
-		$data = $this->file_get_contents($path1);
-		return $this->file_put_contents($path2, $data);
-	}
-	public function fopen($path,$mode){}
-	public function toTmpFile($path){}
-	public function fromTmpFile($tmpPath,$path){}
-	public function fromUploadedFile($tmpPath,$path){}
-	public function getMimeType($path){}
-	public function hash($type,$path,$raw){}
-	public function free_space($path){}
-	public function search($query){}
-	public function getLocalFile($path){}
-}
diff --git a/lib/filesystem.php b/lib/filesystem.php
index 90195bc2130e137de210fecb42c2bf037ec8f10a..12905d189f947c2e878466695df9cd6b3e886cfb 100644
--- a/lib/filesystem.php
+++ b/lib/filesystem.php
@@ -146,20 +146,15 @@ class OC_Filesystem{
 	* @return string
 	*/
 	static public function getMountPoint($path){
+		OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path));
 		if(!$path){
 			$path='/';
 		}
 		if(substr($path,0,1)!=='/'){
 			$path='/'.$path;
 		}
-		if(substr($path,-1)!=='/'){
-			$path=$path.'/';
-		}
 		$foundMountPoint='';
 		foreach(OC_Filesystem::$mounts as $mountpoint=>$storage){
-			if(substr($mountpoint,-1)!=='/'){
-				$mountpoint=$mountpoint.'/';
-			}
 			if($mountpoint==$path){
 				return $mountpoint;
 			}
@@ -259,6 +254,9 @@ class OC_Filesystem{
 	* @param string mountpoint
 	*/
 	static public function mount($class,$arguments,$mountpoint){
+		if(substr($mountpoint,-1)!=='/'){
+			$mountpoint=$mountpoint.'/';
+		}
 		if(substr($mountpoint,0,1)!=='/'){
 			$mountpoint='/'.$mountpoint;
 		}
@@ -345,7 +343,7 @@ class OC_Filesystem{
 	static public function filemtime($path){
 		return self::$defaultInstance->filemtime($path);
 	}
-	static public function touch($path, $mtime){
+	static public function touch($path, $mtime=null){
 		return self::$defaultInstance->touch($path, $mtime);
 	}
 	static public function file_get_contents($path){
diff --git a/lib/filesystemview.php b/lib/filesystemview.php
index 91c6cd17720babaf01420f8500d96a415c6d2a73..89e0385fe9c6247ba6879200f34fff309df4a69b 100644
--- a/lib/filesystemview.php
+++ b/lib/filesystemview.php
@@ -136,7 +136,14 @@ class OC_FilesystemView {
 		return $this->basicOperation('filesize',$path);
 	}
 	public function readfile($path){
-		return $this->basicOperation('readfile',$path,array('read'));
+		$handle=$this->fopen($path,'r');
+		$chunkSize = 1024*1024;// 1 MB chunks
+		while (!feof($handle)) {
+			echo fread($handle, $chunkSize);
+			@ob_flush();
+			flush(); 
+		}
+		return $this->filesize($path);
 	}
 	public function is_readable($path){
 		return $this->basicOperation('is_readable',$path);
@@ -156,14 +163,27 @@ class OC_FilesystemView {
 	public function filemtime($path){
 		return $this->basicOperation('filemtime',$path);
 	}
-	public function touch($path, $mtime){
+	public function touch($path, $mtime=null){
 		return $this->basicOperation('touch', $path, array('write'), $mtime);
 	}
 	public function file_get_contents($path){
 		return $this->basicOperation('file_get_contents',$path,array('read'));
 	}
 	public function file_put_contents($path,$data){
-		return $this->basicOperation('file_put_contents',$path,array('create','write'),$data);
+		if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier
+			$target=$this->fopen($path,'w');
+			if($target){
+				$count=OC_Helper::streamCopy($data,$target);
+				fclose($target);
+				fclose($data);
+				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path));
+				return $count>0;
+			}else{
+				return false;
+			}
+		}else{
+			return $this->basicOperation('file_put_contents',$path,array('create','write'),$data);
+		}
 	}
 	public function unlink($path){
 		return $this->basicOperation('unlink',$path,array('delete'));
@@ -179,10 +199,13 @@ class OC_FilesystemView {
 					if($storage=$this->getStorage($path1)){
 						$result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2));
 					}
-				}elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){
-					$tmpFile=$storage1->toTmpFile($this->getInternalPath($path1));
-					$result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2));
+				}else{
+					$source=$this->fopen($path1,'r');
+					$target=$this->fopen($path2,'w');
+					$count=OC_Helper::streamCopy($data,$target);
+					$storage1=$this->getStorage($path1);
 					$storage1->unlink($this->getInternalPath($path1));
+					$result=$count>0;
 				}
 				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2));
 				return $result;
@@ -207,9 +230,10 @@ class OC_FilesystemView {
 					if($storage=$this->getStorage($path1)){
 						$result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2));
 					}
-				}elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){
-					$tmpFile=$storage1->toTmpFile($this->getInternalPath($path1));
-					$result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2));
+				}else{
+					$source=$this->fopen($path1,'r');
+					$target=$this->fopen($path2,'w');
+					$count=OC_Helper::streamCopy($data,$target);
 				}
         OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2));
 				if(!$exists){
@@ -224,18 +248,26 @@ class OC_FilesystemView {
 		$hooks=array();
 		switch($mode){
 			case 'r':
+			case 'rb':
 				$hooks[]='read';
 				break;
 			case 'r+':
+			case 'rb+':
 			case 'w+':
+			case 'wb+':
 			case 'x+':
+			case 'xb+':
 			case 'a+':
+			case 'ab+':
 				$hooks[]='read';
 				$hooks[]='write';
 				break;
 			case 'w':
+			case 'wb':
 			case 'x':
+			case 'xb':
 			case 'a':
+			case 'ab':
 				$hooks[]='write';
 				break;
 			default:
@@ -245,29 +277,29 @@ class OC_FilesystemView {
 		return $this->basicOperation('fopen',$path,$hooks,$mode);
 	}
 	public function toTmpFile($path){
-		if(OC_FileProxy::runPreProxies('toTmpFile',$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){
-			OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, array( OC_Filesystem::signal_param_path => $path));
-			return $storage->toTmpFile($this->getInternalPath($path));
+		if(OC_Filesystem::isValidPath($path)){
+			$source=$this->fopen($path,'r');
+			if($source){
+				$extention=substr($path,strrpos($path,'.'));
+				$tmpFile=OC_Helper::tmpFile($extention);
+				return file_put_contents($tmpFile,$source);
+			}
 		}
 	}
 	public function fromTmpFile($tmpFile,$path){
-		if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){
-			$run=true;
-			$exists=$this->file_exists($path);
-			if(!$exists){
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
+		if(OC_Filesystem::isValidPath($path)){
+			if(!$tmpFile){
+				debug_print_backtrace();
 			}
-			if($run){
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
-			}
-			if($run){
-				$result=$storage->fromTmpFile($tmpFile,$this->getInternalPath($path));
-				if(!$exists){
-					OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path));
-				}
-				OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path));
-				return $result;
+			$source=fopen($tmpFile,'r');
+			if($source){
+				$this->file_put_contents($path,$source);
+				unlink($tmpFile);
+				return true;
+			}else{
 			}
+		}else{
+			return false;
 		}
 	}
 
@@ -291,26 +323,32 @@ class OC_FilesystemView {
 	 * @return mixed
 	 */
 	private function basicOperation($operation,$path,$hooks=array(),$extraParam=null){
-		if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){
+		if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path)){
 			$interalPath=$this->getInternalPath($path);
 			$run=true;
-			foreach($hooks as $hook){
-				if($hook!='read'){
-					OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
-				}else{
-					OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path));
+			if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){
+				foreach($hooks as $hook){
+					if($hook!='read'){
+						OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run));
+					}else{
+						OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path));
+					}
 				}
 			}
-			if($run){
-				if($extraParam){
+			if($run and $storage=$this->getStorage($path)){
+				if(!is_null($extraParam)){
 					$result=$storage->$operation($interalPath,$extraParam);
 				}else{
 					$result=$storage->$operation($interalPath);
 				}
 				$result=OC_FileProxy::runPostProxies($operation,$path,$result);
-				foreach($hooks as $hook){
-					if($hook!='read'){
-						OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path));
+				if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){
+					if($operation!='fopen'){//no post hooks for fopen, the file stream is still open
+						foreach($hooks as $hook){
+							if($hook!='read'){
+								OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path));
+							}
+						}
 					}
 				}
 				return $result;
diff --git a/lib/geo.php b/lib/geo.php
new file mode 100644
index 0000000000000000000000000000000000000000..a967ab28a96edc3a54b9caa3914d15e31091190e
--- /dev/null
+++ b/lib/geo.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class OC_Geo{
+	/*
+	 * @brief returns the closest timezone to coordinates
+	 * @param (string) $latitude - Latitude
+	 * @param (string) $longitude - Longitude
+	 * @return (string) $timezone - closest timezone
+	 */
+	public static function timezone($latitude, $longitude){	
+		$alltimezones = DateTimeZone::listIdentifiers();
+		$variances = array();
+		//calculate for all timezones the system know
+		foreach($alltimezones as $timezone){
+			$datetimezoneobj = new DateTimeZone($timezone);
+			$locationinformations = $datetimezoneobj->getLocation();
+			$latitudeoftimezone = $locationinformations['latitude'];
+			$longitudeoftimezone = $locationinformations['longitude'];
+			$variances[abs($latitudeoftimezone - $latitude) + abs($longitudeoftimezone - $longitude)] = $timezone;
+		}
+		//sort array and return the timezone with the smallest difference
+		ksort($variances);
+		reset($variances);
+		return current($variances);
+	}
+}
\ No newline at end of file
diff --git a/lib/group/database.php b/lib/group/database.php
index f35f61434f087058ceddf8114cac30d79d5b49db..1afd4b5fe4cfb672c342ee3efa57afaf060ed9a2 100644
--- a/lib/group/database.php
+++ b/lib/group/database.php
@@ -130,7 +130,7 @@ class OC_Group_Database extends OC_Group_Backend {
 	 * removes the user from a group.
 	 */
 	public static function removeFromGroup( $uid, $gid ){
-		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*group_user` WHERE `uid` = ? AND `gid` = ?" );
+		$query = OC_DB::prepare( "DELETE FROM *PREFIX*group_user WHERE uid = ? AND gid = ?" );
 		$result = $query->execute( array( $uid, $gid ));
 
 		return true;
diff --git a/lib/helper.php b/lib/helper.php
index 2f71bdad2dc8e22e9126dae7d063a92ea894429f..0c6c73aa76b624c5f44272dd1883a9997869ad21 100644
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -25,6 +25,9 @@
  * Collection of useful functions
  */
 class OC_Helper {
+	private static $mimetypes=array();
+	private static $tmpFiles=array();
+	
 	/**
 	 * @brief Creates an url
 	 * @param $app app
@@ -37,8 +40,8 @@ class OC_Helper {
 		if( $app != '' ){
 			$app .= '/';
 			// Check if the app is in the app folder
-			if( file_exists( OC::$SERVERROOT . '/apps/'. $app.$file )){
-				$urlLinkTo =  OC::$WEBROOT . '/apps/' . $app . $file;
+			if( file_exists( OC::$APPSROOT . '/apps/'. $app.$file )){
+				$urlLinkTo =  OC::$APPSWEBROOT . '/apps/' . $app . $file;
 			}
 			else{
 				$urlLinkTo =  OC::$WEBROOT . '/' . $app . $file;
@@ -81,24 +84,27 @@ class OC_Helper {
 	 * Returns the path to the image.
 	 */
         public static function imagePath( $app, $image ){
-                // Read the selected theme from the config file
-                $theme=OC_Config::getValue( "theme" );
+		// Read the selected theme from the config file
+		$theme=OC_Config::getValue( "theme" );
 
-                // Check if the app is in the app folder
-                if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){
-                        return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image";
-                }elseif( file_exists( OC::$SERVERROOT."/apps/$app/img/$image" )){
-                        return OC::$WEBROOT."/apps/$app/img/$image";
-                }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){
-                        return OC::$WEBROOT."/themes/$theme/$app/img/$image";
-                }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){
-                        return OC::$WEBROOT."/$app/img/$image";
-                }elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )){
-                        return OC::$WEBROOT."/themes/$theme/core/img/$image";
-                }else{
-                        return OC::$WEBROOT."/core/img/$image";
-                }
-        }
+		// Check if the app is in the app folder
+		if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){
+			return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image";
+		}elseif( file_exists( OC::$APPSROOT."/apps/$app/img/$image" )){
+			return OC::$APPSWEBROOT."/apps/$app/img/$image";
+		}elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){
+			return OC::$WEBROOT."/themes/$theme/$app/img/$image";
+		}elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){
+			return OC::$WEBROOT."/$app/img/$image";
+		}elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )){
+			return OC::$WEBROOT."/themes/$theme/core/img/$image";
+		}elseif( file_exists( OC::$SERVERROOT."/core/img/$image" )){
+			return OC::$WEBROOT."/core/img/$image";
+		}else{
+			echo('image not found: image:'.$image.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);	
+			die();
+		}
+	}
 
 	/**
 	 * @brief get path to icon of file type
@@ -194,7 +200,7 @@ class OC_Helper {
 			$bytes *= $bytes_array[$matches[1]];
 		}
 
-		$bytes = intval(round($bytes, 2));
+		$bytes = round($bytes, 2);
 
 		return $bytes; 
 	}
@@ -267,6 +273,62 @@ class OC_Helper {
 			unlink($dir);
 		}
 	}
+
+	/**
+	 * get the mimetype form a local file
+	 * @param string path
+	 * @return string
+	 * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
+	 */
+	static function getMimeType($path){
+		$isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://');
+		$mimeType='application/octet-stream';
+		if ($mimeType=='application/octet-stream') {
+			if(count(self::$mimetypes)>0){
+				self::$mimetypes = include('mimetypes.fixlist.php');
+			}
+			$extention=strtolower(strrchr(basename($path), "."));
+			$extention=substr($extention,1);//remove leading .
+			$mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream';
+
+		}
+		if (@is_dir($path)) {
+			// directories are easy
+			return "httpd/unix-directory";
+		}
+		if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){
+			$info = @strtolower(finfo_file($finfo,$path));
+			if($info){
+				$mimeType=substr($info,0,strpos($info,';'));
+			}
+			finfo_close($finfo);
+		}
+		if (!$isWrapped and $mimeType=='application/octet-stream' && function_exists("mime_content_type")) {
+			// use mime magic extension if available
+			$mimeType = mime_content_type($path);
+		}
+		if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) {
+			// it looks like we have a 'file' command,
+			// lets see it it does have mime support
+			$path=str_replace("'","\'",$path);
+			$fp = popen("file -i -b '$path' 2>/dev/null", "r");
+			$reply = fgets($fp);
+			pclose($fp);
+
+			//trim the character set from the end of the response
+			$mimeType=substr($reply,0,strrpos($reply,' '));
+		}
+		if ($mimeType=='application/octet-stream') {
+			// Fallback solution: (try to guess the type by the file extension
+			if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){
+				self::$mimetypes=include('mimetypes.list.php');
+			}
+			$extention=strtolower(strrchr(basename($path), "."));
+			$extention=substr($extention,1);//remove leading .
+			$mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream';
+		}
+		return $mimeType;
+	}
 	
 	/**
 	 * @brief Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d.
@@ -340,4 +402,47 @@ class OC_Helper {
 		}
 		return false;
 	}
+	
+	/**
+	 * copy the contents of one stream to another
+	 * @param resource source
+	 * @param resource target
+	 * @return int the number of bytes copied
+	 */
+	public static function streamCopy($source,$target){
+		if(!$source or !$target){
+			return false;
+		}
+		$count=0;
+		while(!feof($source)){
+			$count+=fwrite($target,fread($source,8192));
+		}
+		return $count;
+	}
+	
+	/**
+	 * create a temporary file with an unique filename
+	 * @param string postfix
+	 * @return string
+	 *
+	 * temporary files are automatically cleaned up after the script is finished
+	 */
+	public static function tmpFile($postfix=''){
+		$file=get_temp_dir().'/'.md5(time().rand()).$postfix;
+		$fh=fopen($file,'w');
+		fclose($fh);
+		self::$tmpFiles[]=$file;
+		return $file;
+	}
+	
+	/**
+	 * remove all files created by self::tmpFile
+	 */
+	public static function cleanTmp(){
+		foreach(self::$tmpFiles as $file){
+			if(file_exists($file)){
+				unlink($file);
+			}
+		}
+	}
 }
diff --git a/lib/installer.php b/lib/installer.php
index b2f817e702f306e8e05518a9bf38a1cb5d50870c..2a9676998f6e537982fda061176aed2139d9c9ef 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -62,7 +62,7 @@ class OC_Installer{
 		
 		//download the file if necesary
 		if($data['source']=='http'){
-			$path=tempnam(get_temp_dir(),'oc_installer_');
+			$path=OC_Helper::tmpFile('.zip');
 			if(!isset($data['href'])){
 				OC_Log::write('core','No href specified when installing app from http',OC_Log::ERROR);
 				return false;
@@ -103,7 +103,7 @@ class OC_Installer{
 			return false;
 		}
 		$info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml');
-		$basedir=OC::$SERVERROOT.'/apps/'.$info['id'];
+		$basedir=OC::$APPSROOT.'/apps/'.$info['id'];
 		
 		//check if an app with the same id is already installed
 		if(self::isInstalled( $info['id'] )){
@@ -142,9 +142,6 @@ class OC_Installer{
 		
 		//remove temporary files
 		OC_Helper::rmdirr($extractDir);
-		if($data['source']=='http'){
-			unlink($path);
-		}
 		
 		//install the database
 		if(is_file($basedir.'/appinfo/database.xml')){
@@ -244,10 +241,10 @@ class OC_Installer{
 	 * If $enabled is false, apps are installed as disabled.
 	 */
 	public static function installShippedApps(){
-		$dir = opendir( OC::$SERVERROOT."/apps" );
+		$dir = opendir( OC::$APPSROOT."/apps" );
 		while( false !== ( $filename = readdir( $dir ))){
-			if( substr( $filename, 0, 1 ) != '.' and is_dir(OC::$SERVERROOT."/apps/$filename") ){
-				if( file_exists( OC::$SERVERROOT."/apps/$filename/appinfo/app.php" )){
+			if( substr( $filename, 0, 1 ) != '.' and is_dir(OC::$APPSROOT."/apps/$filename") ){
+				if( file_exists( OC::$APPSROOT."/apps/$filename/appinfo/app.php" )){
 					if(!OC_Installer::isInstalled($filename)){
 						$info = OC_Installer::installShippedApp($filename);
 						$enabled = isset($info['default_enable']);
@@ -270,15 +267,15 @@ class OC_Installer{
 	 */
 	public static function installShippedApp($app){
 		//install the database
-		if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/database.xml")){
-			OC_DB::createDbFromStructure(OC::$SERVERROOT."/apps/$app/appinfo/database.xml");
+		if(is_file(OC::$APPSROOT."/apps/$app/appinfo/database.xml")){
+			OC_DB::createDbFromStructure(OC::$APPSROOT."/apps/$app/appinfo/database.xml");
 		}
 
 		//run appinfo/install.php
-		if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/install.php")){
-			include(OC::$SERVERROOT."/apps/$app/appinfo/install.php");
+		if(is_file(OC::$APPSROOT."/apps/$app/appinfo/install.php")){
+			include(OC::$APPSROOT."/apps/$app/appinfo/install.php");
 		}
-		$info=OC_App::getAppInfo(OC::$SERVERROOT."/apps/$app/appinfo/info.xml");
+		$info=OC_App::getAppInfo(OC::$APPSROOT."/apps/$app/appinfo/info.xml");
 		OC_Appconfig::setValue($app,'installed_version',$info['version']);
 		return $info;
 	}
diff --git a/lib/l10n.php b/lib/l10n.php
index a5544eb3a2766c95e8aaed053ce05e946f04495c..636326f9864a84a4b3b718c09cf3ec4bb684eeb1 100644
--- a/lib/l10n.php
+++ b/lib/l10n.php
@@ -243,8 +243,8 @@ class OC_L10N{
 		$i18ndir = OC::$SERVERROOT.'/core/l10n/';
 		if($app != ''){
 			// Check if the app is in the app folder
-			if(file_exists(OC::$SERVERROOT.'/apps/'.$app.'/l10n/')){
-				$i18ndir = OC::$SERVERROOT.'/apps/'.$app.'/l10n/';
+			if(file_exists(OC::$APPSROOT.'/apps/'.$app.'/l10n/')){
+				$i18ndir = OC::$APPSROOT.'/apps/'.$app.'/l10n/';
 			}
 			else{
 				$i18ndir = OC::$SERVERROOT.'/'.$app.'/l10n/';
diff --git a/lib/log.php b/lib/log.php
index 446ddd48848c3809485245f11f78fb51624e0566..4e450a027f5367289a2cc67697f3d9c7b00e4886 100644
--- a/lib/log.php
+++ b/lib/log.php
@@ -3,7 +3,7 @@
  * ownCloud
  *
  * @author Robin Appelman
- * @copyright 2011 Robin Appelman icewind1991@gmail.com
+ * @copyright 2012 Robin Appelman icewind1991@gmail.com
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -50,25 +50,29 @@ class OC_Log{
 			fclose($fh);
 		}
 	}
-	
-	public static function getEntries(){
+
+	/**
+	 * get entries from the log in reverse chronological order
+	 * @param int limit
+	 * @param int offset
+	 * @return array
+	 */
+	public static function getEntries($limit=50,$offset=0){
 		$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
 		$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
 		$entries=array();
 		if(!file_exists($logFile)){
 			return array();
 		}
-		$fh=fopen($logFile,'r');
-		if($fh === false){ // Unable to read log file!
+		$contents=file($logFile);
+		if(!$contents){//error while reading log
 			return array();
 		}
-		while(!feof($fh)){
-			$line=fgets($fh);
-			if($line){
-				$entries[]=json_decode($line);
-			}
+		$end=max(count($contents)-$offset-1,0);
+		$start=max($end-$limit,0);
+		for($i=$end;$i>$start;$i--){
+			$entries[]=json_decode($contents[$i]);
 		}
-		fclose($fh);
 		return $entries;
 	}
 }
diff --git a/lib/search.php b/lib/search.php
index f6f805bfe6587e2dc76ac25a91aad504f0e045bc..6b33fa38140fb0ce21a729a64a0a06e13b702a62 100644
--- a/lib/search.php
+++ b/lib/search.php
@@ -29,7 +29,7 @@ class OC_Search{
 	
 	/**
 	 * register a new search provider to be used
-	 * @param OC_Search_Provider $provider
+	 * @param string $provider class name of a OC_Search_Provider
 	 */
 	public static function registerProvider($provider){
 		self::$providers[]=$provider;
@@ -43,7 +43,7 @@ class OC_Search{
 	public static function search($query){
 		$results=array();
 		foreach(self::$providers as $provider){
-			$results=array_merge($results,$provider->search($query));
+			$results=array_merge($results, $provider::search($query));
 		}
 		return $results;
 	}
diff --git a/lib/search/provider.php b/lib/search/provider.php
index cceed8b04a38f7353003650a54b61dcddc3994e2..9487ca51f2b94640f33b09535578e0a0ac6b6a5c 100644
--- a/lib/search/provider.php
+++ b/lib/search/provider.php
@@ -2,15 +2,11 @@
 /**
  * provides search functionalty
  */
-abstract class OC_Search_Provider{
-	public function __construct(){
-		OC_Search::registerProvider($this);
-	}
-
+interface OC_Search_Provider {
 	/**
 	 * search for $query
 	 * @param string $query
 	 * @return array An array of OC_Search_Result's
 	 */
-	abstract function search($query);
+	static function search($query);
 }
diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php
index 34803c75aeba18a32dc88f94fb8e4b506739e9a7..3bdb3bcd2af197718b9f91ae772cc0cf410dc100 100644
--- a/lib/search/provider/file.php
+++ b/lib/search/provider/file.php
@@ -1,15 +1,15 @@
 <?php
 
-class OC_Search_Provider_File extends OC_Search_Provider{
-	function search($query){
+class OC_Search_Provider_File implements OC_Search_Provider{
+	static function search($query){
 		$files=OC_FileCache::search($query,true);
 		$results=array();
 		foreach($files as $fileData){
 			$file=$fileData['path'];
-			if($fileData['mime']=='httpd/unix-directory'){
+			$mime=$fileData['mimetype'];
+			if($mime=='httpd/unix-directory'){
 				$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'index.php' ).'?dir='.$file,'Files');
 			}else{
-				$mime=$fileData['mime'];
 				$mimeBase=$fileData['mimepart'];
 				switch($mimeBase){
 					case 'audio':
diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1e0fa0e1d9cadffdca404212b1630ae87a80905
--- /dev/null
+++ b/lib/streamwrappers.php
@@ -0,0 +1,306 @@
+<?php
+global $FAKEDIRS;
+$FAKEDIRS=array();
+
+class OC_FakeDirStream{
+	public static $dirs=array();
+	private $name;
+	private $index;
+
+	public function dir_opendir($path,$options){
+		global $FAKEDIRS;
+		$url=parse_url($path);
+		$this->name=substr($path,strlen('fakedir://'));
+		$this->index=0;
+		if(!isset(self::$dirs[$this->name])){
+			self::$dirs[$this->name]=array();
+		}
+		return true;
+	}
+
+	public function dir_readdir(){
+		if($this->index>=count(self::$dirs[$this->name])){
+			return false;
+		}
+		$filename=self::$dirs[$this->name][$this->index];
+		$this->index++;
+		return $filename;
+	}
+
+	public function dir_closedir() {
+		$this->name='';
+		return true;
+	}
+
+	public function dir_rewinddir() {
+		$this->index=0;
+		return true;
+	}
+}
+
+class OC_StaticStreamWrapper {
+	public $context;
+	protected static $data = array();
+
+	protected $path    = '';
+	protected $pointer = 0;
+	protected $writable = false;
+
+	public function stream_close() {}
+
+	public function stream_eof() {
+		return $this->pointer >= strlen(self::$data[$this->path]);
+	}
+
+	public function stream_flush() {}
+
+	public function stream_open($path, $mode, $options, &$opened_path) {
+		switch ($mode[0]) {
+			case 'r':
+				if (!isset(self::$data[$path])) return false;
+				$this->path = $path;
+				$this->writable = isset($mode[1]) && $mode[1] == '+';
+				break;
+			case 'w':
+				self::$data[$path] = '';
+				$this->path = $path;
+				$this->writable = true;
+				break;
+			case 'a':
+				if (!isset(self::$data[$path])) self::$data[$path] = '';
+				$this->path = $path;
+				$this->writable = true;
+				$this->pointer = strlen(self::$data[$path]);
+				break;
+			case 'x':
+				if (isset(self::$data[$path])) return false;
+				$this->path = $path;
+				$this->writable = true;
+				break;
+			case 'c':
+				if (!isset(self::$data[$path])) self::$data[$path] = '';
+				$this->path = $path;
+				$this->writable = true;
+				break;
+			default:
+				return false;
+		}
+		$opened_path = $this->path;
+		return true;
+	}
+
+	public function stream_read($count) {
+		$bytes = min(strlen(self::$data[$this->path]) - $this->pointer, $count);
+		$data = substr(self::$data[$this->path], $this->pointer, $bytes);
+		$this->pointer += $bytes;
+		return $data;
+	}
+
+	public function stream_seek($offset, $whence = SEEK_SET) {
+		$len = strlen(self::$data[$this->path]);
+		switch ($whence) {
+			case SEEK_SET:
+				if ($offset <= $len) {
+					$this->pointer = $offset;
+					return true;
+				}
+				break;
+			case SEEK_CUR:
+				if ($this->pointer + $offset <= $len) {
+					$this->pointer += $offset;
+					return true;
+				}
+				break;
+			case SEEK_END:
+				if ($len + $offset <= $len) {
+					$this->pointer = $len + $offset;
+					return true;
+				}
+				break;
+		}
+		return false;
+	}
+
+	public function stream_stat() {
+		$size = strlen(self::$data[$this->path]);
+		$time = time();
+		return array(
+			0 => 0,
+			'dev' => 0,
+			1 => 0,
+			'ino' => 0,
+			2 => 0777,
+			'mode' => 0777,
+			3 => 1,
+			'nlink' => 1,
+			4 => 0,
+			'uid' => 0,
+			5 => 0,
+			'gid' => 0,
+			6 => '',
+			'rdev' => '',
+			7 => $size,
+			'size' => $size,
+			8 => $time,
+			'atime' => $time,
+			9 => $time,
+			'mtime' => $time,
+			10 => $time,
+			'ctime' => $time,
+			11 => -1,
+			'blksize' => -1,
+			12 => -1,
+			'blocks' => -1,
+		);
+	}
+
+	public function stream_tell() {
+		return $this->pointer;
+	}
+
+	public function stream_write($data) {
+		if (!$this->writable) return 0;
+		$size = strlen($data);
+		$len = strlen(self::$data[$this->path]);
+		if ($this->stream_eof()) {
+			self::$data[$this->path] .= $data;
+		} else {
+			self::$data[$this->path] = substr_replace(
+				self::$data[$this->path],
+				$data,
+				$this->pointer
+			);
+		}
+		$this->pointer += $size;
+		return $size;
+	}
+
+	public function unlink($path) {
+		if (isset(self::$data[$path])) {
+			unset(self::$data[$path]);
+		}
+		return true;
+	}
+
+	public function url_stat($path) {
+		if (isset(self::$data[$path])) {
+			$size = strlen(self::$data[$path]);
+			$time = time();
+			return array(
+				0 => 0,
+				'dev' => 0,
+				1 => 0,
+				'ino' => 0,
+				2 => 0777,
+				'mode' => 0777,
+				3 => 1,
+				'nlink' => 1,
+				4 => 0,
+				'uid' => 0,
+				5 => 0,
+				'gid' => 0,
+				6 => '',
+				'rdev' => '',
+				7 => $size,
+				'size' => $size,
+				8 => $time,
+				'atime' => $time,
+				9 => $time,
+				'mtime' => $time,
+				10 => $time,
+				'ctime' => $time,
+				11 => -1,
+				'blksize' => -1,
+				12 => -1,
+				'blocks' => -1,
+			);
+		}
+		return false;
+	}
+}
+
+/**
+ * stream wrapper that provides a callback on stream close
+ */
+class OC_CloseStreamWrapper{
+	public static $callBacks=array();
+	private $path='';
+	private $source;
+	private static $open=array();
+	public function stream_open($path, $mode, $options, &$opened_path){
+		$path=substr($path,strlen('close://'));
+		$this->path=$path;
+		$this->source=fopen($path,$mode);
+		if(is_resource($this->source)){
+			$this->meta=stream_get_meta_data($this->source);
+		}
+		self::$open[]=$path;
+		return is_resource($this->source);
+	}
+
+	public function stream_seek($offset, $whence=SEEK_SET){
+		fseek($this->source,$offset,$whence);
+	}
+
+	public function stream_tell(){
+		return ftell($this->source);
+	}
+
+	public function stream_read($count){
+		return fread($this->source,$count);
+	}
+
+	public function stream_write($data){
+		return fwrite($this->source,$data);
+	}
+
+	public function stream_set_option($option,$arg1,$arg2){
+		switch($option){
+			case STREAM_OPTION_BLOCKING:
+				stream_set_blocking($this->source,$arg1);
+				break;
+			case STREAM_OPTION_READ_TIMEOUT:
+				stream_set_timeout($this->source,$arg1,$arg2);
+				break;
+			case STREAM_OPTION_WRITE_BUFFER:
+				stream_set_write_buffer($this->source,$arg1,$arg2);
+		}
+	}
+
+	public function stream_stat(){
+		return fstat($this->source);
+	}
+
+	public function stream_lock($mode){
+		flock($this->source,$mode);
+	}
+
+	public function stream_flush(){
+		return fflush($this->source);
+	}
+
+	public function stream_eof(){
+		return feof($this->source);
+	}
+
+	public function url_stat($path) {
+		$path=substr($path,strlen('close://'));
+		if(file_exists($path)){
+			return stat($path);
+		}else{
+			return false;
+		}
+	}
+
+	public function stream_close(){
+		fclose($this->source);
+		if(isset(self::$callBacks[$this->path])){
+			call_user_func(self::$callBacks[$this->path],$this->path);
+		}
+	}
+
+	public function unlink($path){
+		$path=substr($path,strlen('close://'));
+		return unlink($path);
+	}
+}
diff --git a/lib/template.php b/lib/template.php
index d991759fbcd96b3fbca7a60eccd1d13cdfe6d973..eea2925975c27be02877eb423315312b8d11654f 100644
--- a/lib/template.php
+++ b/lib/template.php
@@ -171,7 +171,7 @@ class OC_Template{
 		// Check if it is a app template or not.
 		if( $app != "" ){
 			// Check if the app is in the app folder or in the root
-			if( file_exists( OC::$SERVERROOT."/apps/$app/templates/" )){
+			if( file_exists( OC::$APPSROOT."/apps/$app/templates/" )){
 				// Check if the template is overwritten by the selected theme
 				if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name$fext.php" )){
 					$template = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name$fext.php";
@@ -179,12 +179,12 @@ class OC_Template{
 				}elseif( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name.php" )){
 					$template = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name.php";
 					$path = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/";
-				}elseif( OC::$SERVERROOT."/apps/$app/templates/"."$name$fext.php" ){
-					$template = OC::$SERVERROOT."/apps/$app/templates/"."$name$fext.php";
-					$path = OC::$SERVERROOT."/apps/$app/templates/";
+				}elseif( OC::$APPSROOT."/apps/$app/templates/"."$name$fext.php" ){
+					$template = OC::$APPSROOT."/apps/$app/templates/"."$name$fext.php";
+					$path = OC::$APPSROOT."/apps/$app/templates/";
 				}else{
-					$template = OC::$SERVERROOT."/apps/$app/templates/"."$name.php";
-					$path = OC::$SERVERROOT."/apps/$app/templates/";
+					$template = OC::$APPSROOT."/apps/$app/templates/"."$name.php";
+					$path = OC::$APPSROOT."/apps/$app/templates/";
 				}
 			}else{
 				// Check if the template is overwritten by the selected theme
@@ -197,12 +197,15 @@ class OC_Template{
 				}elseif( file_exists( OC::$SERVERROOT."/$app/templates/"."$name$fext.php" )){
 					$template = OC::$SERVERROOT."/$app/templates/"."$name$fext.php";
 					$path = OC::$SERVERROOT."/$app/templates/";
-				}else{
+				}elseif( file_exists( OC::$SERVERROOT."/$app/templates/"."$name.php" )){
 					$template = OC::$SERVERROOT."/$app/templates/"."$name.php";
 					$path = OC::$SERVERROOT."/$app/templates/";
+				}else{
+					echo('template not found: template:'.$name.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);	
+					die();
 				}
 
-			}
+			}	
 		}else{
 			// Check if the template is overwritten by the selected theme
 			if( file_exists( OC::$SERVERROOT."/themes/$theme/core/templates/"."$name$fext.php" )){
@@ -346,52 +349,76 @@ class OC_Template{
 
 			// Add the core js files or the js files provided by the selected theme
 			foreach(OC_Util::$scripts as $script){
-				if(is_file(OC::$SERVERROOT."/themes/$theme/apps/$script$fext.js" )){
+				// Is it in 3rd party?
+				if(is_file(OC::$THIRDPARTYROOT."/$script.js" )){
+					$page->append( "jsfiles", OC::$THIRDPARTYWEBROOT."/$script.js" );
+
+				// Is it in apps and overwritten by the theme?
+				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/apps/$script$fext.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/apps/$script$fext.js" );
 				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/apps/$script.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/apps/$script.js" );
 
-				}elseif(is_file(OC::$SERVERROOT."/apps/$script$fext.js" )){
-					$page->append( "jsfiles", OC::$WEBROOT."/apps/$script$fext.js" );
-				}elseif(is_file(OC::$SERVERROOT."/apps/$script.js" )){
-					$page->append( "jsfiles", OC::$WEBROOT."/apps/$script.js" );
+				// Is it part of an app?
+				}elseif(is_file(OC::$APPSROOT."/apps/$script$fext.js" )){
+					$page->append( "jsfiles", OC::$APPSWEBROOT."/apps/$script$fext.js" );
+				}elseif(is_file(OC::$APPSROOT."/apps/$script.js" )){
+					$page->append( "jsfiles", OC::$APPSWEBROOT."/apps/$script.js" );
 
+				// Is it in the owncloud root but overwritten by the theme?
 				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/$script$fext.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/$script$fext.js" );
 				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/$script.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/$script.js" );
-
+	
+				// Is it in the owncloud root ?
 				}elseif(is_file(OC::$SERVERROOT."/$script$fext.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/$script$fext.js" );
 				}elseif(is_file(OC::$SERVERROOT."/$script.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/$script.js" );
 
+				// Is in core but overwritten by a theme?
 				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/core/$script$fext.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/core/$script$fext.js" );
 				}elseif(is_file(OC::$SERVERROOT."/themes/$theme/core/$script.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/core/$script.js" );
 
+				// Is it in core?
 				}elseif(is_file(OC::$SERVERROOT."/core/$script$fext.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/core/$script$fext.js" );
-				}else{
+				}elseif(is_file(OC::$SERVERROOT."/core/$script.js" )){
 					$page->append( "jsfiles", OC::$WEBROOT."/core/$script.js" );
 
+				}else{
+					echo('js file not found: script:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);	
+					die();
+
 				}
 			}
 			// Add the css files
 			foreach(OC_Util::$styles as $style){
-				if(is_file(OC::$SERVERROOT."/apps/$style$fext.css" )){
-					$page->append( "cssfiles", OC::$WEBROOT."/apps/$style$fext.css" );
-				}elseif(is_file(OC::$SERVERROOT."/apps/$style.css" )){
-					$page->append( "cssfiles", OC::$WEBROOT."/apps/$style.css" );
+				// is it in 3rdparty?
+				if(is_file(OC::$THIRDPARTYROOT."/$style.css" )){
+					$page->append( "cssfiles", OC::$THIRDPARTYWEBROOT."/$style.css" );
+				// or in apps?
+				}elseif(is_file(OC::$APPSROOT."/apps/$style$fext.css" )){
+					$page->append( "cssfiles", OC::$APPSWEBROOT."/apps/$style$fext.css" );
+				}elseif(is_file(OC::$APPSROOT."/apps/$style.css" )){
+					$page->append( "cssfiles", OC::$APPSWEBROOT."/apps/$style.css" );
+				// or in the owncloud root?
 				}elseif(is_file(OC::$SERVERROOT."/$style$fext.css" )){
 					$page->append( "cssfiles", OC::$WEBROOT."/$style$fext.css" );
 				}elseif(is_file(OC::$SERVERROOT."/$style.css" )){
 					$page->append( "cssfiles", OC::$WEBROOT."/$style.css" );
+				// or in core ?	
 				}elseif(is_file(OC::$SERVERROOT."/core/$style$fext.css" )){
 					$page->append( "cssfiles", OC::$WEBROOT."/core/$style$fext.css" );
-				}else{
+				}elseif(is_file(OC::$SERVERROOT."/core/$style.css" )){
 					$page->append( "cssfiles", OC::$WEBROOT."/core/$style.css" );
+
+				}else{
+					echo('css file not found: style:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);	
+					die();
 				}
 			}
                         // Add the theme css files. you can override the default values here
diff --git a/lib/user.php b/lib/user.php
index 34f44f572e0b1b533ff125dd4830eaec531542e4..fda19a33154bf97add79e40e468b774a2c175f3d 100644
--- a/lib/user.php
+++ b/lib/user.php
@@ -195,8 +195,9 @@ class OC_User {
 		if( $run ){
 			$uid=self::checkPassword( $uid, $password );
 			if($uid){
-				OC_Crypt::init($uid,$password);
-				return self::setUserId($uid);
+				self::setUserId($uid);
+				OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password ));
+				return true;
 			}
 		}
 		return false;
@@ -209,7 +210,6 @@ class OC_User {
 	 */
 	public static function setUserId($uid) {
 		$_SESSION['user_id'] = $uid;
-		OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid ));
 		return true;
 	}
 
@@ -321,7 +321,10 @@ class OC_User {
 		$users=array();
 		foreach(self::$_usedBackends as $backend){
 			if($backend->implementsActions(OC_USER_BACKEND_GET_USERS)){
-				$users=array_merge($users,$backend->getUsers());
+				$backendUsers=$backend->getUsers();
+				if(is_array($backendUsers)){
+					$users=array_merge($users,$backendUsers);
+				}
 			}
 		}
 		return $users;
diff --git a/lib/user/database.php b/lib/user/database.php
index 452709c1fb977aacbcdcc63aace597f4344ece1e..3eade276dd9e05a97d2e0a8d73e2c4a0652759e4 100644
--- a/lib/user/database.php
+++ b/lib/user/database.php
@@ -33,12 +33,28 @@
  *
  */
 
+require_once 'phpass/PasswordHash.php';
+
 /**
  * Class for user management in a SQL Database (e.g. MySQL, SQLite)
  */
 class OC_User_Database extends OC_User_Backend {
 	static private $userGroupCache=array();
+	/**
+	 * @var PasswordHash
+	 */
+	static private $hasher=null;
+	
+	private function getHasher(){
+		if(!self::$hasher){
+			//we don't want to use DES based crypt(), since it doesn't return a has with a recognisable prefix
+			$forcePortable=(CRYPT_BLOWFISH!=1);
+			self::$hasher=new PasswordHash(8,$forcePortable);
+		}
+		return self::$hasher;
 
+	}
+	
 	/**
 	 * @brief Create a new user
 	 * @param $uid The username of the user to create
@@ -51,10 +67,11 @@ class OC_User_Database extends OC_User_Backend {
 	public function createUser( $uid, $password ){
 		if( $this->userExists($uid) ){
 			return false;
-		}
-		else{
+		}else{
+			$hasher=$this->getHasher();
+			$hash = $hasher->HashPassword($password);
 			$query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" );
-			$result = $query->execute( array( $uid, sha1( $password )));
+			$result = $query->execute( array( $uid, $hash));
 
 			return $result ? true : false;
 		}
@@ -84,8 +101,10 @@ class OC_User_Database extends OC_User_Backend {
 	 */
 	public function setPassword( $uid, $password ){
 		if( $this->userExists($uid) ){
+			$hasher=$this->getHasher();
+			$hash = $hasher->HashPassword($password);
 			$query = OC_DB::prepare( "UPDATE *PREFIX*users SET password = ? WHERE uid = ?" );
-			$result = $query->execute( array( sha1( $password ), $uid ));
+			$result = $query->execute( array( $hash, $uid ));
 
 			return true;
 		}
@@ -103,12 +122,28 @@ class OC_User_Database extends OC_User_Backend {
 	 * Check if the password is correct without logging in the user
 	 */
 	public function checkPassword( $uid, $password ){
-		$query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users WHERE uid LIKE ? AND password = ?" );
-		$result = $query->execute( array( $uid, sha1( $password )));
+		$query = OC_DB::prepare( "SELECT uid, password FROM *PREFIX*users WHERE uid LIKE ?" );
+		$result = $query->execute( array( $uid));
 
 		$row=$result->fetchRow();
 		if($row){
-			return $row['uid'];
+			$storedHash=$row['password'];
+			if (substr($storedHash,0,1)=='$'){//the new phpass based hashing
+				$hasher=$this->getHasher();
+				if($hasher->CheckPassword($password, $storedHash)){
+					return $row['uid'];
+				}else{
+					return false;
+				}
+			}else{//old sha1 based hashing
+				if(sha1($password)==$storedHash){
+					//upgrade to new hashing
+					$this->setPassword($row['uid'],$password);
+					return $row['uid'];
+				}else{
+					return false;
+				}
+			}
 		}else{
 			return false;
 		}
diff --git a/lib/util.php b/lib/util.php
index 1b1e29b674980f89133f81ca1006160591640184..fa5b3daaab6ccaf788dffc593e64e28215a9d227 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -8,6 +8,7 @@ class OC_Util {
 	public static $scripts=array();
 	public static $styles=array();
 	public static $headers=array();
+	private static $rootMounted=false;
 	private static $fsSetup=false;
 
 	// Can be set up
@@ -35,9 +36,12 @@ class OC_Util {
 			$user = OC_User::getUser();
 		}
 
-		if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem
-			//first set up the local "root" storage
+		//first set up the local "root" storage
+		if(!self::$rootMounted){
 			OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/');
+			self::$rootMounted=true;
+		}
+		if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem
 
 			OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root";
 			if( !is_dir( OC::$CONFIG_DATADIRECTORY )){
@@ -62,7 +66,7 @@ class OC_Util {
 	 * @return array
 	 */
 	public static function getVersion(){
-		return array(3,00,1);
+		return array(3,00,3);
 	}
 
 	/**
diff --git a/owncloud.db.filesystem b/owncloud.db.filesystem
deleted file mode 100644
index 082977a37eff00bc5a0e6aeffa6a8fa9f57025fd..0000000000000000000000000000000000000000
Binary files a/owncloud.db.filesystem and /dev/null differ
diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php
new file mode 100644
index 0000000000000000000000000000000000000000..600ebefcecef77d5f5ae8c9184ec964d4b60f9b1
--- /dev/null
+++ b/settings/ajax/getlog.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Copyright (c) 2012, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+OC_JSON::checkAdminUser();
+
+$count=(isset($_GET['count']))?$_GET['count']:50;
+$offset=(isset($_GET['offset']))?$_GET['offset']:0;
+
+$entries=OC_Log::getEntries($count,$offset);
+OC_JSON::success(array("data" => $entries));
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
index d4e3c58ac113d91f19fe8dd2ba72367c404dd402..f59017600ac5b991069a2b057ed63fcea14cb7e0 100644
--- a/settings/ajax/setquota.php
+++ b/settings/ajax/setquota.php
@@ -1,18 +1,37 @@
 <?php
+/**
+ * Copyright (c) 2012, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
 
 // Init owncloud
 require_once('../../lib/base.php');
 
 OC_JSON::checkAdminUser();
 
-$username = $_POST["username"];
+$username = isset($_POST["username"])?$_POST["username"]:'';
 
 //make sure the quota is in the expected format
-$quota= OC_Helper::computerFileSize($_POST["quota"]);
-$quota=OC_Helper::humanFileSize($quota);
+$quota=$_POST["quota"];
+if($quota!='none' and $quota!='default'){
+	$quota= OC_Helper::computerFileSize($quota);
+	if($quota==0){
+		$quota='default';
+	}else{
+		$quota=OC_Helper::humanFileSize($quota);
+	}
+}
 
 // Return Success story
-OC_Preferences::setValue($username,'files','quota',$quota);
+if($username){
+	OC_Preferences::setValue($username,'files','quota',$quota);
+}else{//set the default quota when no username is specified
+	if($quota=='default'){//'default' as default quota makes no sense
+		$quota='none';
+	}
+	OC_Appconfig::setValue('files','default_quota',$quota);
+}
 OC_JSON::success(array("data" => array( "username" => $username ,'quota'=>$quota)));
 
 ?>
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 8d89cee6ec04777bead223cb5e88545cc5e3423b..e80de0f1ad23ab09007794b9d8b80c068df97dc5 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -26,11 +26,20 @@ tr:hover>td.password>span { margin:0; cursor:pointer; }
 tr:hover>td.remove>img, tr:hover>td.password>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; }
 tr:hover>td.remove>img { float:right; }
 li.selected { background-color:#ddd; }
-#content>table:not(.nostyle) { margin-top:6.5em; }
+#content>table:not(.nostyle) { margin-top:3em; }
 table:not(.nostyle) { width:100%; }
-
+#rightcontent { padding-left: 1em; }
+td.quota { position:relative }
+div.quota { float:right; display:block; position:absolute; right:25em; top:0; }
+select.quota { position:absolute; left:0; top:0; width:10em; }
+input.quota-other { display:none; position:absolute; left:0.1em; top:0.1em; width:7em; border:none; -webkit-box-shadow: none -mox-box-shadow:none ; box-shadow:none; }
+div.quota>span { position:absolute; right:0em; white-space:nowrap; top: 0.7em }
+select.quota.active { background: #fff; }
 
 /* APPS */
 li { color:#888; }
 li.active { color:#000; }
 span.version { margin-left:3em; color:#ddd; }
+
+/* LOF */
+#log { white-space:normal; }
\ No newline at end of file
diff --git a/settings/js/log.js b/settings/js/log.js
new file mode 100644
index 0000000000000000000000000000000000000000..3814d9c10bf449d2a53fa3aae4ff4f682932601a
--- /dev/null
+++ b/settings/js/log.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2012, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OC.Log={
+	levels:['Debug','Info','Warning','Error','Fatal'],
+	loaded:50,//are initially loaded
+	getMore:function(){
+		$.get(OC.filePath('settings','ajax','getlog.php'),{offset:OC.Log.loaded},function(result){
+			if(result.status=='success'){
+				OC.Log.addEntries(result.data);
+			}
+		});
+		OC.Log.loaded+=50;
+	},
+	addEntries:function(entries){
+		for(var i=0;i<entries.length;i++){
+			var entry=entries[i];
+			var row=$('<tr/>');
+			var levelTd=$('<td/>');
+			levelTd.text(OC.Log.levels[entry.level]);
+			row.append(levelTd);
+			
+			var appTd=$('<td/>');
+			appTd.text(entry.app);
+			row.append(appTd);
+			
+			var messageTd=$('<td/>');
+			messageTd.text(entry.message);
+			row.append(messageTd);
+			
+			var timeTd=$('<td/>');
+			timeTd.text(formatDate(entry.time));
+			row.append(timeTd);
+			$('#log').append(row);
+		}
+	}
+}
+
+$(document).ready(function(){
+	$('#moreLog').click(function(){
+		OC.Log.getMore();
+	})
+});
diff --git a/settings/js/users.js b/settings/js/users.js
index c9b1d855db0f7898e7945f7e2ff2c5238827c9ab..eed93d3b30389fcd600c2b0976e1aef253733196 100644
--- a/settings/js/users.js
+++ b/settings/js/users.js
@@ -5,6 +5,18 @@
  */
 
 $(document).ready(function(){
+	function setQuota(uid,quota,ready){
+		$.post(
+			OC.filePath('settings','ajax','setquota.php'),
+			{username:uid,quota:quota},
+			function(result){
+				if(ready){
+					ready(result.data.quota);
+				}
+			}
+		);
+	}
+	
 	function applyMultiplySelect(element){
 		var checked=[];
 		var user=element.data('username');
@@ -82,48 +94,66 @@ $(document).ready(function(){
 	$('td.password').live('click',function(event){
 		$(this).children('img').click();
 	});
-
-	$('td.quota>img').live('click',function(event){
-		event.stopPropagation();
-		var img=$(this);
-		var uid=img.parent().parent().data('uid');
-		var input=$('<input>');
-		var quota=img.parent().children('span').text();
-		if(quota=='None'){
-			quota='';
+	
+	$('select.quota').live('change',function(){
+		var select=$(this);
+		var uid=$(this).parent().parent().data('uid');
+		var quota=$(this).val();
+		var other=$(this).next();
+		if(quota!='other'){
+			other.hide();
+			select.data('previous',quota);
+			setQuota(uid,quota);
+		}else{
+			other.show();
+			select.addClass('active');
+			other.focus();
 		}
-		input.val(quota);
-		img.css('display','none');
-		img.parent().children('span').replaceWith(input);
-		input.focus();
-		input.keypress(function(event) {
-			if(event.keyCode == 13) {
-				$(this).parent().attr('data-quota',$(this).val());
-				if($(this).val().length>0){
-					$.post(
-						OC.filePath('settings','ajax','setquota.php'),
-						{username:uid,quota:$(this).val()},
-						function(result){
-							img.parent().children('span').text(result.data.quota)
-							$(this).parent().attr('data-quota',result.data.quota);
-						}
-					);
-					input.blur();
+	});
+	$('select.quota').each(function(i,select){
+		$(select).data('previous',$(select).val());
+	})
+	
+	$('input.quota-other').live('change',function(){
+		var uid=$(this).parent().parent().data('uid');
+		var quota=$(this).val();
+		var select=$(this).prev();
+		var other=$(this);
+		if(quota){
+			setQuota(uid,quota,function(quota){
+				select.children().attr('selected',null);
+				var existingOption=select.children().filter(function(i,option){
+					return ($(option).val()==quota);
+				});
+				if(existingOption.length){
+					existingOption.attr('selected','selected');
 				}else{
-					input.blur();
+					var option=$('<option/>');
+					option.attr('selected','selected').attr('value',quota).text(quota);
+					select.children().last().before(option);
 				}
-			}
-		});
-		input.blur(function(){
-			var quota=$(this).parent().attr('data-quota');
-			$(this).replaceWith($('<span>'+quota+'</span>'));
-			img.css('display','');
-		});
-	});
-	$('td.quota').live('click',function(event){
-		$(this).children('img').click();
+				select.val(quota);
+				select.removeClass('active');
+				other.val(null);
+				other.hide();
+			});
+		}else{
+			var previous=select.data('previous');
+			select.children().attr('selected',null);
+			select.children().each(function(i,option){
+				if($(option).val()==previous){
+					$(option).attr('selected','selected');
+				}
+			});
+			select.removeClass('active');
+			other.hide();
+		}
 	});
 	
+	$('input.quota-other').live('blur',function(){
+		$(this).change();
+	})
+	
 	$('#newuser').submit(function(event){
 		event.preventDefault();
 		var username=$('#newusername').val();
@@ -157,7 +187,13 @@ $(document).ready(function(){
 		select.data('username',username);
 		select.data('userGroups',groups.join(', '));
 		tr.find('td.groups').empty();
-		$.each($('#content table').data('groups').split(', '),function(i,group){
+		var allGroups=$('#content table').data('groups').split(', ');
+		for(var i=0;i<groups.length;i++){
+			if(allGroups.indexOf(groups[i])==-1){
+				allGroups.push(groups[i]);
+			}
+		}
+		$.each(allGroups,function(i,group){
 			select.append($('<option value="'+group+'">'+group+'</option>'));
 		});
 		tr.find('td.groups').append(select);
@@ -166,5 +202,9 @@ $(document).ready(function(){
 		}
 		applyMultiplySelect(select);
 		$('#content table tbody').last().after(tr);
+		
+		tr.find('select.quota option').attr('selected',null);
+		tr.find('select.quota option').first().attr('selected','selected');
+		tr.find('select.quota').data('previous','default');
 	});
 });
diff --git a/settings/log.php b/settings/log.php
index 21303c2170ffeb6dde86bf5151dd68a9cc83829d..946f2b6f8e5678b428b326cf021dcac1842c4b1a 100644
--- a/settings/log.php
+++ b/settings/log.php
@@ -3,7 +3,7 @@
  * ownCloud
  *
  * @author Robin Appelman
- * @copyright 2011 Robin Appelman icewind1991@gmail.com
+ * @copyright 2012 Robin Appelman icewind1991@gmail.com
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -30,6 +30,9 @@ OC_App::setActiveNavigationEntry( "core_log" );
 
 $entries=OC_Log::getEntries();
 
+OC_Util::addScript('settings','log');
+OC_Util::addStyle('settings','settings');
+
 function compareEntries($a,$b){
 	return $b->time - $a->time;
 }
diff --git a/settings/templates/log.php b/settings/templates/log.php
index bcf5258f5f5047632ad3324e6e8eca5885070ad4..da5defc320e8c3c6b1f439fcba38742bc5ed32c1 100644
--- a/settings/templates/log.php
+++ b/settings/templates/log.php
@@ -9,7 +9,7 @@ $levels=array('Debug','Info','Warning','Error','Fatal');
 <div id="controls">
 	
 </div>
-<table>
+<table id='log'>
 	<?php foreach($_['entries'] as $entry):?>
 		<tr>
 			<td>
@@ -26,4 +26,5 @@ $levels=array('Debug','Info','Warning','Error','Fatal');
 			</td>
 		</tr>
 	<?php endforeach;?>
-</table>
\ No newline at end of file
+</table>
+<input id='moreLog' type='button' value='<?php echo $l->t('More');?>...'></input>
diff --git a/settings/templates/users.php b/settings/templates/users.php
index bcc4d65fe43280f7dca577f31cede21b5c0d9e4d..a23a5bafe617f406c38a3453e5df94f2b9c84eec 100644
--- a/settings/templates/users.php
+++ b/settings/templates/users.php
@@ -12,16 +12,30 @@ foreach($_["groups"] as $group) {
 
 <div id="controls">
 	<form id="newuser">
-		<th class="name"><input id="newusername" placeholder="<?php echo $l->t('Name')?>" /></th>
-		<th class="password"><input type="password" id="newuserpassword" placeholder="<?php echo $l->t('Password')?>" /></th>
-		<th class="groups"><select id="newusergroups" data-placeholder="groups" title="<?php echo $l->t('Groups')?>" multiple="multiple">
+		<input id="newusername" placeholder="<?php echo $l->t('Name')?>" />
+		<input type="password" id="newuserpassword" placeholder="<?php echo $l->t('Password')?>" />
+		<select id="newusergroups" data-placeholder="groups" title="<?php echo $l->t('Groups')?>" multiple="multiple">
 		<?php foreach($_["groups"] as $group): ?>
 			<option value="<?php echo $group['name'];?>"><?php echo $group['name'];?></option>
 		<?php endforeach;?>
-		</select></th>
-		<th class="quota"></th>
-		<th><input type="submit" value="<?php echo $l->t('Create')?>" /></th>
+		</select>
+		<input type="submit" value="<?php echo $l->t('Create')?>" />
 	</form>
+	<div class="quota">
+		<span><?php echo $l->t('Default Quota');?>:</span>
+		<select class='quota'>
+			<?php foreach($_['quota_preset'] as $preset):?>
+				<?php if($preset!='default'):?>
+					<option <?php if($_['default_quota']==$preset) echo 'selected="selected"';?> value='<?php echo $preset;?>'><?php echo $preset;?></option>
+				<?php endif;?>
+			<?php endforeach;?>
+			<?php if(array_search($_['default_quota'],$_['quota_preset'])===false):?>
+				<option selected="selected" value='<?php echo $_['default_quota'];?>'><?php echo $_['default_quota'];?></option>
+			<?php endif;?>
+			<option value='other'><?php echo $l->t('Other');?>...</option>
+		</select>
+		<input class='quota-other'></input>
+	</div>
 </div>
 
 <table data-groups="<?php echo implode(', ',$allGroups);?>">
@@ -49,9 +63,17 @@ foreach($_["groups"] as $group) {
 					<?php endforeach;?>
 				</select>
 			</td>
-			<td class="quota" data-quota="<?php echo $user['quota']?>">
-				<span><?php echo ($user['quota']>0)?$user['quota']:'None';?></span>
-				<img class="svg action" src="<?php echo image_path('core','actions/rename.svg')?>" alt="set new password" title="set quota" />
+			<td class="quota">
+				<select class='quota'>
+					<?php foreach($_['quota_preset'] as $preset):?>
+						<option <?php if($user['quota']==$preset) echo 'selected="selected"';?> value='<?php echo $preset;?>'><?php echo $preset;?></option>
+					<?php endforeach;?>
+					<?php if(array_search($user['quota'],$_['quota_preset'])===false):?>
+						<option selected="selected" value='<?php echo $user['quota'];?>'><?php echo $user['quota'];?></option>
+					<?php endif;?>
+					<option value='other'><?php echo $l->t('Other');?>...</option>
+				</select>
+				<input class='quota-other'></input>
 			</td>
 			<td class="remove">
 				<?php if($user['name']!=OC_User::getUser()):?>
diff --git a/settings/users.php b/settings/users.php
index e5dcc0494817dff3d416bd5e4936a70064d38872..96515a90ce456ed4c213316f2affaa04351de4b1 100644
--- a/settings/users.php
+++ b/settings/users.php
@@ -18,17 +18,26 @@ $users = array();
 $groups = array();
 
 foreach( OC_User::getUsers() as $i ){
-	$users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Preferences::getValue($i,'files','quota',0));
+	$users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Preferences::getValue($i,'files','quota','default'));
 }
 
 foreach( OC_Group::getGroups() as $i ){
 	// Do some more work here soon
 	$groups[] = array( "name" => $i );
 }
+$quotaPreset=OC_Appconfig::getValue('files','quota_preset','default,none,1 GB, 5 GB, 10 GB');
+$quotaPreset=explode(',',$quotaPreset);
+foreach($quotaPreset as &$preset){
+	$preset=trim($preset);
+}
+
+$defaultQuota=OC_Appconfig::getValue('files','default_quota','none');
 
 $tmpl = new OC_Template( "settings", "users", "user" );
 $tmpl->assign( "users", $users );
 $tmpl->assign( "groups", $groups );
+$tmpl->assign( 'quota_preset', $quotaPreset);
+$tmpl->assign( 'default_quota', $defaultQuota);
 $tmpl->printPage();
 
 ?>
diff --git a/tests/index.php b/tests/index.php
index d29579a4bbaa62348395adc48d0846b46f044d09..2e86366740bfcd99ba1fb87be9f466e7e2da0f08 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -26,22 +26,29 @@ require_once 'simpletest/mock_objects.php';
 require_once 'simpletest/collector.php';
 require_once 'simpletest/default_reporter.php';
 
-//load all test cases
-loadTests();
+//load core test cases
+loadTests(dirname(__FILE__));
+
+//load app test cases
+$apps=OC_Appconfig::getApps();
+foreach($apps as $app){
+	if(is_dir(OC::$SERVERROOT.'/apps/'.$app.'/tests')){
+		loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests');
+	}
+}
 
 function loadTests($dir=''){
-	$basedir=dirname(__FILE__).'/';
-	if($dh=opendir($basedir.$dir)){
+	if($dh=opendir($dir)){
 		while($name=readdir($dh)){
 			if(substr($name,0,1)!='.'){//no hidden files, '.' or '..'
 				$file=$dir.'/'.$name;
-				if(is_dir($basedir.$file)){
+				if(is_dir($file)){
 					loadTests($file);
 				}elseif(substr($file,-4)=='.php' and $file!=__FILE__){
 					$testCase=new TestSuite(getTestName($file));
-					$testCase->addFile($basedir.$file);
+					$testCase->addFile($file);
 					if($testCase->getSize()>0){
-						$testCase->run(new DefaultReporter());
+						$testCase->run(new HtmlReporter());
 					}
 				}
 			}
@@ -50,6 +57,7 @@ function loadTests($dir=''){
 }
 
 function getTestName($file){
-	//TODO: get better test names
-	return substr($file,5,-4);//strip /lib/ and .php
+// 	//TODO: get better test names
+	$file=substr($file,strlen(OC::$SERVERROOT));
+	return substr($file,0,-4);//strip .php
 }
diff --git a/tests/lib/filestorage.php b/tests/lib/filestorage.php
index 7f024dafaa3abf9f2de148812e47a15dcad0aaf3..9ffa0eca9cb0e2e7bf9928480429ffb41aac36b7 100644
--- a/tests/lib/filestorage.php
+++ b/tests/lib/filestorage.php
@@ -81,10 +81,6 @@ abstract class Test_FileStorage extends UnitTestCase {
 		$this->instance->file_put_contents('/lorem.txt',$sourceText);
 		$this->assertEqual($sourceText,$this->instance->file_get_contents('/lorem.txt'),'data returned from file_get_contents is not equal to the source data');
 
-		//fill a file with a stream
-		$this->instance->file_put_contents('/lorem.txt',fopen($sourceFile,'r'));
-		$this->assertEqual($sourceText,$this->instance->file_get_contents('/lorem.txt'),'data returned from file_get_contents is not equal to the source data');
-
 		//empty the file
 		$this->instance->file_put_contents('/lorem.txt','');
 		$this->assertEqual('',$this->instance->file_get_contents('/lorem.txt'),'file not emptied');
@@ -98,17 +94,96 @@ abstract class Test_FileStorage extends UnitTestCase {
 		$this->assertEqual(false,$this->instance->getMimeType('/non/existing/file'));
 		
 		$textFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
-		$this->instance->file_put_contents('/lorem.txt',fopen($textFile,'r'));
+		$this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r'));
 		$this->assertEqual('text/plain',$this->instance->getMimeType('/lorem.txt'));
 		
 		$pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png';
-		$this->instance->file_put_contents('/logo-wide.png',fopen($pngFile,'r'));
+		$this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r'));
 		$this->assertEqual('image/png',$this->instance->getMimeType('/logo-wide.png'));
 		
 		$svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg';
-		$this->instance->file_put_contents('/logo-wide.svg',fopen($svgFile,'r'));
+		$this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r'));
 		$this->assertEqual('image/svg+xml',$this->instance->getMimeType('/logo-wide.svg'));
 	}
-}
+	
+	public function testCopyAndMove(){
+		$textFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$this->instance->file_put_contents('/source.txt',file_get_contents($textFile));
+		$this->instance->copy('/source.txt','/target.txt');
+		$this->assertTrue($this->instance->file_exists('/target.txt'));
+		$this->assertEqual($this->instance->file_get_contents('/source.txt'),$this->instance->file_get_contents('/target.txt'));
+		
+		$this->instance->rename('/source.txt','/target2.txt');
+		$this->assertTrue($this->instance->file_exists('/target2.txt'));
+		$this->assertFalse($this->instance->file_exists('/source.txt'));
+		$this->assertEqual(file_get_contents($textFile),$this->instance->file_get_contents('/target.txt'));
+	}
+	
+	public function testLocalFile(){
+		$textFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile));
+		$localFile=$this->instance->getLocalFile('/lorem.txt');
+		$this->assertTrue(file_exists($localFile));
+		$this->assertEqual(file_get_contents($localFile),file_get_contents($textFile));
+	}
 
+	public function testStat(){
+		$textFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$ctimeStart=time();
+		$this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile));
+		$this->assertTrue($this->instance->is_readable('/lorem.txt'));
+		$ctimeEnd=time();
+		$cTime=$this->instance->filectime('/lorem.txt');
+		$mTime=$this->instance->filemtime('/lorem.txt');
+		$this->assertTrue($ctimeStart<=$cTime);
+		$this->assertTrue($cTime<=$ctimeEnd);
+		$this->assertTrue($ctimeStart<=$mTime);
+		$this->assertTrue($mTime<=$ctimeEnd);
+		$this->assertEqual(filesize($textFile),$this->instance->filesize('/lorem.txt'));
+		
+		$stat=$this->instance->stat('/lorem.txt');
+		//only size, mtime and ctime are requered in the result
+		$this->assertEqual($stat['size'],$this->instance->filesize('/lorem.txt'));
+		$this->assertEqual($stat['mtime'],$mTime);
+		$this->assertEqual($stat['ctime'],$cTime);
+		
+		$mtimeStart=time();
+		$this->instance->touch('/lorem.txt');
+		$mtimeEnd=time();
+		$originalCTime=$cTime;
+		$cTime=$this->instance->filectime('/lorem.txt');
+		$mTime=$this->instance->filemtime('/lorem.txt');
+		$this->assertTrue($mtimeStart<=$mTime);
+		$this->assertTrue($mTime<=$mtimeEnd);
+		$this->assertEqual($cTime,$originalCTime);
+		
+		if($this->instance->touch('/lorem.txt',100)!==false){
+			$mTime=$this->instance->filemtime('/lorem.txt');
+			$this->assertEqual($mTime,100);
+		}
+		
+		$mtimeStart=time();
+		$fh=$this->instance->fopen('/lorem.txt','a');
+		fwrite($fh,' ');
+		fclose($fh);
+		clearstatcache();
+		$mtimeEnd=time();
+		$originalCTime=$cTime;
+		$mTime=$this->instance->filemtime('/lorem.txt');
+		$this->assertTrue($mtimeStart<=$mTime);
+		$this->assertTrue($mTime<=$mtimeEnd);
+	}
 
+	public function testSearch(){
+		$textFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r'));
+		$pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png';
+		$this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r'));
+		$svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg';
+		$this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r'));
+		$result=$this->instance->search('logo');
+		$this->assertEqual(2,count($result));
+		$this->assertNotIdentical(false,array_search('/logo-wide.svg',$result));
+		$this->assertNotIdentical(false,array_search('/logo-wide.png',$result));
+	}
+}
diff --git a/tests/lib/filestorage/commontest.php b/tests/lib/filestorage/commontest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab5375ec2b0d078aa7c8338d1d36db3d04279ab6
--- /dev/null
+++ b/tests/lib/filestorage/commontest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2012 Robin Appelman icewind@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/>.
+*
+*/
+
+class Test_Filestorage_CommonTest extends Test_FileStorage {
+	/**
+	 * @var string tmpDir
+	 */
+	private $tmpDir;
+	public function setUp(){
+		$this->tmpDir=get_temp_dir().'/filestoragecommon';
+		if(!file_exists($this->tmpDir)){
+			mkdir($this->tmpDir);
+		}
+		$this->instance=new OC_Filestorage_CommonTest(array('datadir'=>$this->tmpDir));
+	}
+
+	public function tearDown(){
+		OC_Helper::rmdirr($this->tmpDir);
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php
new file mode 100644
index 0000000000000000000000000000000000000000..17a92c6658c1f12e6a4d490821af44dc605112ae
--- /dev/null
+++ b/tests/lib/streamwrappers.php
@@ -0,0 +1,78 @@
+<?php
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2012 Robin Appelman icewind@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/>.
+*
+*/
+
+class Test_StreamWrappers extends UnitTestCase {
+	public function testFakeDir(){
+		$items=array('foo','bar');
+		OC_FakeDirStream::$dirs['test']=$items;
+		$dh=opendir('fakedir://test');
+		$result=array();
+		while($file=readdir($dh)){
+			$result[]=$file;
+			$this->assertNotIdentical(false,array_search($file,$items));
+		}
+		$this->assertEqual(count($items),count($result));
+	}
+
+	public function testStaticStream(){
+		$sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$staticFile='static://test';
+		$this->assertFalse(file_exists($staticFile));
+		file_put_contents($staticFile,file_get_contents($sourceFile));
+		$this->assertTrue(file_exists($staticFile));
+		$this->assertEqual(file_get_contents($sourceFile),file_get_contents($staticFile));
+		unlink($staticFile);
+		clearstatcache();
+		$this->assertFalse(file_exists($staticFile));
+	}
+
+	public function testCloseStream(){
+		//ensure all basic stream stuff works
+		$sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt';
+		$tmpFile=OC_Helper::TmpFile('.txt');
+		$file='close://'.$tmpFile;
+		$this->assertTrue(file_exists($file));
+		file_put_contents($file,file_get_contents($sourceFile));
+		$this->assertEqual(file_get_contents($sourceFile),file_get_contents($file));
+		unlink($file);
+		clearstatcache();
+		$this->assertFalse(file_exists($file));
+		
+		//test callback
+		$tmpFile=OC_Helper::TmpFile('.txt');
+		$file='close://'.$tmpFile;
+		OC_CloseStreamWrapper::$callBacks[$tmpFile]=array('Test_StreamWrappers','closeCallBack');
+		$fh=fopen($file,'w');
+		fwrite($fh,'asd');
+		try{
+			fclose($fh);
+			$this->fail('Expected exception');
+		}catch(Exception $e){
+			$path=$e->getMessage();
+			$this->assertEqual($path,$tmpFile);
+		}
+	}
+
+	public static function closeCallBack($path){
+		throw new Exception($path);
+	}
+}
\ No newline at end of file