diff --git a/.gitignore b/.gitignore
index 4749dea19dc782532161ee51266e5a02e9b19602..4ae39ed7facd0f80283568f8a39ad1b817cf0abd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,3 +54,7 @@ nbproject
 # WebFinger
 .well-known
 /.buildpath
+3rdparty/autoload.php
+3rdparty/composer/
+3rdparty/symfony/
+composer.lock
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
new file mode 100644
index 0000000000000000000000000000000000000000..e1ab560803dcb14f7324873a2ece76615448cab5
--- /dev/null
+++ b/apps/files/appinfo/routes.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$this->create('download', 'download{file}')
+	->requirements(array('file' => '.*'))
+	->actionInclude('files/download.php');
+
diff --git a/apps/files/index.php b/apps/files/index.php
index 92fda5b21e596adcd6ed99599ea429342b477596..3441e371a31b8f02540920e90104a8918f4deb83 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -75,11 +75,11 @@ foreach( explode( '/', $dir ) as $i ) {
 // make breadcrumb und filelist markup
 $list = new OCP\Template( 'files', 'part.list', '' );
 $list->assign( 'files', $files, false );
-$list->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false);
+$list->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'?dir=', false);
 $list->assign( 'downloadURL', OCP\Util::linkTo('files', 'download.php').'?file=', false);
 $breadcrumbNav = new OCP\Template( 'files', 'part.breadcrumb', '' );
 $breadcrumbNav->assign( 'breadcrumb', $breadcrumb, false );
-$breadcrumbNav->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'&dir=', false);
+$breadcrumbNav->assign( 'baseURL', OCP\Util::linkTo('files', 'index.php').'?dir=', false);
 
 $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
 $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index e184cbfa9153a041a055aa6fa492744988e35c37..0cf4a05222585c4e69fc40bf5e35257e97e927e0 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -185,7 +185,7 @@ FileActions.register('all','Rename', OC.PERMISSION_UPDATE, function(){return OC.
 });
 
 FileActions.register('dir','Open', OC.PERMISSION_READ, '', function(filename){
-	window.location=OC.linkTo('files', 'index.php') + '&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
+	window.location=OC.linkTo('files', 'index.php') + '?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
 });
 
 FileActions.setDefault('dir','Open');
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 6b49f6226688ae667675a253cf13672f0ebc9cfa..f08e412921e207fe7d98d4a17871e54c7fdb33e0 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -46,7 +46,7 @@ var FileList={
 		html = $('<tr></tr>').attr({ "data-type": "dir", "data-size": size, "data-file": name, "data-permissions": $('#permissions').val()});
 		td = $('<td></td>').attr({"class": "filename", "style": 'background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')' });
 		td.append('<input type="checkbox" />');
-		link_elem = $('<a></a>').attr({ "class": "name", "href": OC.linkTo('files', 'index.php')+"&dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/') });
+		link_elem = $('<a></a>').attr({ "class": "name", "href": OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/') });
 		link_elem.append($('<span></span>').addClass('nametext').text(name));
 		link_elem.append($('<span></span>').attr({'class': 'uploadtext', 'currentUploads': 0}));
 		td.append(link_elem);
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..7916b15cb9fa5e28ef495e5ba5f5500a86bf0818
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,18 @@
+{
+	"description": "ownCloud gives you universal access to your files/contacts/calendar through a web interface or WebDAV.",
+	"homepage": "http://owncloud.org",
+	"license": "AGPL-3.0+",
+	"support": {
+		"email": "owncloud@kde.org",
+		"irc": "irc://irc.freenode.org/owncloud",
+		"forum": "http://forum.owncloud.org/",
+		"issues": "https://github.com/owncloud/core/issues"
+	},
+	"require": {
+		"php": ">=5.3.2",
+		"symfony/routing": "2.0.*"
+	},
+	"config": {
+		"vendor-dir": "3rdparty"
+	}
+}
diff --git a/core/ajax/appconfig.php b/core/ajax/appconfig.php
index 1b43afa74fbf781d5e8ac3fcacbd6d2a9a0918fc..4f26dedc79760259965859709f424d481047da97 100644
--- a/core/ajax/appconfig.php
+++ b/core/ajax/appconfig.php
@@ -5,7 +5,6 @@
  * See the COPYING-README file.
  */
 
-require_once "../../lib/base.php";
 OC_Util::checkAdminUser();
 OCP\JSON::callCheck();
 
diff --git a/core/ajax/requesttoken.php b/core/ajax/requesttoken.php
index 705330b2c3e85886e2ff772cf62ffc97929db9e1..9d43a7228523b6aa7859d9f9eae11636738310b0 100644
--- a/core/ajax/requesttoken.php
+++ b/core/ajax/requesttoken.php
@@ -26,7 +26,6 @@
  * @return json: success/error state indicator including a fresh request token
  * @author Christian Reiner
  */
-require_once '../../lib/base.php';
 
 // don't load apps or filesystem for this task
 $RUNTIME_NOAPPS    = true;
diff --git a/core/ajax/share.php b/core/ajax/share.php
index 84e84be5acb77e39993b832e84f4442113f4cd01..0fa162fb37123217ceb57ff63547c1857607e69a 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -18,7 +18,6 @@
 * 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';
 
 OC_JSON::checkLoggedIn();
 OCP\JSON::callCheck();
diff --git a/core/ajax/translations.php b/core/ajax/translations.php
index 75679da2c0466cf904955aabf6fc39013398abfd..e22cbad4708f09979e41273c6eef1bbda2d4f5ba 100644
--- a/core/ajax/translations.php
+++ b/core/ajax/translations.php
@@ -21,9 +21,6 @@
 *
 */
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 $app = $_POST["app"];
 
 $l = OC_L10N::get( $app );
diff --git a/core/ajax/vcategories/add.php b/core/ajax/vcategories/add.php
index 81fa06dbf19491079955f89d77273b7e70509ede..8d31275dbfb5bb7f7f4f93f7e7b29c20a19f50b0 100644
--- a/core/ajax/vcategories/add.php
+++ b/core/ajax/vcategories/add.php
@@ -14,7 +14,6 @@ function debug($msg) {
 	OC_Log::write('core', 'ajax/vcategories/add.php: '.$msg, OC_Log::DEBUG);
 }
 
-require_once '../../../lib/base.php';
 OC_JSON::checkLoggedIn();
 $category = isset($_GET['category'])?strip_tags($_GET['category']):null;
 $app = isset($_GET['app'])?$_GET['app']:null;
diff --git a/core/ajax/vcategories/delete.php b/core/ajax/vcategories/delete.php
index cd46a25b79df9d898debabb4b49432389fa610a6..74b0220870c47f856af56acd6eecd3c32b62070e 100644
--- a/core/ajax/vcategories/delete.php
+++ b/core/ajax/vcategories/delete.php
@@ -15,7 +15,6 @@ function debug($msg) {
 	OC_Log::write('core', 'ajax/vcategories/delete.php: '.$msg, OC_Log::DEBUG);
 }
 
-require_once '../../../lib/base.php';
 OC_JSON::checkLoggedIn();
 $app = isset($_POST['app'])?$_POST['app']:null;
 $categories = isset($_POST['categories'])?$_POST['categories']:null;
diff --git a/core/ajax/vcategories/edit.php b/core/ajax/vcategories/edit.php
index a0e67841c5571d1b6489b8ac032ca8c69f78de9c..caeebcaa940f2a529d69ec58a6edaa3d48105aa0 100644
--- a/core/ajax/vcategories/edit.php
+++ b/core/ajax/vcategories/edit.php
@@ -15,7 +15,6 @@ function debug($msg) {
 	OC_Log::write('core', 'ajax/vcategories/edit.php: '.$msg, OC_Log::DEBUG);
 }
 
-require_once '../../../lib/base.php';
 OC_JSON::checkLoggedIn();
 $app = isset($_GET['app'])?$_GET['app']:null;
 
diff --git a/core/js/js.js b/core/js/js.js
index c5e32f3c27821c8e4b81a9615a96acc1a098ae1f..130d413296ebbfb9d9b254b613e4295081404692 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -95,9 +95,9 @@ var OC={
 		var isCore=OC.coreApps.indexOf(app)!==-1,
 			link=OC.webroot;
 		if((file.substring(file.length-3) === 'php' || file.substring(file.length-3) === 'css') && !isCore){
-			link+='/?app=' + app;
+			link+='/index.php/apps/' + app;
 			if (file != 'index.php') {
-				link+='&getfile=';
+				link+='/';
 				if(type){
 					link+=encodeURI(type + '/');
 				}
@@ -113,7 +113,12 @@ var OC={
 			}
 			link+=file;
 		}else{
-			link+='/';
+			if ((app == 'settings' || app == 'core') && type == 'ajax') {
+				link+='/index.php/';
+			}
+			else {
+				link+='/';
+			}
 			if(!isCore){
 				link+='apps/';
 			}
diff --git a/core/js/router.js b/core/js/router.js
new file mode 100644
index 0000000000000000000000000000000000000000..8b66f5a05c5c5f79d8a84e79117dd36464635f3d
--- /dev/null
+++ b/core/js/router.js
@@ -0,0 +1,73 @@
+OC.router_base_url = OC.webroot + '/index.php/',
+OC.Router = {
+	routes_request: $.ajax(OC.router_base_url + 'core/routes.json', {
+		dataType: 'json',
+		success: function(jsondata) {
+			if (jsondata.status == 'success') {
+				OC.Router.routes = jsondata.data;
+			}
+		}
+	}),
+	generate:function(name, opt_params) {
+		if (!('routes' in this)) {
+			if(this.routes_request.state() != 'resolved') {
+				alert('wait');// wait
+			}
+		}
+		if (!(name in this.routes)) {
+			throw new Error('The route "' + name + '" does not exist.');
+		}
+		var route = this.routes[name];
+		var params = opt_params || {};
+		var unusedParams = $.extend(true, {}, params);
+		var url = '';
+		var optional = true;
+		$(route.tokens).each(function(i, token) {
+			if ('text' === token[0]) {
+			    url = token[1] + url;
+			    optional = false;
+
+			    return;
+			}
+
+			if ('variable' === token[0]) {
+			    if (false === optional || !(token[3] in route.defaults)
+				    || ((token[3] in params) && params[token[3]] != route.defaults[token[3]])) {
+				var value;
+				if (token[3] in params) {
+				    value = params[token[3]];
+				    delete unusedParams[token[3]];
+				} else if (token[3] in route.defaults) {
+				    value = route.defaults[token[3]];
+				} else if (optional) {
+				    return;
+				} else {
+				    throw new Error('The route "' + name + '" requires the parameter "' + token[3] + '".');
+				}
+
+				var empty = true === value || false === value || '' === value;
+
+				if (!empty || !optional) {
+				    url = token[1] + encodeURIComponent(value).replace(/%2F/g, '/') + url;
+				}
+
+				optional = false;
+			    }
+
+			    return;
+			}
+
+			throw new Error('The token type "' + token[0] + '" is not supported.');
+		});
+		if (url === '') {
+			url = '/';
+		}
+
+		unusedParams = $.param(unusedParams);
+		if (unusedParams.length > 0) {
+			url += '?'+unusedParams;
+		}
+
+		return OC.router_base_url + url;
+	}
+};
diff --git a/core/lostpassword/controller.php b/core/lostpassword/controller.php
new file mode 100644
index 0000000000000000000000000000000000000000..e616fe7dff89e6486b5fa3e3f3e778adb7041bd2
--- /dev/null
+++ b/core/lostpassword/controller.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Core_LostPassword_Controller {
+	protected static function displayLostPasswordPage($error, $requested) {
+		OC_Template::printGuestPage('core/lostpassword', 'lostpassword', array('error' => $error, 'requested' => $requested));
+	}
+	
+	protected static function displayResetPasswordPage($success, $args) {
+		$route_args = array();
+		$route_args['token'] = $args['token'];
+		$route_args['user'] = $args['user'];
+		OC_Template::printGuestPage('core/lostpassword', 'resetpassword', array('success' => $success, 'args' => $route_args));
+	}
+
+	protected static function checkToken($user, $token) {
+		return OC_Preferences::getValue($user, 'owncloud', 'lostpassword') === hash('sha256', $token);
+	}
+
+	public static function index($args) {
+		self::displayLostPasswordPage(false, false);
+	}
+
+	public static function sendEmail($args) {
+		if (OC_User::userExists($_POST['user'])) {
+			$token = hash('sha256', OC_Util::generate_random_bytes(30).OC_Config::getValue('passwordsalt', ''));
+			OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', hash('sha256', $token)); // Hash the token again to prevent timing attacks
+			$email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', '');
+			if (!empty($email)) {
+				$link = OC_Helper::linkToRoute('core_lostpassword_reset', array('user' => $_POST['user'], 'token' => $token));
+				$link = OC_Helper::makeURLAbsolute($link);
+
+				$tmpl = new OC_Template('core/lostpassword', 'email');
+				$tmpl->assign('link', $link, false);
+				$msg = $tmpl->fetchPage();
+				$l = OC_L10N::get('core');
+				$from = 'lostpassword-noreply@' . OCP\Util::getServerHost();
+				OC_Mail::send($email, $_POST['user'], $l->t('ownCloud password reset'), $msg, $from, 'ownCloud');
+				echo('Mailsent');
+
+				self::displayLostPasswordPage(false, true);
+			} else {
+				self::displayLostPasswordPage(true, false);
+			}
+		} else {
+			self::displayLostPasswordPage(true, false);
+		}
+	}
+
+	public static function reset($args) {
+		// Someone wants to reset their password:
+		if(self::checkToken($args['user'], $args['token'])) {
+			self::displayResetPasswordPage(false, $args);
+		} else {
+			// Someone lost their password
+			self::displayLostPasswordPage(false, false);
+		}
+	}
+
+	public static function resetPassword($args) {
+		if (self::checkToken($args['user'], $args['token'])) {
+			if (isset($_POST['password'])) {
+				if (OC_User::setPassword($args['user'], $_POST['password'])) {
+					OC_Preferences::deleteKey($args['user'], 'owncloud', 'lostpassword');
+					OC_User::unsetMagicInCookie();
+					self::displayResetPasswordPage(true, $args);
+				} else {
+					self::displayResetPasswordPage(false, $args);
+				}
+			} else {
+				self::reset($args);
+			}
+		} else {
+			// Someone lost their password
+			self::displayLostPasswordPage(false, false);
+		}
+	}
+}
diff --git a/core/lostpassword/index.php b/core/lostpassword/index.php
deleted file mode 100644
index 1da5bce8ea809b5aad81cb95399fbae2b008b6a6..0000000000000000000000000000000000000000
--- a/core/lostpassword/index.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Frank Karlitschek frank@owncloud.org
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
-*/
-
-$RUNTIME_NOAPPS = true; //no apps
-require_once '../../lib/base.php';
-
-
-// Someone lost their password:
-if (isset($_POST['user'])) {
-	if (OC_User::userExists($_POST['user'])) {
-		$token = hash("sha256", OC_Util::generate_random_bytes(30).OC_Config::getValue('passwordsalt', ''));
-		OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', hash("sha256", $token)); // Hash the token again to prevent timing attacks
-		$email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', '');
-		if (!empty($email)) {
-			$link = OC_Helper::linkToAbsolute('core/lostpassword', 'resetpassword.php', array('user' => $_POST['user'], 'token' => $token));
-			$tmpl = new OC_Template('core/lostpassword', 'email');
-			$tmpl->assign('link', $link, false);
-			$msg = $tmpl->fetchPage();
-			$l = OC_L10N::get('core');
-			$from = 'lostpassword-noreply@' . OCP\Util::getServerHost();
-			OC_MAIL::send($email, $_POST['user'], $l->t('ownCloud password reset'), $msg, $from, 'ownCloud');
-			echo('sent');
-		}
-		OC_Template::printGuestPage('core/lostpassword', 'lostpassword', array('error' => false, 'requested' => true));
-	} else {
-		OC_Template::printGuestPage('core/lostpassword', 'lostpassword', array('error' => true, 'requested' => false));
-	}
-} else {
-	OC_Template::printGuestPage('core/lostpassword', 'lostpassword', array('error' => false, 'requested' => false));
-}
diff --git a/core/lostpassword/resetpassword.php b/core/lostpassword/resetpassword.php
deleted file mode 100644
index 7cd383921d7ebc62aaf2ed481c002e44dbd0f258..0000000000000000000000000000000000000000
--- a/core/lostpassword/resetpassword.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Frank Karlitschek frank@owncloud.org
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
-*/
-
-$RUNTIME_NOAPPS = true; //no apps
-require_once '../../lib/base.php';
-
-// Someone wants to reset their password:
-if(isset($_GET['token']) && isset($_GET['user']) && OC_Preferences::getValue($_GET['user'], 'owncloud', 'lostpassword') === hash("sha256", $_GET['token'])) {
-	if (isset($_POST['password'])) {
-		if (OC_User::setPassword($_GET['user'], $_POST['password'])) {
-			OC_Preferences::deleteKey($_GET['user'], 'owncloud', 'lostpassword');
-			OC_Template::printGuestPage('core/lostpassword', 'resetpassword', array('success' => true));
-		} else {
-			OC_Template::printGuestPage('core/lostpassword', 'resetpassword', array('success' => false));
-		}
-	} else {
-		OC_Template::printGuestPage('core/lostpassword', 'resetpassword', array('success' => false));
-	}
-} else {
-	// Someone lost their password
-	OC_Template::printGuestPage('core/lostpassword', 'lostpassword', array('error' => false, 'requested' => false));
-}
diff --git a/core/lostpassword/templates/lostpassword.php b/core/lostpassword/templates/lostpassword.php
index 4b871963b8055fbc6159ba0b8dcb595acd51da42..55c070f3e0cff518cf157fdf68d46d899d6cc44e 100644
--- a/core/lostpassword/templates/lostpassword.php
+++ b/core/lostpassword/templates/lostpassword.php
@@ -1,11 +1,11 @@
-<form action="index.php" method="post">
+<form action="<?php echo OC_Helper::linkToRoute('core_lostpassword_send_email') ?>" method="post">
 	<fieldset>
 		<?php echo $l->t('You will receive a link to reset your password via Email.'); ?>
 		<?php if ($_['requested']): ?>
-			<?php echo $l->t('Requested'); ?>
+			<?php echo $l->t('Reset email send.'); ?>
 		<?php else: ?>
 			<?php if ($_['error']): ?>
-				<?php echo $l->t('Login failed!'); ?>
+				<?php echo $l->t('Request failed!'); ?>
 			<?php endif; ?>
 			<p class="infield">
 				<label for="user" class="infield"><?php echo $l->t( 'Username' ); ?></label>
diff --git a/core/lostpassword/templates/resetpassword.php b/core/lostpassword/templates/resetpassword.php
index 56257de7f1371f7f8a4aa3747b651ebd7123adf7..0ab32acca6012d5484b3d97d129c4776044ed316 100644
--- a/core/lostpassword/templates/resetpassword.php
+++ b/core/lostpassword/templates/resetpassword.php
@@ -1,8 +1,8 @@
-<form action="<?php echo 'resetpassword.php?'.$_SERVER['QUERY_STRING']; ?>" method="post">
+<form action="<?php echo OC_Helper::linkToRoute('core_lostpassword_reset', $_['args']) ?>" method="post">
 	<fieldset>
 		<?php if($_['success']): ?>
 			<h1><?php echo $l->t('Your password was reset'); ?></h1>
-			<p><a href="<?php echo OC::$WEBROOT ?>/"><?php echo $l->t('To login page'); ?></a></p>
+			<p><a href="<?php echo OC_Helper::linkTo('', 'index.php') ?>/"><?php echo $l->t('To login page'); ?></a></p>
 		<?php else: ?>
 			<p class="infield">
 				<label for="password" class="infield"><?php echo $l->t( 'New password' ); ?></label>
diff --git a/core/routes.php b/core/routes.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cf27498846436300b01a18f2f4258230990ef8a
--- /dev/null
+++ b/core/routes.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('settings/routes.php');
+
+// Core ajax actions
+// AppConfig
+$this->create('core_ajax_appconfig', '/core/ajax/appconfig.php')
+	->actionInclude('core/ajax/appconfig.php');
+// RequestToken
+$this->create('core_ajax_requesttoken', '/core/ajax/requesttoken.php')
+	->actionInclude('core/ajax/requesttoken.php');
+// Share
+$this->create('core_ajax_share', '/core/ajax/share.php')
+	->actionInclude('core/ajax/share.php');
+// Translations
+$this->create('core_ajax_translations', '/core/ajax/translations.php')
+	->actionInclude('core/ajax/translations.php');
+// VCategories
+$this->create('core_ajax_vcategories_add', '/core/ajax/vcategories/add.php')
+	->actionInclude('core/ajax/vcategories/add.php');
+$this->create('core_ajax_vcategories_delete', '/core/ajax/vcategories/delete.php')
+	->actionInclude('core/ajax/vcategories/delete.php');
+$this->create('core_ajax_vcategories_edit', '/core/ajax/vcategories/edit.php')
+	->actionInclude('core/ajax/vcategories/edit.php');
+// Routing
+$this->create('core_ajax_routes', '/core/routes.json')
+	->action('OC_Router', 'JSRoutes');
+
+OC::$CLASSPATH['OC_Core_LostPassword_Controller'] = 'core/lostpassword/controller.php';
+$this->create('core_lostpassword_index', '/lostpassword/')
+	->get()
+	->action('OC_Core_LostPassword_Controller', 'index');
+$this->create('core_lostpassword_send_email', '/lostpassword/')
+	->post()
+	->action('OC_Core_LostPassword_Controller', 'sendEmail');
+$this->create('core_lostpassword_reset', '/lostpassword/reset/{token}/{user}')
+	->get()
+	->action('OC_Core_LostPassword_Controller', 'reset');
+$this->create('core_lostpassword_reset_password', '/lostpassword/reset/{token}/{user}')
+	->post()
+	->action('OC_Core_LostPassword_Controller', 'resetPassword');
+
+// Not specifically routed
+$this->create('app_css', '/apps/{app}/{file}')
+	->requirements(array('file' => '.*.css'))
+	->action('OC', 'loadCSSFile');
+$this->create('app_index_script', '/apps/{app}/')
+	->defaults(array('file' => 'index.php'))
+	//->requirements(array('file' => '.*.php'))
+	->action('OC', 'loadAppScriptFile');
+$this->create('app_script', '/apps/{app}/{file}')
+	->defaults(array('file' => 'index.php'))
+	->requirements(array('file' => '.*.php'))
+	->action('OC', 'loadAppScriptFile');
diff --git a/lib/app.php b/lib/app.php
index c9329df91d1754923b047c7589014fb927761a76..9e1b88635c8a88b93df42d33d8484ea35fb4d740 100755
--- a/lib/app.php
+++ b/lib/app.php
@@ -282,33 +282,33 @@ class OC_App{
 		// by default, settings only contain the help menu
 		if(OC_Config::getValue('knowledgebaseenabled', true)==true) {
 			$settings = array(
-				array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "help.php" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" ))
+				array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_help" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" ))
 			);
 		}
 
 		// if the user is logged-in
 		if (OC_User::isLoggedIn()) {
 			// personal menu
-			$settings[] = array( "id" => "personal", "order" => 1, "href" => OC_Helper::linkTo( "settings", "personal.php" ), "name" => $l->t("Personal"), "icon" => OC_Helper::imagePath( "settings", "personal.svg" ));
+			$settings[] = array( "id" => "personal", "order" => 1, "href" => OC_Helper::linkToRoute( "settings_personal" ), "name" => $l->t("Personal"), "icon" => OC_Helper::imagePath( "settings", "personal.svg" ));
 
 			// if there are some settings forms
 			if(!empty(self::$settingsForms))
 				// settings menu
-				$settings[]=array( "id" => "settings", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "settings.php" ), "name" => $l->t("Settings"), "icon" => OC_Helper::imagePath( "settings", "settings.svg" ));
+				$settings[]=array( "id" => "settings", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_settings" ), "name" => $l->t("Settings"), "icon" => OC_Helper::imagePath( "settings", "settings.svg" ));
 
 			//SubAdmins are also allowed to access user management
 			if(OC_SubAdmin::isSubAdmin($_SESSION["user_id"]) || OC_Group::inGroup( $_SESSION["user_id"], "admin" )) {
 				// admin users menu
-				$settings[] = array( "id" => "core_users", "order" => 2, "href" => OC_Helper::linkTo( "settings", "users.php" ), "name" => $l->t("Users"), "icon" => OC_Helper::imagePath( "settings", "users.svg" ));
+				$settings[] = array( "id" => "core_users", "order" => 2, "href" => OC_Helper::linkToRoute( "settings_users" ), "name" => $l->t("Users"), "icon" => OC_Helper::imagePath( "settings", "users.svg" ));
 			}
 
 
 			// if the user is an admin
 			if(OC_Group::inGroup( $_SESSION["user_id"], "admin" )) {
 				// admin apps menu
-				$settings[] = array( "id" => "core_apps", "order" => 3, "href" => OC_Helper::linkTo( "settings", "apps.php" ).'?installed', "name" => $l->t("Apps"), "icon" => OC_Helper::imagePath( "settings", "apps.svg" ));
+				$settings[] = array( "id" => "core_apps", "order" => 3, "href" => OC_Helper::linkToRoute( "settings_apps" ).'?installed', "name" => $l->t("Apps"), "icon" => OC_Helper::imagePath( "settings", "apps.svg" ));
 
-				$settings[]=array( "id" => "admin", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "admin.php" ), "name" => $l->t("Admin"), "icon" => OC_Helper::imagePath( "settings", "admin.svg" ));
+				$settings[]=array( "id" => "admin", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_admin" ), "name" => $l->t("Admin"), "icon" => OC_Helper::imagePath( "settings", "admin.svg" ));
 			}
 		}
 
@@ -484,6 +484,12 @@ class OC_App{
 	public static function getCurrentApp() {
 		$script=substr($_SERVER["SCRIPT_NAME"], strlen(OC::$WEBROOT)+1);
 		$topFolder=substr($script, 0, strpos($script, '/'));
+		if (empty($topFolder)) {
+			$path_info = OC_Request::getPathInfo();
+			if ($path_info) {
+				$topFolder=substr($path_info, 1, strpos($path_info, '/', 1)-1);
+			}
+		}
 		if($topFolder=='apps') {
 			$length=strlen($topFolder);
 			return substr($script, $length+1, strpos($script, '/', $length+1)-$length-1);
diff --git a/lib/base.php b/lib/base.php
index 186ed53e9808ced0932f9b83772e1afcbb47046c..15dc34f63d2e20a07aba3cc96a6646cc6caf0a77 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -67,6 +67,10 @@ class OC{
 	 * check if owncloud runs in cli mode
 	 */
 	public static $CLI = false;
+	/*
+	 * OC router
+	 */
+	protected static $router = null;
 	/**
 	 * SPL autoload
 	 */
@@ -93,6 +97,9 @@ class OC{
 		elseif(strpos($className, 'Sabre_')===0) {
 			$path =  str_replace('_', '/', $className) . '.php';
 		}
+		elseif(strpos($className, 'Symfony\\Component\\Routing\\')===0) {
+			$path = 'symfony/routing/'.str_replace('\\', '/', $className) . '.php';
+		}
 		elseif(strpos($className, 'Test_')===0) {
 			$path =  'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php');
 		}else{
@@ -250,6 +257,7 @@ class OC{
 		OC_Util::addScript( "config" );
 		//OC_Util::addScript( "multiselect" );
 		OC_Util::addScript('search', 'result');
+		OC_Util::addScript('router');
 
 		if( OC_Config::getValue( 'installed', false )) {
 			if( OC_Appconfig::getValue( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax' ) {
@@ -290,6 +298,15 @@ class OC{
 		$_SESSION['LAST_ACTIVITY'] = time();
 	}
 
+	public static function getRouter() {
+		if (!isset(OC::$router)) {
+			OC::$router = new OC_Router();
+			OC::$router->loadRoutes();
+		}
+
+		return OC::$router;
+	}
+
 	public static function init() {
 		// register autoloader
 		spl_autoload_register(array('OC','autoload'));
@@ -465,9 +482,21 @@ class OC{
 			header('location: '.OC_Helper::linkToRemote('webdav'));
 			return;
 		}
+		try {
+			OC::getRouter()->match(OC_Request::getPathInfo());
+			return;
+		} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
+			//header('HTTP/1.0 404 Not Found');
+		} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
+			OC_Response::setStatus(405);
+			return;
+		}
+		$app = OC::$REQUESTEDAPP;
+		$file = OC::$REQUESTEDFILE;
+		$param = array('app' => $app, 'file' => $file);
 		// Handle app css files
-		if(substr(OC::$REQUESTEDFILE, -3) == 'css') {
-			self::loadCSSFile();
+		if(substr($file, -3) == 'css') {
+			self::loadCSSFile($param);
 			return;
 		}
 		// Someone is logged in :
@@ -479,13 +508,12 @@ class OC{
 				OC_User::logout();
 				header("Location: ".OC::$WEBROOT.'/');
 			}else{
-				$app = OC::$REQUESTEDAPP;
-				$file = OC::$REQUESTEDFILE;
 				if(is_null($file)) {
-					$file = 'index.php';
+					$param['file'] = 'index.php';
 				}
-				$file_ext = substr($file, -3);
-				if ($file_ext != 'php'|| !self::loadAppScriptFile($app, $file)) {
+				$file_ext = substr($param['file'], -3);
+				if ($file_ext != 'php'
+				    || !self::loadAppScriptFile($param)) {
 					header('HTTP/1.0 404 Not Found');
 				}
 			}
@@ -495,7 +523,10 @@ class OC{
 		self::handleLogin();
 	}
 
-	protected static function loadAppScriptFile($app, $file) {
+	public static function loadAppScriptFile($param) {
+		OC_App::loadApps();
+		$app = $param['app'];
+		$file = $param['file'];
 		$app_path = OC_App::getAppPath($app);
 		$file = $app_path . '/' . $file;
 		unset($app, $app_path);
@@ -506,9 +537,9 @@ class OC{
 		return false;
 	}
 
-	protected static function loadCSSFile() {
-		$app = OC::$REQUESTEDAPP;
-		$file = OC::$REQUESTEDFILE;
+	public static function loadCSSFile($param) {
+		$app = $param['app'];
+		$file = $param['file'];
 		$app_path = OC_App::getAppPath($app);
 		if (file_exists($app_path . '/' . $file)) {
 			$app_web_path = OC_App::getAppWebPath($app);
diff --git a/lib/helper.php b/lib/helper.php
index 2da06c4cc454df9ab07bab2f6902aec272a2adb4..060d887fd67de3461536a1209249d6b84f0f25ad 100644
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -28,6 +28,20 @@ class OC_Helper {
 	private static $mimetypes=array();
 	private static $tmpFiles=array();
 
+	/**
+	 * @brief Creates an url using a defined route
+	 * @param $route
+	 * @param $parameters
+	 * @param $args array with param=>value, will be appended to the returned url
+	 * @returns the url
+	 *
+	 * Returns a url to the given app and file.
+	 */
+	public static function linkToRoute( $route, $parameters = array() ) {
+		$urlLinkTo = OC::getRouter()->generate($route, $parameters);
+		return $urlLinkTo;
+	}
+
 	/**
 	 * @brief Creates an url
 	 * @param string $app app
@@ -44,8 +58,8 @@ class OC_Helper {
 			// Check if the app is in the app folder
 			if( $app_path && file_exists( $app_path.'/'.$file )) {
 				if(substr($file, -3) == 'php' || substr($file, -3) == 'css') {
-					$urlLinkTo =  OC::$WEBROOT . '/?app=' . $app;
-					$urlLinkTo .= ($file!='index.php')?'&getfile=' . urlencode($file):'';
+					$urlLinkTo =  OC::$WEBROOT . '/index.php/apps/' . $app;
+					$urlLinkTo .= ($file!='index.php') ? '/' . $file : '';
 				}else{
 					$urlLinkTo =  OC_App::getAppWebPath($app) . '/' . $file;
 				}
diff --git a/lib/ocs.php b/lib/ocs.php
index 7350c3c88218cb63350c5b4d7293de6082776688..60577ec5d575c55bf711b68c7c67c32be01a5fa5 100644
--- a/lib/ocs.php
+++ b/lib/ocs.php
@@ -23,7 +23,8 @@
 *
 */
 
-
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
 
 /**
  * Class to handle open collaboration services API requests
@@ -92,91 +93,144 @@ class OC_OCS {
 			exit();
 		}
 
-		// preprocess url
-		$url = strtolower($_SERVER['REQUEST_URI']);
-		if(substr($url, (strlen($url)-1))<>'/') $url.='/';
-		$ex=explode('/', $url);
-		$paracount=count($ex);
 		$format = self::readData($method, 'format', 'text', '');
 
-		// eventhandler
+		$router = new OC_Router();
+		$router->useCollection('root');
 		// CONFIG
-		// apiconfig - GET - CONFIG
-		if(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'config')) {
-			OC_OCS::apiconfig($format);
+		$router->create('config', '/config.{format}')
+			->defaults(array('format' => $format))
+			->action('OC_OCS', 'apiConfig')
+			->requirements(array('format'=>'xml|json'));
 
 		// PERSON
-		// personcheck - POST - PERSON/CHECK
-		} elseif(($method=='post') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-3]=='person') and ($ex[$paracount-2] == 'check')) {
-			$login = self::readData($method, 'login', 'text');
-			$passwd = self::readData($method, 'password', 'text');
-			OC_OCS::personcheck($format, $login, $passwd);
+		$router->create('person_check', '/person/check.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$login = OC_OCS::readData('post', 'login', 'text');
+					$passwd = OC_OCS::readData('post', 'password', 'text');
+					OC_OCS::personCheck($format,$login,$passwd);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// ACTIVITY
 		// activityget - GET ACTIVITY   page,pagesize als urlparameter
-		}elseif(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')) {
-			$page = self::readData($method, 'page', 'int', 0);
-			$pagesize = self::readData($method, 'pagesize', 'int', 10);
-			if($pagesize<1 or $pagesize>100) $pagesize=10;
-			OC_OCS::activityget($format, $page, $pagesize);
-
+		$router->create('activity_get', '/activity.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$page = OC_OCS::readData('get', 'page', 'int', 0);
+					$pagesize = OC_OCS::readData('get', 'pagesize', 'int', 10);
+					if($pagesize<1 or $pagesize>100) $pagesize=10;
+					OC_OCS::activityGet($format, $page, $pagesize);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// activityput - POST ACTIVITY
-		}elseif(($method=='post') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')) {
-			$message = self::readData($method, 'message', 'text');
-			OC_OCS::activityput($format, $message);
-
+		$router->create('activity_put', '/activity.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$message = OC_OCS::readData('post', 'message', 'text');
+					OC_OCS::activityPut($format,$message);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// PRIVATEDATA
 		// get - GET DATA
-		}elseif(($method=='get') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-2] == 'getattribute')) {
-			OC_OCS::privateDataGet($format);
-
-		}elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-3] == 'getattribute')) {
-			$app=$ex[$paracount-2];
-			OC_OCS::privateDataGet($format, $app);
-		}elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'getattribute')) {
-
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			OC_OCS::privateDataGet($format, $app, $key);
-
+		$router->create('privatedata_get',
+				  '/privatedata/getattribute/{app}/{key}.{format}')
+			->defaults(array('app' => '', 'key' => '', 'format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					OC_OCS::privateDataGet($format, $app, $key);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// set - POST DATA
-		}elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'setattribute')) {
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			$value = self::readData($method, 'value', 'text');
-			OC_OCS::privatedataset($format, $app, $key, $value);
+		$router->create('privatedata_set',
+				  '/privatedata/setattribute/{app}/{key}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					$value=OC_OCS::readData('post', 'value', 'text');
+					OC_OCS::privateDataSet($format, $app, $key, $value);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// delete - POST DATA
-		}elseif(($method=='post') and ($ex[$paracount-6] =='v1.php') and ($ex[$paracount-4] == 'deleteattribute')) {
-			$key=$ex[$paracount-2];
-			$app=$ex[$paracount-3];
-			OC_OCS::privatedatadelete($format, $app, $key);
+		$router->create('privatedata_delete',
+				  '/privatedata/deleteattribute/{app}/{key}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$app = addslashes(strip_tags($parameters['app']));
+					$key = addslashes(strip_tags($parameters['key']));
+					OC_OCS::privateDataDelete($format, $app, $key);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// CLOUD
 		// systemWebApps
-		}elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-4]=='cloud') and ($ex[$paracount-3] == 'system') and ($ex[$paracount-2] == 'webapps')) {
-			OC_OCS::systemwebapps($format);
+		$router->create('system_webapps',
+				  '/cloud/system/webapps.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					OC_OCS::systemwebapps($format);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// quotaget
-		}elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')) {
-			$user=$ex[$paracount-3];
-			OC_OCS::quotaget($format, $user);
-
+		$router->create('quota_get',
+				  '/cloud/user/{user}.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::quotaGet($format, $user);
+					})
+			->requirements(array('format'=>'xml|json'));
 		// quotaset
-		}elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')) {
-			$user=$ex[$paracount-3];
-			$quota = self::readData('post', 'quota', 'int');
-			OC_OCS::quotaset($format, $user, $quota);
+		$router->create('quota_set',
+				  '/cloud/user/{user}.{format}')
+			->post()
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					$quota = self::readData('post', 'quota', 'int');
+					OC_OCS::quotaSet($format, $user, $quota);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// keygetpublic
-		}elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'publickey')) {
-			$user=$ex[$paracount-3];
-			OC_OCS::publicKeyGet($format, $user);
+		$router->create('keygetpublic',
+				  '/cloud/user/{user}/publickey.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::publicKeyGet($format,$user);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 		// keygetprivate
-		}elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'privatekey')) {
-			$user=$ex[$paracount-3];
-			OC_OCS::privateKeyGet($format, $user);
+		$router->create('keygetpublic',
+				  '/cloud/user/{user}/privatekey.{format}')
+			->defaults(array('format' => $format))
+			->action(function ($parameters) {
+					$format = $parameters['format'];
+					$user = $parameters['user'];
+					OC_OCS::privateKeyGet($format,$user);
+					})
+			->requirements(array('format'=>'xml|json'));
 
 
 // add more calls here
@@ -190,13 +244,14 @@ class OC_OCS {
 // sharing
 // versioning
 // news (rss)
-
-
-
-		}else{
+		try {
+			$router->match($_SERVER['PATH_INFO']);
+		} catch (ResourceNotFoundException $e) {
 			$txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
 			$txt.=OC_OCS::getdebugoutput();
 			echo(OC_OCS::generatexml($format, 'failed', 999, $txt));
+		} catch (MethodNotAllowedException $e) {
+			OC_Response::setStatus(405);
 		}
 		exit();
 	}
@@ -378,7 +433,8 @@ class OC_OCS {
 	* @param string $format
 	* @return string xml/json
 	*/
-	private static function apiConfig($format) {
+	public static function apiConfig($parameters) {
+		$format = $parameters['format'];
 		$user=OC_OCS::checkpassword(false);
 		$url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'], 0, -11).'';
 
diff --git a/lib/route.php b/lib/route.php
new file mode 100644
index 0000000000000000000000000000000000000000..89af829d3d342ca9e3e0119526baac1893a7ace8
--- /dev/null
+++ b/lib/route.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use Symfony\Component\Routing\Route;
+
+class OC_Route extends Route {
+	/**
+	 * Specify the method when this route is to be used
+	 *
+	 * @param string $method HTTP method (uppercase)
+	 */
+	public function method($method) {
+		$this->setRequirement('_method', strtoupper($method));
+		return $this;
+	}
+
+	/**
+	 * Specify POST as the method to use with this route
+	 */
+	public function post() {
+		$this->method('POST');
+		return $this;
+	}
+
+	/**
+	 * Specify GET as the method to use with this route
+	 */
+	public function get() {
+		$this->method('GET');
+		return $this;
+	}
+
+	/**
+	 * Specify PUT as the method to use with this route
+	 */
+	public function put() {
+		$this->method('PUT');
+		return $this;
+	}
+
+	/**
+	 * Specify DELETE as the method to use with this route
+	 */
+	public function delete() {
+		$this->method('DELETE');
+		return $this;
+	}
+
+	/**
+	 * Defaults to use for this route
+	 *
+	 * @param array $defaults The defaults
+	 */
+	public function defaults($defaults) {
+		$action = $this->getDefault('action');
+		$this->setDefaults($defaults);
+		if (isset($defaults['action'])) {
+			$action = $defaults['action'];
+		}
+		$this->action($action);
+		return $this;
+	}
+
+	/**
+	 * Requirements for this route
+	 *
+	 * @param array $requirements The requirements
+	 */
+	public function requirements($requirements) {
+		$method = $this->getRequirement('_method');
+		$this->setRequirements($requirements);
+		if (isset($requirements['_method'])) {
+			$method = $requirements['_method'];
+		}
+		if ($method) {
+			$this->method($method);
+		}
+		return $this;
+	}
+
+	/**
+	 * The action to execute when this route matches
+	 * @param string|callable $class the class or a callable
+	 * @param string $function the function to use with the class
+	 *
+	 * This function is called with $class set to a callable or
+	 * to the class with $function
+	 */
+	public function action($class, $function = null) {
+		$action = array($class, $function);
+		if (is_null($function)) {
+			$action = $class;
+		}
+		$this->setDefault('action', $action);
+		return $this;
+	}
+
+	/**
+	 * The action to execute when this route matches, includes a file like
+	 * it is called directly
+	 * @param $file
+	 */
+	public function actionInclude($file) {
+		$function = create_function('$param', 'unset($param["_route"]);$_GET=array_merge($_GET,$param);unset($param);require_once "'.$file.'";');
+		$this->action($function);
+	}
+}
diff --git a/lib/router.php b/lib/router.php
new file mode 100644
index 0000000000000000000000000000000000000000..a471a06802ce57781ae036f08941a6c518959849
--- /dev/null
+++ b/lib/router.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+//use Symfony\Component\Routing\Route;
+
+class OC_Router {
+	protected $collections = array();
+	protected $collection = null;
+	protected $root = null;
+
+	protected $generator= null;
+
+	public function __construct() {
+		$baseUrl = OC_Helper::linkTo('', 'index.php');
+		$method = $_SERVER['REQUEST_METHOD'];
+		$host = OC_Request::serverHost();
+		$schema = OC_Request::serverProtocol();
+		$this->context = new RequestContext($baseUrl, $method, $host, $schema);
+		// TODO cache
+		$this->root = $this->getCollection('root');
+	}
+
+	/**
+	 * loads the api routes
+	 */
+	public function loadRoutes() {
+		foreach(OC_APP::getEnabledApps() as $app){
+			$file = OC_App::getAppPath($app).'/appinfo/routes.php';
+			if(file_exists($file)){
+				$this->useCollection($app);
+				require_once($file);
+				$collection = $this->getCollection($app);
+				$this->root->addCollection($collection, '/apps/'.$app);
+			}
+		}
+		$this->useCollection('root');
+		require_once('core/routes.php');
+	}
+
+	protected function getCollection($name) {
+		if (!isset($this->collections[$name])) {
+			$this->collections[$name] = new RouteCollection();
+		}
+		return $this->collections[$name];
+	}
+
+	/**
+	 * Sets the collection to use for adding routes
+	 *
+	 * @param string $name Name of the colletion to use.
+	 */
+	public function useCollection($name) {
+		$this->collection = $this->getCollection($name);
+	}
+
+	/**
+	 * Create a OC_Route.
+	 *
+	 * @param string $name Name of the route to create.
+	 * @param string $pattern The pattern to match
+	 * @param array  $defaults     An array of default parameter values
+	 * @param array  $requirements An array of requirements for parameters (regexes)
+	 */
+	public function create($name, $pattern, array $defaults = array(), array $requirements = array()) {
+		$route = new OC_Route($pattern, $defaults, $requirements);
+		$this->collection->add($name, $route);
+		return $route;
+	}
+
+	/**
+	 * Find the route matching $url.
+	 *
+	 * @param string $url The url to find
+	 */
+    	public function match($url) {
+		$matcher = new UrlMatcher($this->root, $this->context);
+		$parameters = $matcher->match($url);
+		if (isset($parameters['action'])) {
+			$action = $parameters['action'];
+			if (!is_callable($action)) {
+				var_dump($action);
+				throw new Exception('not a callable action');
+			}
+			unset($parameters['action']);
+			call_user_func($action, $parameters);
+		} elseif (isset($parameters['file'])) {
+			include ($parameters['file']);
+		} else {
+			throw new Exception('no action available');
+		}
+	}
+
+	/**
+	 * Get the url generator
+	 *
+	 */
+	public function getGenerator()
+	{
+		if (null !== $this->generator) {
+			return $this->generator;
+		}
+
+		return $this->generator = new UrlGenerator($this->root, $this->context);
+	}
+
+	/**
+	 * Generate url based on $name and $parameters
+	 *
+	 * @param string $name Name of the route to use.
+	 * @param array $parameters Parameters for the route
+	 */
+	public function generate($name, $parameters = array(), $absolute = false)
+	{
+		return $this->getGenerator()->generate($name, $parameters, $absolute);
+	}
+
+	/**
+	 * Generate JSON response for routing in javascript
+	 */
+	public static function JSRoutes()
+	{
+		// TODO: http caching
+		$routes = array();
+		$router = OC::getRouter();
+		$root = $router->getCollection('root');
+		foreach($root->all() as $name => $route) {
+			$compiled_route = $route->compile();
+			$defaults = $route->getDefaults();
+			unset($defaults['action']);
+			$routes[$name] = array(
+				'tokens' => $compiled_route->getTokens(),
+				'defaults' => $defaults,
+			);
+		}
+		OCP\JSON::success ( array( 'data' => $routes ) );
+	}
+}
diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php
index 0d4b332b792bc75aff29c0e86873a382dc3f42f1..ea536ef77de2f692460db9bc2cab3f905c2a70a8 100644
--- a/lib/search/provider/file.php
+++ b/lib/search/provider/file.php
@@ -16,7 +16,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{
 				$link = OC_Helper::linkTo( 'files', 'index.php', array('dir' => $path));
 				$type = (string)$l->t('Files');
 			}else{
-				$link = OC_Helper::linkTo( 'files', 'download.php', array('file' => $path));
+				$link = OC_Helper::linkToRoute( 'download', array('file' => $path));
 				$mimeBase = $fileData['mimepart'];
 				switch($mimeBase) {
 					case 'audio':
diff --git a/settings/admin.php b/settings/admin.php
index a36f219038666b7fcca5438c70a54f01a919061a..9cb70353f9c8edafafa55468316cca9002be642d 100755
--- a/settings/admin.php
+++ b/settings/admin.php
@@ -5,8 +5,8 @@
  * See the COPYING-README file.
  */
 
-require_once '../lib/base.php';
 OC_Util::checkAdminUser();
+OC_App::loadApps();
 
 OC_Util::addStyle( "settings", "settings" );
 OC_Util::addScript( "settings", "admin" );
diff --git a/settings/ajax/apps/ocs.php b/settings/ajax/apps/ocs.php
index fb78cc89248471008532c2be7fcc7a8aca3a65d8..4d6f1116e7d69b81f863d4236dcd50bf81629641 100644
--- a/settings/ajax/apps/ocs.php
+++ b/settings/ajax/apps/ocs.php
@@ -6,9 +6,6 @@
  * See the COPYING-README file.
  */
 
-// Init owncloud
-require_once '../../../lib/base.php';
-
 OC_JSON::checkAdminUser();
 
 $l = OC_L10N::get('settings');
diff --git a/settings/ajax/changepassword.php b/settings/ajax/changepassword.php
index 12d3b67037ab92aa253e036cc568ee84f1c4ec0d..a0fe5947b6de51aac41e846b20a2434702acfce1 100644
--- a/settings/ajax/changepassword.php
+++ b/settings/ajax/changepassword.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 // Check if we are a user
 OCP\JSON::callCheck();
 OC_JSON::checkLoggedIn();
diff --git a/settings/ajax/creategroup.php b/settings/ajax/creategroup.php
index 431b449a8113e335549ebfecc220f96c1392e27d..0a79527c219f70131e97dfaa8654165338c322a0 100644
--- a/settings/ajax/creategroup.php
+++ b/settings/ajax/creategroup.php
@@ -1,7 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
 OCP\JSON::callCheck();
 OC_JSON::checkAdminUser();
 
diff --git a/settings/ajax/createuser.php b/settings/ajax/createuser.php
index b3e5c23de541d9bd01c30d65808d15431298129f..c87ff422f61369f707b5b0a46cf664bfa777a47f 100644
--- a/settings/ajax/createuser.php
+++ b/settings/ajax/createuser.php
@@ -1,7 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
 OCP\JSON::callCheck();
 OC_JSON::checkSubAdminUser();
 
diff --git a/settings/ajax/disableapp.php b/settings/ajax/disableapp.php
index 977a536af215d084e39584a50dd3d8c89f4ceea1..a39b06b9c7d6ffa27287cf6c005cf7f57e9c7b44 100644
--- a/settings/ajax/disableapp.php
+++ b/settings/ajax/disableapp.php
@@ -1,6 +1,4 @@
 <?php
-// Init owncloud
-require_once '../../lib/base.php';
 OC_JSON::checkAdminUser();
 OCP\JSON::callCheck();
 OC_JSON::setContentTypeHeader();
diff --git a/settings/ajax/enableapp.php b/settings/ajax/enableapp.php
index c3b3491db931f9d706a3f89eacac03b8a2f23200..f4d5c53adeff3af09cb623e2cb08be6e9ba3a6d3 100644
--- a/settings/ajax/enableapp.php
+++ b/settings/ajax/enableapp.php
@@ -1,7 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
 OC_JSON::checkAdminUser();
 OCP\JSON::callCheck();
 OC_JSON::setContentTypeHeader();
diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php
index 9b9240f82539224083233bcb886d4dad56abf2b2..22128ef57b53278ae3fa1f0e9ab76badb74d10dd 100644
--- a/settings/ajax/getlog.php
+++ b/settings/ajax/getlog.php
@@ -5,9 +5,6 @@
  * See the COPYING-README file.
  */
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkAdminUser();
 
 $count=(isset($_GET['count']))?$_GET['count']:50;
diff --git a/settings/ajax/lostpassword.php b/settings/ajax/lostpassword.php
index 2a40ba09a8ac3506af2050b0b5e191897b2db28b..b5f47bbceabe88a41464da3ed6283b14f3b85744 100644
--- a/settings/ajax/lostpassword.php
+++ b/settings/ajax/lostpassword.php
@@ -1,7 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
 OC_JSON::checkLoggedIn();
 OCP\JSON::callCheck();
 
diff --git a/settings/ajax/openid.php b/settings/ajax/openid.php
index ecec085383c1124c599c3c7f6f1cb205ccdcfcd0..23c43c3c48eb209db10b5dfce13c86a0156776d4 100644
--- a/settings/ajax/openid.php
+++ b/settings/ajax/openid.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 $l=OC_L10N::get('settings');
 
 OC_JSON::checkLoggedIn();
diff --git a/settings/ajax/removegroup.php b/settings/ajax/removegroup.php
index 33e1a514c88e8b55964a0a530a095cff2f4cbb5a..798d7916e61f2de452e0abf90ebcd6354df28a9f 100644
--- a/settings/ajax/removegroup.php
+++ b/settings/ajax/removegroup.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkAdminUser();
 OCP\JSON::callCheck();
 
diff --git a/settings/ajax/removeuser.php b/settings/ajax/removeuser.php
index 1e3cd2993b01088e9fc227eb90feb307c01a7635..9ffb32a0b23fa5704cf69dba540c134e93f65960 100644
--- a/settings/ajax/removeuser.php
+++ b/settings/ajax/removeuser.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkSubAdminUser();
 OCP\JSON::callCheck();
 
@@ -25,4 +22,4 @@ if( OC_User::deleteUser( $username )) {
 }
 else{
 	OC_JSON::error(array("data" => array( "message" => $l->t("Unable to delete user") )));
-}
\ No newline at end of file
+}
diff --git a/settings/ajax/setlanguage.php b/settings/ajax/setlanguage.php
index 42eea7a96fdc03bb793fbd2e7bc17694d2832da0..aebb1b31b6f9f0bfad0cd77f169119a1f057ba99 100644
--- a/settings/ajax/setlanguage.php
+++ b/settings/ajax/setlanguage.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 $l=OC_L10N::get('settings');
 
 OC_JSON::checkLoggedIn();
diff --git a/settings/ajax/setloglevel.php b/settings/ajax/setloglevel.php
index 982899e106adbb09355a103863cae73a8e95f491..542219f86c63cf3b9fb35522f65c2892695d3d17 100644
--- a/settings/ajax/setloglevel.php
+++ b/settings/ajax/setloglevel.php
@@ -5,7 +5,6 @@
  * See the COPYING-README file.
  */
 
-require_once '../../lib/base.php';
 OC_Util::checkAdminUser();
 OCP\JSON::callCheck();
 
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
index 2352ae9e4276cac802fbdbdf38e0c39b1aabc6b8..4b32585b3066cff0f955f33e4b88ce582bd7f94e 100644
--- a/settings/ajax/setquota.php
+++ b/settings/ajax/setquota.php
@@ -5,9 +5,6 @@
  * See the COPYING-README file.
  */
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkSubAdminUser();
 OCP\JSON::callCheck();
 
diff --git a/settings/ajax/togglegroups.php b/settings/ajax/togglegroups.php
index 65747968c172bf62abf8665750757260e7af511c..de941f991320f7fffb5b101ab910f339e2fbe996 100644
--- a/settings/ajax/togglegroups.php
+++ b/settings/ajax/togglegroups.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkSubAdminUser();
 OCP\JSON::callCheck();
 
diff --git a/settings/ajax/togglesubadmins.php b/settings/ajax/togglesubadmins.php
index 5f7126dca347dd94ce0c8dd853737a09afb35b8e..7aaa90aad5fbd1ae159d6062c85e6266bdb2f570 100644
--- a/settings/ajax/togglesubadmins.php
+++ b/settings/ajax/togglesubadmins.php
@@ -1,8 +1,5 @@
 <?php
 
-// Init owncloud
-require_once '../../lib/base.php';
-
 OC_JSON::checkAdminUser();
 OCP\JSON::callCheck();
 
@@ -16,4 +13,4 @@ if(OC_SubAdmin::isSubAdminofGroup($username, $group)) {
 	OC_SubAdmin::createSubAdmin($username, $group);
 }
 
-OC_JSON::success();
\ No newline at end of file
+OC_JSON::success();
diff --git a/settings/ajax/userlist.php b/settings/ajax/userlist.php
index 840b6d72dc7bfd861616025ad976c2fdf48fd0f5..61b1a388fc33154d57fabff468ecb7f18a9e54d2 100644
--- a/settings/ajax/userlist.php
+++ b/settings/ajax/userlist.php
@@ -20,8 +20,6 @@
  *
  */
 
-require_once '../../lib/base.php';
-
 OC_JSON::callCheck();
 OC_JSON::checkSubAdminUser();
 if (isset($_GET['offset'])) {
@@ -49,4 +47,4 @@ if (OC_Group::inGroup(OC_User::getUser(), 'admin')) {
 			'quota' => OC_Preferences::getValue($user, 'files', 'quota', 'default'));
 	}
 }
-OC_JSON::success(array('data' => $users));
\ No newline at end of file
+OC_JSON::success(array('data' => $users));
diff --git a/settings/apps.php b/settings/apps.php
index a1c1bf6aa5303710fb3d308a8111e5414ed23cb4..8134b44143a1c1cafbf662f6a320bc7e82d95b14 100644
--- a/settings/apps.php
+++ b/settings/apps.php
@@ -21,8 +21,8 @@
 *
 */
 
-require_once '../lib/base.php';
 OC_Util::checkAdminUser();
+OC_App::loadApps();
 
 // Load the files we need
 OC_Util::addStyle( "settings", "settings" );
diff --git a/settings/help.php b/settings/help.php
index 9157308dd57cf81e3023bb6a3ecf5a45b56d580f..69a5ec9c14691fd58861a2f5a223a70a500bf682 100644
--- a/settings/help.php
+++ b/settings/help.php
@@ -5,9 +5,8 @@
  * See the COPYING-README file.
  */
 
-require_once '../lib/base.php';
 OC_Util::checkLoggedIn();
-
+OC_App::loadApps();
 
 // Load the files we need
 OC_Util::addStyle( "settings", "settings" );
diff --git a/settings/js/users.js b/settings/js/users.js
index 20bd94993bce5f309d1df454588dd22a2912efc4..1474ebcdd81a598a18891f8e6010d4a6995b14ff 100644
--- a/settings/js/users.js
+++ b/settings/js/users.js
@@ -130,7 +130,7 @@ var UserList={
 		if (typeof UserList.offset === 'undefined') {
 			UserList.offset = $('tbody tr').length;
 		}
-		$.get(OC.filePath('settings', 'ajax', 'userlist.php'), { offset: UserList.offset }, function(result) {
+		$.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset }), function(result) {
 			if (result.status === 'success') {
 				$.each(result.data, function(index, user) {
 					var tr = UserList.add(user.name, user.groups, user.subadmin, user.quota, false);
diff --git a/settings/personal.php b/settings/personal.php
index 2031edd8df8839e0f492711b8502ff91dd6ec069..f28ab2ae755feddf6676da15a639081ff502d635 100644
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -5,8 +5,8 @@
  * See the COPYING-README file.
  */
 
-require_once '../lib/base.php';
 OC_Util::checkLoggedIn();
+OC_App::loadApps();
 
 // Highlight navigation entry
 OC_Util::addScript( 'settings', 'personal' );
diff --git a/settings/routes.php b/settings/routes.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f4f83e4542298b28c2631e910de268bf177789e
--- /dev/null
+++ b/settings/routes.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+// Settings pages
+$this->create('settings_help', '/settings/help')
+	->actionInclude('settings/help.php');
+$this->create('settings_personal', '/settings/personal')
+	->actionInclude('settings/personal.php');
+$this->create('settings_settings', '/settings')
+	->actionInclude('settings/settings.php');
+$this->create('settings_users', '/settings/users')
+	->actionInclude('settings/users.php');
+$this->create('settings_apps', '/settings/apps')
+	->actionInclude('settings/apps.php');
+$this->create('settings_admin', '/settings/admin')
+	->actionInclude('settings/admin.php');
+// Settings ajax actions
+// users
+$this->create('settings_ajax_userlist', '/settings/ajax/userlist')
+	->actionInclude('settings/ajax/userlist.php');
+$this->create('settings_ajax_createuser', '/settings/ajax/createuser.php')
+	->actionInclude('settings_ajax_createuser');
+$this->create('settings_ajax_removeuser', '/settings/ajax/removeuser.php')
+	->actionInclude('settings/ajax/removeuser.php');
+$this->create('settings_ajax_setquota', '/settings/ajax/setquota.php')
+	->actionInclude('settings/ajax/setquota.php');
+$this->create('settings_ajax_creategroup', '/settings/ajax/creategroup.php')
+	->actionInclude('settings_ajax_creategroup');
+$this->create('settings_ajax_togglegroups', '/settings/ajax/togglegroups.php')
+	->actionInclude('settings/ajax/togglegroups.php');
+$this->create('settings_ajax_togglesubadmins', '/settings/ajax/togglesubadmins.php')
+	->actionInclude('settings/ajax/togglesubadmins.php');
+$this->create('settings_ajax_removegroup', '/settings/ajax/removegroup.php')
+	->actionInclude('settings/ajax/removegroup.php');
+$this->create('settings_ajax_changepassword', '/settings/ajax/changepassword.php')
+	->actionInclude('settings/ajax/changepassword.php');
+// personel
+$this->create('settings_ajax_lostpassword', '/settings/ajax/lostpassword.php')
+	->actionInclude('settings/ajax/lostpassword.php');
+$this->create('settings_ajax_setlanguage', '/settings/ajax/setlanguage.php')
+	->actionInclude('settings/ajax/setlanguage.php');
+// apps
+$this->create('settings_ajax_apps_ocs', '/settings/ajax/apps/ocs.php')
+	->actionInclude('settings/ajax/apps/ocs.php');
+$this->create('settings_ajax_enableapp', '/settings/ajax/enableapp.php')
+	->actionInclude('settings/ajax/enableapp.php');
+$this->create('settings_ajax_disableapp', '/settings/ajax/disableapp.php')
+	->actionInclude('settings/ajax/disableapp.php');
+// admin
+$this->create('settings_ajax_getlog', '/settings/ajax/getlog.php')
+	->actionInclude('settings/ajax/getlog.php');
+$this->create('settings_ajax_setloglevel', '/settings/ajax/setloglevel.php')
+	->actionInclude('settings/ajax/setloglevel.php');
+
+// apps/user_openid
+$this->create('settings_ajax_openid', '/settings/ajax/openid.php')
+	->actionInclude('settings/ajax/openid.php');
diff --git a/settings/settings.php b/settings/settings.php
index 68c07ff60f0733a8a39f62b5bcef468e8ae8b195..add94b5b01135ff54018268c0a5dee077ddf1307 100644
--- a/settings/settings.php
+++ b/settings/settings.php
@@ -5,9 +5,9 @@
  * See the COPYING-README file.
  */
 
-require_once '../lib/base.php';
 OC_Util::checkLoggedIn();
 OC_Util::verifyUser();
+OC_App::loadApps();
 
 OC_Util::addStyle( 'settings', 'settings' );
 OC_App::setActiveNavigationEntry( 'settings' );
diff --git a/settings/users.php b/settings/users.php
index e76505cc78d6e9d44206f9b7a6cfc0000a985610..6eaae474538a26385eff0a6cc74ba94e6936ba28 100644
--- a/settings/users.php
+++ b/settings/users.php
@@ -5,8 +5,8 @@
  * See the COPYING-README file.
  */
 
-require_once '../lib/base.php';
 OC_Util::checkSubAdminUser();
+OC_App::loadApps();
 
 // We have some javascript foo!
 OC_Util::addScript( 'settings', 'users' );
@@ -57,4 +57,4 @@ $tmpl->assign( 'subadmins', $subadmins);
 $tmpl->assign( 'numofgroups', count($accessiblegroups));
 $tmpl->assign( 'quota_preset', $quotaPreset);
 $tmpl->assign( 'default_quota', $defaultQuota);
-$tmpl->printPage();
\ No newline at end of file
+$tmpl->printPage();