From 519a526419a6f28c837bd0db8ddd2f31acf729d2 Mon Sep 17 00:00:00 2001
From: BlackLight <blacklight@autistici.org>
Date: Wed, 29 Dec 2010 21:32:43 +0100
Subject: [PATCH] Tons of multiuser fixes and improvements

---
 README                       |  14 ++-
 blash.json                   | 205 ----------------------------------
 commands/cat.json            |  14 ++-
 commands/cd.json             |   6 +-
 commands/find.json           |   4 +-
 commands/logout.json         |  29 +++++
 commands/ls.json             |  33 ++++--
 commands/passwd.json         |   6 +
 commands/su.json             |  29 ++++-
 commands/useradd.json        |   6 +
 commands/whoami.json         |   6 +
 index.html                   |   4 +-
 modules/users/.users.php.swp | Bin 20480 -> 0 bytes
 modules/users/files.php      |  45 ++++++++
 modules/users/user_utils.php | 208 +++++++++++++++++++++++++++++++++++
 modules/users/userlist.php   |   2 +-
 modules/users/users.php      |  49 +++------
 blash.js => system/blash.js  |  62 +++++++++--
 system/blash.json            |  73 ++++++++++++
 system/files.json            | 158 ++++++++++++++++++++++++++
 system/files_json.php        | 163 +++++++++++++++++++++++++++
 md5.js => system/md5.js      |   0
 22 files changed, 838 insertions(+), 278 deletions(-)
 delete mode 100644 blash.json
 delete mode 100644 modules/users/.users.php.swp
 create mode 100644 modules/users/files.php
 create mode 100644 modules/users/user_utils.php
 rename blash.js => system/blash.js (91%)
 create mode 100644 system/blash.json
 create mode 100644 system/files.json
 create mode 100644 system/files_json.php
 rename md5.js => system/md5.js (100%)

diff --git a/README b/README
index 926f392..dd916a6 100644
--- a/README
+++ b/README
@@ -27,6 +27,9 @@ pseudo-terminal are represented in normal font, the files that links to an
 external content is instead represented as an `executable' file (by default, in
 colour green).
 
+Q.: Which is the default password for 'root' user?
+A.: blash. Remember to use it only one time, then change it using the passwd command.
+
 Q.: Which browsers are compatible with blash?
 A.: So far blash has been successfully tested with the following browsers:
 
@@ -45,11 +48,14 @@ blash. Internet Explorer is currently NOT working with blash, at least any
 release <= 8. I'm sorry, choose a decent browser.
 
 Q.: How do I add the contents for my web site in blash?
-A.: Open the file blash.json. This JSON file contains the preferences for your
+A.: Open the file system/blash.json. This JSON file contains the preferences for your
 shell (banner, prompt style, default user, escape sequences recognized in the
-prompt string...), the commands to be used, and the array "directories". This
-array is the container of your web site data. Each element can have the
-following fields:
+prompt string...), the commands to be used, and so on. Change this file in order to
+change the preferences of your CMS.
+
+The contents of your web site are held in the file system/files_json.php. Modify this
+file directly using the example contents as trace for adding or removing new contents.
+Each content can include the following fields:
 
 - path (MANDATORY), the full absolute path that identifies that resource
 - type (MANDATORY), the resource type (directory or file)
diff --git a/blash.json b/blash.json
deleted file mode 100644
index a3accad..0000000
--- a/blash.json
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * Sample configuration and contents
- */
-
-{
-	"banner" : "blash version 0.1<br/>" +
-		"Copyright (C) 2010 BlackLight &lt;blacklight@autistici.org&gt;" +
-		"<br/>Licence GPLv3+: GNU GPL version 3 or later " +
-		"&lt;<a class=\"bannerLink\" href=\"http://gnu.org/licences/gpl.html\" target=\"_new\">" +
-		"http://gnu.org/licences/gpl.html</a>&gt;<br/>" +
-		"Source code available at <a class=\"bannerLink\" target=\"_new\" " +
-		"href=\"https://github.com/BlackLight/blash\">" +
-		"https://github.com/BlackLight/blash</a><br/><br/>" +
-		"This is free software; you are free to change and " +
-		"redistribuite it.<br/>There is NO WARRANTY, to the " +
-		"extent permitted by law.<br/>" +
-		"Type '<span class=\"brief\">man blash</span>' for help on usage and available commands<br/><br/>",
-
-	"user" : "guest",
-	"machine" : "localhost",
-	"shellName" : "blash",
-	"basepath" : "/",
-	"promptText" : "[#{800}%n#{888}@#{800}%m#{888} %W] $ ",
-	"promptSequences" : [
-		{
-			"sequence" : "%n",
-			"default_text" : "guest",
-			"text" : function ()  {
-				return shell.user;
-			},
-		},
-		{
-			"sequence" : "%m",
-			"default_text" : "localhost",
-			"text" : function ()  {
-				return shell.json.machine;
-			},
-		},
-		{
-			"sequence" : "%W",
-			"default_text" : "/",
-			"text" : function ()  {
-				return shell.path;
-			},
-		}
-	],
-
-	"directories" : [
-		{
-			"path" : "/",
-			"type" : "directory",
-		},
-		{
-			"path" : "/blog",
-			"type" : "directory",
-		},
-		{
-			"path" : "/news",
-			"type" : "directory",
-		},
-		{
-			"path" : "/forum",
-			"type" : "directory",
-		},
-		{
-			"path" : "/tutorials",
-			"type" : "directory",
-		},
-		{
-			"path" : "/software",
-			"type" : "directory",
-		},
-		{
-			"path" : "/etc",
-			"type" : "directory",
-		},
-		{
-			"path" : "/home",
-			"type" : "directory",
-		},
-		{
-			"path" : "/home/guest",
-			"type" : "directory",
-		},
-		{
-			"path" : "/home/guest/mbox",
-			"type" : "file",
-			"content" : "No new mail",
-		},
-		{
-			"path" : "/google",
-			"type" : "file",
-			"href" : "http://www.google.com",
-		},
-		{
-			"path" : "/blog/post1",
-			"type" : "file",
-			"content" : "This is my first post",
-		},
-		{
-			"path" : "/blog/post2",
-			"type" : "file",
-			"content" : "This is my second post",
-		},
-		{
-			"path" : "/blog/post3",
-			"type" : "file",
-			"content" : "This is my third post",
-		},
-		{
-			"path" : "/etc/blashrc",
-			"type" : "file",
-			"content" : "This is the default blash configuration file",
-		},
-		{
-			"path" : "/forum/post1",
-			"type" : "file",
-			"content" : "<b>Sent by <i>admin</i> at <i>00:00:01</i></b><br/>Welcome to the forum<br/><br/>\n" +
-				"<b>Sent by <i>troll</i> at <i>00:00:02</i></b><br/>lulz<br/>\n",
-		},
-		{
-			"path" : "/forum/post2",
-			"type" : "file",
-			"content" : "<b>Sent by <i>lolcat</i> at <i>00:00:03</i></b><br/>Can I haz cheezburger?<br/>\n",
-		},
-		{
-			"path" : "/home/guest/.blashrc",
-			"type" : "file",
-			"content" : "Custom blash configuration file",
-		},
-		{
-			"path" : "/home/guest/mbox",
-			"type" : "file",
-			"content" : "No new mail",
-		},
-		{
-			"path" : "/news/news1",
-			"type" : "file",
-			"content" : "Nothing new under the sun",
-		},
-		{
-			"path" : "/software/soft1",
-			"type" : "file",
-			"href" : "/software/soft1.tar.gz",
-		},
-		{
-			"path" : "/software/soft2",
-			"type" : "file",
-			"href" : "/software/soft2.tar.gz",
-		},
-		{
-			"path" : "/software/soft3",
-			"type" : "file",
-			"href" : "/software/soft3.tar.gz",
-		},
-		{
-			"path" : "/tutorials/tut1",
-			"type" : "file",
-			"href" : "/software/tut1.pdf",
-		},
-		{
-			"path" : "/tutorials/tut2",
-			"type" : "file",
-			"href" : "/software/tut2.pdf",
-		},
-		{
-			"path" : "/github",
-			"type" : "file",
-			"href" : "https://github.com/BlackLight/blash",
-		},
-		{
-			"path" : "/aboutme",
-			"type" : "file",
-			"content" : "Luke, I am your father",
-		},
-		{
-			"path" : "/contacts",
-			"type" : "file",
-			"content" : "Contact me at spam@montypython.com",
-		},
-		{
-			"path" : "/irc",
-			"type" : "file",
-			"content" : "IRC channel at #thegame@irc.randomstuff.com",
-		},
-	],
-
-	"commands" : [
-		"cat",
-		"cd",
-		"clear",
-		"echo",
-		"eval",
-		"find",
-		"logout",
-		"ls",
-		"man",
-		"passwd",
-		"pwd",
-		"su",
-		"useradd",
-		"whoami",
-	],
-}
-
diff --git a/commands/cat.json b/commands/cat.json
index 753ca61..e560b36 100644
--- a/commands/cat.json
+++ b/commands/cat.json
@@ -9,14 +9,14 @@
 	"action" : function ( arg )
 	{
 		var out = '';
+		var found = false;
+		var dir = shell.files;
 
 		if ( !arg || arg.length == 0 )
 		{
 			return "Argument required<br/>\n";
 		}
 
-		var found = false;
-		var dir = shell.json.directories;
 		arg = shell.expandPath ( arg );
 
 		for ( var i=0; i < dir.length && !found; i++ )
@@ -39,7 +39,15 @@
 			}
 		}
 
+		if ( !found )
+		{
+			arg = arg.replace ( '<', '&lt;' );
+			arg = arg.replace ( '>', '&gt;' );
+			return "cat: " + arg + ": No such file or directory<br/>\n";
+		}
+
+		out = out.replace ( /<br\/>\s*$/, '' );
 		return out;
-	},
+	}
 }
 
diff --git a/commands/cd.json b/commands/cd.json
index e1665fb..4dedda8 100644
--- a/commands/cd.json
+++ b/commands/cd.json
@@ -18,13 +18,13 @@
 		var found = false;
 		arg = shell.expandPath ( arg );
 
-		for ( var i=0; i < shell.json.directories.length && !found; i++ )
+		for ( var i=0; i < shell.files.length && !found; i++ )
 		{
-			if ( shell.json.directories[i].path == arg )
+			if ( shell.files[i].path == arg )
 			{
 				found = true;
 
-				if ( shell.json.directories[i].type != 'directory' )
+				if ( shell.files[i].type != 'directory' )
 				{
 					return "cd: not a directory: " + arg + "<br/>\n";
 				}
diff --git a/commands/find.json b/commands/find.json
index 596b6fd..c5501b7 100644
--- a/commands/find.json
+++ b/commands/find.json
@@ -22,9 +22,9 @@
 
 		var re = new RegExp ( arg, "i" );
 
-		for ( var i in shell.json.directories )
+		for ( var i in shell.files )
 		{
-			var dir = shell.json.directories[i];
+			var dir = shell.files[i];
 
 			if ( dir.path.match ( re ))
 			{
diff --git a/commands/logout.json b/commands/logout.json
index 116ee78..856b626 100644
--- a/commands/logout.json
+++ b/commands/logout.json
@@ -10,6 +10,12 @@
 	{
 		var out = '';
 
+		if ( !shell.has_users )
+		{
+			return "Users module not enabled<br/>\n";
+		}
+
+
 		if ( shell.user == shell.json.user )
 		{
 			return out;
@@ -27,6 +33,29 @@
 		http.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
 		http.setRequestHeader( "Content-length", params.length );
 		http.setRequestHeader( "Connection", "close" );
+
+		http.onreadystatechange = function ()
+		{
+			if ( http.readyState == 4 && http.status == 200 )
+			{
+				var files_config = window.location.href;
+				files_config = files_config.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/files.php' );
+
+				var http2 = new XMLHttpRequest();
+				http2.open ( "GET", files_config, true );
+
+				http2.onreadystatechange = function ()
+				{
+					if ( http2.readyState == 4 && http2.status == 200 )
+					{
+						shell.files = eval ( '(' + http2.responseText + ')' );
+					}
+				}
+
+				http2.send ( null );
+			}
+		}
+
 		http.send ( params );
 		return out;
 	},
diff --git a/commands/ls.json b/commands/ls.json
index 373845d..56e421b 100644
--- a/commands/ls.json
+++ b/commands/ls.json
@@ -9,12 +9,13 @@
 	"action" : function ( arg )
 	{
 		var dirs = new Array();
-		var out = '';
 		var exists = false;
+		var re = null;
 
 		if ( !arg || arg.length == 0 )
 		{
-			var re = null;
+			re = null;
+			arg = shell.path;
 
 			if ( shell.path == '/' )
 			{
@@ -24,14 +25,24 @@
 			}
 		} else if ( arg && arg.length > 0 ) {
 			arg = shell.expandPath ( arg );
-			var re = new RegExp ( '^' + arg + '/[^/]+$' );
+			re = new RegExp ( '^' + arg + '/[^/]+$' );
 		}
 
-		for ( var i=0; i < shell.json.directories.length; i++ )
-		{
-			var dir = shell.json.directories[i];
+		shell.re = re;
+		return this.ls ( arg );
+	},
 
-			if ( dir.path.match ( re ))
+	"ls" : function ( arg )
+	{
+		var dirs = new Array();
+		var out = '';
+		var exists = false;
+
+		for ( var i=0; i < shell.files.length; i++ )
+		{
+			var dir = shell.files[i];
+
+			if ( dir.path.match ( shell.re ))
 			{
 				exists = true;
 				dir.path.match ( /\/([^\/]+)$/ );
@@ -114,9 +125,9 @@
 
 		if ( !exists )
 		{
-			for ( var i=0; i < shell.json.directories.length; i++ )
+			for ( var i=0; i < shell.files.length; i++ )
 			{
-				var dir = shell.json.directories[i];
+				var dir = shell.files[i];
 				arg = arg.replace ( /\/+$/, '' );
 
 				if ( dir.path.match ( arg ))
@@ -135,9 +146,9 @@
 						(( shell.path == '/' ) ? '' : '/' ) + arg );
 			}
 
-			for ( var i=0; i < shell.json.directories.length; i++ )
+			for ( var i=0; i < shell.files.length; i++ )
 			{
-				var dir = shell.json.directories[i];
+				var dir = shell.files[i];
 
 				if ( dir.path.match ( re ))
 				{
diff --git a/commands/passwd.json b/commands/passwd.json
index 84f400b..842e7b8 100644
--- a/commands/passwd.json
+++ b/commands/passwd.json
@@ -97,6 +97,12 @@
 	{
 		var out = '';
 
+		if ( !shell.has_users )
+		{
+			return "Users module not enabled<br/>\n";
+		}
+
+
 		shell.auto_prompt_focus = false;
 		shell.auto_prompt_refresh = false;
 		shell.newuser = arg;
diff --git a/commands/su.json b/commands/su.json
index ea03868..07e5189 100644
--- a/commands/su.json
+++ b/commands/su.json
@@ -10,6 +10,11 @@
 	{
 		var out = '';
 
+		if ( !shell.has_users )
+		{
+			return "Users module not enabled<br/>\n";
+		}
+
 		if ( !arg || arg.length == 0 )
 		{
 			arg = 'root';
@@ -60,17 +65,29 @@
 			{
 				if ( http.readyState == 4 && http.status == 200 )
 				{
-					if ( http.responseText.match ( /^Successfully logged in as '(.+?)'\s+(.*)\s*$/i ))
+					if ( http.responseText.match ( /^Successfully logged in as '(.+?)'/i ))
 					{
 						var user = RegExp.$1;
-						var auth = RegExp.$2;
-
 						shell.user = user;
-						shell.cmdOut.innerHTML = "Successfully logged in as '" + user + "'";
-					} else  {
-						shell.cmdOut.innerHTML = '';
+
+						var files_config = window.location.href;
+						files_config = files_config.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/files.php' );
+
+						var http2 = new XMLHttpRequest();
+						http2.open ( "GET", files_config, true );
+
+						http2.onreadystatechange = function ()
+						{
+							if ( http2.readyState == 4 && http2.status == 200 )
+							{
+								shell.files = eval ( '(' + http2.responseText + ')' );
+							}
+						}
+
+						http2.send ( null );
 					}
 
+					shell.cmdOut.innerHTML = http.responseText;
 					shell.refreshPrompt ( false, false );
 				}
 			}
diff --git a/commands/useradd.json b/commands/useradd.json
index c1dc323..b218b2f 100644
--- a/commands/useradd.json
+++ b/commands/useradd.json
@@ -78,6 +78,12 @@
 	{
 		var out = '';
 
+		if ( !shell.has_users )
+		{
+			return "Users module not enabled<br/>\n";
+		}
+
+
 		if ( !arg || arg.length == 0 )
 		{
 			return "Usage: " + this.name + " &lt;username&gt;<br/>\n";
diff --git a/commands/whoami.json b/commands/whoami.json
index 049a868..7ad70c0 100644
--- a/commands/whoami.json
+++ b/commands/whoami.json
@@ -9,6 +9,12 @@
 	"action" : function ( arg )
 	{
 		var out = '';
+
+		if ( !shell.has_users )
+		{
+			return "guest<br/>\n";
+		}
+
 		
 		if ( arg )
 		{
diff --git a/index.html b/index.html
index 5c6f0b4..95664f2 100644
--- a/index.html
+++ b/index.html
@@ -1,8 +1,8 @@
 <html>
 	<head>
 		<title>Blash - An AJAX interactive shell emulator for web browsing</title>
-		<script type="text/javascript" language="javascript" src="blash.js"></script>
-		<script type="text/javascript" language="javascript" src="md5.js"></script>
+		<script type="text/javascript" language="javascript" src="system/blash.js"></script>
+		<script type="text/javascript" language="javascript" src="system/md5.js"></script>
 		<link rel="stylesheet" href="blash.css" type="text/css">
 	</head>
 
diff --git a/modules/users/.users.php.swp b/modules/users/.users.php.swp
deleted file mode 100644
index df69205fabc55595d66de96cc65ca162ef80c074..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 20480
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

diff --git a/modules/users/files.php b/modules/users/files.php
new file mode 100644
index 0000000..7e057a7
--- /dev/null
+++ b/modules/users/files.php
@@ -0,0 +1,45 @@
+<?php
+
+include '../../system/files_json.php';
+include 'user_utils.php';
+
+if ( !$files_json || strlen ( $files_json ) == 0 )
+{
+	print "Empty JSON files content\n";
+	return false;
+}
+
+$json = json_decode ( $files_json, true );
+
+if ( !$json )
+{
+	print "Empty or invalid JSON files content\n";
+	return false;
+}
+
+print "[\n";
+
+for ( $i=0; $i < count ( $json ); $i++ )
+{
+	$can_read = false;
+	$perms = getPerms ( $json[$i]['path'] );
+	$perms = json_decode ( $perms, true );
+
+	if ( $perms['read'] == true )
+	{
+		$keys = array_keys ( $json[$i] );
+
+		print "{\n";
+
+		foreach ( $keys as $k )
+		{
+			print '"'.$k.'": "'.$json[$i][$k].'",'."\n";
+		}
+
+		print "},\n\n";
+	}
+}
+
+print "]\n";
+
+?>
diff --git a/modules/users/user_utils.php b/modules/users/user_utils.php
new file mode 100644
index 0000000..b73bda1
--- /dev/null
+++ b/modules/users/user_utils.php
@@ -0,0 +1,208 @@
+<?php
+
+function getUser ()
+{
+	include 'userlist.php';
+
+	if ( isset ( $_COOKIE['username'] ) && isset ( $_COOKIE['auth'] ))
+	{
+		if ( !( $xml = new SimpleXMLElement ( $xmlcontent )))
+		{
+			return "Unable to open the users XML file\n";
+		}
+
+		for ( $i = 0; $i < count ( $xml->user ); $i++ )
+		{
+			if ( !strcasecmp ( $xml->user[$i]['name'], $_COOKIE['username'] ))
+			{
+				$auth = md5 ( $xml->user[$i]['name'] . $xml->user[$i]['pass'] );
+
+				if ( !strcasecmp ( $auth, $_COOKIE['auth'] ))
+				{
+					return $xml->user[$i]['name'];
+				} else {
+					return "guest";
+				}
+			}
+		}
+
+		return "guest";
+	}
+
+	return "guest";
+}
+
+function getPerms ( $resource )
+{
+	include "../../system/files_json.php";
+
+	if ( !$files_json || strlen ( $files_json ) == 0 )
+	{
+		return '{ "message": "Empty JSON file container" }';
+	}
+
+	$user = getUser();
+	$resource = str_replace ( '"', '\"', $resource );
+
+	if ( $user == 'root' )
+	{
+		return '{ "resource" : "'.$resource.'", "read" : true, "write" : true }'."\n";
+	}
+
+	if ( preg_match ( '@/[^/]+/+$@', $resource ))
+	{
+		$resource = preg_replace ( '@/+$@', '', $resource );
+	}
+
+	$json = json_decode ( $files_json, true );
+	$dir = $resource;
+	$response = "{ \"resource\": \"$dir\"\n";
+
+	$read_perm_found = false;   // Have we found information about the read permissions of this resource?
+	$write_perm_found = false;  // Have we found information about the write permissions of this resource?
+	$res_found = false;         // Have we found the resource?
+	$can_read = false;
+	$can_write = false;
+
+	if ( !$json || count ( $json ) == 0 )
+	{
+		return '{ "message": "Empty JSON file" }';
+	}
+
+	do
+	{
+		for ( $i=0; $i < count ( $json ); $i++ )
+		{
+			if ( !strcmp ( $json[$i]['path'], $dir ))
+			{
+				$res_found = true;
+
+				if ( !$read_perm_found )
+				{
+					if ( isset ( $json[$i]['can_read'] ))
+					{
+						$read_perm_found = true;
+						$read = $json[$i]['can_read'];
+
+						if ( preg_match ( '/[\s,]*'.$user.'[\s,]*/', $read ))
+						{
+							$response .= ", \"read\": true\n";
+							$can_read = true;
+						} else if ( preg_match_all ( "/[\s,]?@([^\s,]+)[\s,]?/", $read, $matches )) {
+							for ( $j=1; $j < count ( $matches ); $j++ )
+							{
+								if ( !strcasecmp ( $matches[$j][0], "all" ))
+								{
+									$response .= ", \"read\": true\n";
+									$can_read = true;
+								} else if ( !strcasecmp ( $matches[$j], "registered" ) && $user != 'guest' ) {
+									$response .= ", \"read\": true\n";
+									$can_read = true;
+								} else {
+									if ( isset ( $json['groups'] ))
+									{
+										for ( $k=0; $k < count ( $json['groups'] ); $k++ )
+										{
+											if ( $json['groups'][$k]['name'] == $matches[$k] )
+											{
+												if ( isset ( $json['groups'][$k]['users'] ))
+												{
+													if ( preg_match ( '/[\s,]*'.$user.'[\s,]*/', $json['groups'][$k]['users'] ))
+													{
+														$can_read = true;
+													}
+												}
+
+												break;
+											}
+										}
+									}
+								}
+							}
+						}
+
+						if ( !$can_read )
+						{
+							$response .= ", \"read\": false\n";
+						}
+					}
+				}
+
+				if ( !$write_perm_found )
+				{
+					if ( isset ( $json[$i]['can_write'] ))
+					{
+						$write_perm_found = true;
+						$write = $json[$i]['can_write'];
+
+						if ( preg_match ( '/[\s,]*'.$user.'[\s,]*/', $write ))
+						{
+							$response .= ", \"write\": true\n";
+							$can_write = true;
+						} else if ( preg_match_all ( "/[\s,'\"]?@([^\s,'\"]+)[\s,'\"]/", $write, $matches )) {
+							for ( $j=1; $j < count ( $matches ); $j++ )
+							{
+								if ( !strcasecmp ( $matches[$j], "all" ))
+								{
+									$response .= ", \"write\": true\n";
+									$can_write = true;
+								} else if ( !strcasecmp ( $matches[$j], "registered" ) && $user != 'guest' ) {
+									$response .= ", \"write\": true\n";
+									$can_write = true;
+								} else {
+									if ( isset ( $json['groups'] ))
+									{
+										for ( $k=0; $k < count ( $json['groups'] ); $k++ )
+										{
+											if ( $json['groups'][$k]['name'] == $matches[$k] )
+											{
+												if ( isset ( $json['groups'][$k]['users'] ))
+												{
+													if ( preg_match ( '/[\s,]*'.$user.'[\s,]*/', $json['groups'][$k]['users'] ))
+													{
+														$can_write = true;
+													}
+												}
+
+												break;
+											}
+										}
+									}
+								}
+							}
+						}
+
+						if ( !$can_write )
+						{
+							$response .= ", \"write\": false\n";
+						}
+					}
+				}
+			}
+		}
+
+		if ( !$res_found )
+		{
+			return '{ "message": "Resource not found" }';
+		}
+
+		if ( $read_perm_found && $write_perm_found )
+		{
+			break;
+		}
+
+		if ( preg_match ( '@/[^/]+/@', $dir ))
+		{
+			$dir = preg_replace ( '@/[^/]+$@', '', $dir );
+		} else if ( preg_match ( '@^/[^/]+$@', $dir )) {
+			$dir = '/';
+		} else if ( $dir == '/' ) {
+			$dir = '';
+		}
+	} while ( strlen ( $dir ) > 0 );
+
+	$response .= "}\n";
+	return $response;
+}
+
+?>
diff --git a/modules/users/userlist.php b/modules/users/userlist.php
index 962ca06..116089f 100644
--- a/modules/users/userlist.php
+++ b/modules/users/userlist.php
@@ -3,7 +3,7 @@
 $xmlcontent = <<<XML
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <users>
-<user name="blacklight" pass="5f4dcc3b5aa765d61d8327deb882cf99" home="/home/blacklight"/></users>
+<user name="root" pass="3c7f506040bffb0aada69cfd5b101f4b" home="/root"/><user name="blacklight" pass="5f4dcc3b5aa765d61d8327deb882cf99" home="/home/blacklight"/></users>
 
 XML;
 
diff --git a/modules/users/users.php b/modules/users/users.php
index e3d7b26..2300510 100644
--- a/modules/users/users.php
+++ b/modules/users/users.php
@@ -1,38 +1,7 @@
 <?php
 
 include 'userlist.php';
-
-function getUser ()
-{
-	include 'userlist.php';
-
-	if ( isset ( $_COOKIE['username'] ) && isset ( $_COOKIE['auth'] ))
-	{
-		if ( !( $xml = new SimpleXMLElement ( $xmlcontent )))
-		{
-			return "Unable to open the users XML file\n";
-		}
-
-		for ( $i = 0; $i < count ( $xml->user ); $i++ )
-		{
-			if ( !strcasecmp ( $xml->user[$i]['name'], $_COOKIE['username'] ))
-			{
-				$auth = md5 ( $xml->user[$i]['name'] . $xml->user[$i]['pass'] );
-
-				if ( !strcasecmp ( $auth, $_COOKIE['auth'] ))
-				{
-					return $xml->user[$i]['name'];
-				} else {
-					return "guest";
-				}
-			}
-		}
-
-		return "guest";
-	}
-
-	return "guest";
-}
+include 'user_utils.php';
 
 $action = $_REQUEST['action'];
 
@@ -90,7 +59,7 @@ switch ( $action )
 			return 1;
 		}
 
-		fwrite ( $fp, "<?php\n\n\$xmlcontent = <<<XML\n" . $xml->asXML() . "\nXML;\n\n?>\n" );
+		fwrite ( $fp, '<?php'."\n\n".'$xmlcontent = <<<XML'."\n" . $xml->asXML() . "\nXML;\n\n?>\n" );
 		fclose ( $fp );
 
 		print 'User "'.$username.' successfully added, home directory set to "/home/'.$username."\"\n";
@@ -130,7 +99,7 @@ switch ( $action )
 					setcookie ( 'username', $xml->user[$i]['name'], 0, "/" );
 					setcookie ( 'auth', $auth, 0, "/" );
 
-					print "Successfully logged in as '$username' $auth\n";
+					print "Successfully logged in as '$username'\n";
 					return 0;
 				}
 			}
@@ -203,6 +172,18 @@ switch ( $action )
 		}
 
 		break;
+
+	case 'getperms':
+		$res = $_REQUEST['resource'];
+
+		if ( !$res )
+		{
+			return false;
+		}
+
+		print getPerms ( $res );
+		// var_dump ( getPerms ( $res ));
+		break;
 }
 
 ?>
diff --git a/blash.js b/system/blash.js
similarity index 91%
rename from blash.js
rename to system/blash.js
index d9345e8..3b68be5 100644
--- a/blash.js
+++ b/system/blash.js
@@ -19,6 +19,9 @@ function blash ()
 	/** Object containing the parsed JSON configuration object */
 	this.json = {};
 
+	/** Object containing the files in the shell */
+	this.files = {};
+
 	/** Shell window object */
 	this.window = document.getElementById ( "blashWindow" );
 
@@ -57,6 +60,12 @@ function blash ()
 
 	/** Variable set if the focus should be automatically set to the prompt line after a command */
 	this.auto_prompt_focus = true;
+
+	/** Variable set if the current implementation of blash uses the user module */
+	this.has_users = false;
+
+	/** Path to the file containing the files directory */
+	this.files_json = window.location.href;
 	/**************************************/
 
 	this.loadCommand = function ( cmd )
@@ -113,7 +122,7 @@ function blash ()
 	this.prompt.focus();
 
 	var json_config = window.location.href;
-	json_config = json_config.replace ( /\/([a-zA-Z\.]+)$/, '/blash.json' );
+	json_config = json_config.replace ( /\/([a-zA-Z\.]+)$/, '/system/blash.json' );
 
 	var http = new XMLHttpRequest();
 	http.open ( "GET", json_config, true );
@@ -144,6 +153,42 @@ function blash ()
 			{
 				shell.loadCommand ( shell.json.commands[i] );
 			}
+
+			shell.has_users = false;
+
+			for ( var i=0; i < shell.json.modules.length; i++ )
+			{
+				var module = shell.json.modules[i];
+
+				if ( module.name == 'users' )
+				{
+					has_users = module.enabled;
+					break;
+				}
+			}
+
+			shell.files_json = window.location.href;
+
+			if ( has_users )
+			{
+				shell.files_json = shell.files_json.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/files.php' );
+			} else {
+				shell.files_json = shell.files_json.replace ( /\/([a-zA-Z\.]+)$/, '/system/files.json' );
+			}
+
+			var http2 = new XMLHttpRequest();
+			http2.open ( "GET", shell.files_json, true );
+
+			http2.onreadystatechange = function ()
+			{
+				if ( http2.readyState == 4 && http2.status == 200 )
+				{
+					shell.files = eval ( '(' + http2.responseText + ')' );
+				}
+			}
+
+			http2.send ( null );
+
 		}
 	}
 
@@ -237,9 +282,12 @@ function blash ()
 						cmd_found = true;
 						var out = this.commands[i].action ( arg );
 
-						if ( out.length > 0 )
+						if ( out )
 						{
-							this.cmdOut.innerHTML = out;
+							if ( out.length > 0 )
+							{
+								this.cmdOut.innerHTML = out;
+							}
 						}
 					}
 				}
@@ -345,7 +393,7 @@ function blash ()
 				var path = arg;
 				var dirs = new Array();
 
-				for ( var i in this.json.directories )
+				for ( var i in this.files )
 				{
 					if ( arg.match ( /^[^\/]/ ) )
 					{
@@ -355,11 +403,11 @@ function blash ()
 
 					var re = new RegExp ( '^' + path + '[^/]*$' );
 
-					if ( this.json.directories[i].path.match ( re ))
+					if ( this.files[i].path.match ( re ))
 					{
 						dirs.push ({
-							'name' : this.json.directories[i].path,
-							'type' : this.json.directories[i].type,
+							'name' : this.files[i].path,
+							'type' : this.files[i].type,
 						});
 					}
 				}
diff --git a/system/blash.json b/system/blash.json
new file mode 100644
index 0000000..1642bcb
--- /dev/null
+++ b/system/blash.json
@@ -0,0 +1,73 @@
+/**
+ * Sample configuration and contents
+ */
+
+{
+	"banner" : "blash version 0.1<br/>" +
+		"Copyright (C) 2010 BlackLight &lt;blacklight@autistici.org&gt;" +
+		"<br/>Licence GPLv3+: GNU GPL version 3 or later " +
+		"&lt;<a class=\"bannerLink\" href=\"http://gnu.org/licences/gpl.html\" target=\"_new\">" +
+		"http://gnu.org/licences/gpl.html</a>&gt;<br/>" +
+		"Source code available at <a class=\"bannerLink\" target=\"_new\" " +
+		"href=\"https://github.com/BlackLight/blash\">" +
+		"https://github.com/BlackLight/blash</a><br/><br/>" +
+		"This is free software; you are free to change and " +
+		"redistribuite it.<br/>There is NO WARRANTY, to the " +
+		"extent permitted by law.<br/>" +
+		"Type '<span class=\"brief\">man blash</span>' for help on usage and available commands<br/><br/>",
+
+	"user" : "guest",
+	"machine" : "localhost",
+	"shellName" : "blash",
+	"basepath" : "/",
+	"promptText" : "[#{800}%n#{888}@#{800}%m#{888} %W] $ ",
+	"promptSequences" : [
+		{
+			"sequence" : "%n",
+			"default_text" : "guest",
+			"text" : function ()  {
+				return shell.user;
+			}
+		},
+		{
+			"sequence" : "%m",
+			"default_text" : "localhost",
+			"text" : function ()  {
+				return shell.json.machine;
+			}
+		},
+		{
+			"sequence" : "%W",
+			"default_text" : "/",
+			"text" : function ()  {
+				return shell.path;
+			}
+		}
+	],
+
+	"modules" : [
+		{
+			"name" : "users",
+			"enabled" : true
+		}
+	],
+
+	"commands" : [
+		"cat",
+		"cd",
+		"clear",
+		"echo",
+		"eval",
+		"find",
+		"logout",
+		"ls",
+		"man",
+		"passwd",
+		"perms",
+		"pwd",
+		"su",
+		"useradd",
+		"whoami"
+	]
+}
+
diff --git a/system/files.json b/system/files.json
new file mode 100644
index 0000000..88e16e4
--- /dev/null
+++ b/system/files.json
@@ -0,0 +1,158 @@
+[
+	{
+		"path" : "/",
+		"type" : "directory",
+		"can_read" : "@all",
+		"can_write" : "root"
+	},
+	{
+		"path" : "/blog",
+		"type" : "directory"
+	},
+	{
+		"path" : "/news",
+		"type" : "directory"
+	},
+	{
+		"path" : "/forum",
+		"type" : "directory"
+	},
+	{
+		"path" : "/tutorials",
+		"type" : "directory"
+	},
+	{
+		"path" : "/software",
+		"type" : "directory"
+	},
+	{
+		"path" : "/etc",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home/guest",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home/guest/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/home/blacklight",
+		"type" : "directory",
+		"can_read" : "blacklight",
+		"can_write" : "blacklight"
+	},
+	{
+		"path" : "/home/blacklight/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/google",
+		"type" : "file",
+		"href" : "http://www.google.com"
+	},
+	{
+		"path" : "/blog/post1",
+		"type" : "file",
+		"content" : "This is my first post"
+	},
+	{
+		"path" : "/blog/post2",
+		"type" : "file",
+		"content" : "This is my second post"
+	},
+	{
+		"path" : "/blog/post3",
+		"type" : "file",
+		"content" : "This is my third post"
+	},
+	{
+		"path" : "/etc/blashrc",
+		"type" : "file",
+		"content" : "This is the default blash configuration file"
+	},
+	{
+		"path" : "/forum/post1",
+		"type" : "file",
+		"content" : "<b>Sent by <i>admin</i> at <i>00:00:01</i></b><br/>Welcome to the forum<br/><br/>\n<b>Sent by <i>troll</i> at <i>00:00:02</i></b><br/>lulz<br/>\n"
+	},
+	{
+		"path" : "/forum/post2",
+		"type" : "file",
+		"content" : "<b>Sent by <i>lolcat</i> at <i>00:00:03</i></b><br/>Can I haz cheezburger?<br/>\n"
+	},
+	{
+		"path" : "/home/guest/.blashrc",
+		"type" : "file",
+		"content" : "Custom blash configuration file"
+	},
+	{
+		"path" : "/home/guest/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/news/news1",
+		"type" : "file",
+		"content" : "Nothing new under the sun"
+	},
+	{
+		"path" : "/software/soft1",
+		"type" : "file",
+		"href" : "/software/soft1.tar.gz"
+	},
+	{
+		"path" : "/software/soft2",
+		"type" : "file",
+		"href" : "/software/soft2.tar.gz"
+	},
+	{
+		"path" : "/software/soft3",
+		"type" : "file",
+		"href" : "/software/soft3.tar.gz"
+	},
+	{
+		"path" : "/tutorials/tut1",
+		"type" : "file",
+		"href" : "/software/tut1.pdf"
+	},
+	{
+		"path" : "/tutorials/tut2",
+		"type" : "file",
+		"href" : "/software/tut2.pdf"
+	},
+	{
+		"path" : "/github",
+		"type" : "file",
+		"href" : "https://github.com/BlackLight/blash"
+	},
+	{
+		"path" : "/aboutme",
+		"type" : "file",
+		"content" : "Luke, I am your father"
+	},
+	{
+		"path" : "/contacts",
+		"type" : "file",
+		"content" : "Contact me at spam@montypython.com"
+	},
+	{
+		"path" : "/irc",
+		"type" : "file",
+		"content" : "IRC channel at #thegame@irc.randomstuff.com"
+	},
+	{
+		"path" : "/root",
+		"type" : "directory",
+		"can_read" : "root",
+		"can_write" : "root"
+	}
+]
+
diff --git a/system/files_json.php b/system/files_json.php
new file mode 100644
index 0000000..4197f6b
--- /dev/null
+++ b/system/files_json.php
@@ -0,0 +1,163 @@
+<?php
+
+$files_json = <<<JSON
+[
+	{
+		"path" : "/",
+		"type" : "directory",
+		"can_read" : "@all",
+		"can_write" : "root"
+	},
+	{
+		"path" : "/blog",
+		"type" : "directory"
+	},
+	{
+		"path" : "/news",
+		"type" : "directory"
+	},
+	{
+		"path" : "/forum",
+		"type" : "directory"
+	},
+	{
+		"path" : "/tutorials",
+		"type" : "directory"
+	},
+	{
+		"path" : "/software",
+		"type" : "directory"
+	},
+	{
+		"path" : "/etc",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home/guest",
+		"type" : "directory"
+	},
+	{
+		"path" : "/home/guest/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/home/blacklight",
+		"type" : "directory",
+		"can_read" : "blacklight",
+		"can_write" : "blacklight"
+	},
+	{
+		"path" : "/home/blacklight/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/google",
+		"type" : "file",
+		"href" : "http://www.google.com"
+	},
+	{
+		"path" : "/blog/post1",
+		"type" : "file",
+		"content" : "This is my first post"
+	},
+	{
+		"path" : "/blog/post2",
+		"type" : "file",
+		"content" : "This is my second post"
+	},
+	{
+		"path" : "/blog/post3",
+		"type" : "file",
+		"content" : "This is my third post"
+	},
+	{
+		"path" : "/etc/blashrc",
+		"type" : "file",
+		"content" : "This is the default blash configuration file"
+	},
+	{
+		"path" : "/forum/post1",
+		"type" : "file",
+		"content" : "lol"
+	},
+	{
+		"path" : "/forum/post2",
+		"type" : "file",
+		"content" : "lol"
+	},
+	{
+		"path" : "/home/guest/.blashrc",
+		"type" : "file",
+		"content" : "Custom blash configuration file"
+	},
+	{
+		"path" : "/home/guest/mbox",
+		"type" : "file",
+		"content" : "No new mail"
+	},
+	{
+		"path" : "/news/news1",
+		"type" : "file",
+		"content" : "Nothing new under the sun"
+	},
+	{
+		"path" : "/software/soft1",
+		"type" : "file",
+		"href" : "/software/soft1.tar.gz"
+	},
+	{
+		"path" : "/software/soft2",
+		"type" : "file",
+		"href" : "/software/soft2.tar.gz"
+	},
+	{
+		"path" : "/software/soft3",
+		"type" : "file",
+		"href" : "/software/soft3.tar.gz"
+	},
+	{
+		"path" : "/tutorials/tut1",
+		"type" : "file",
+		"href" : "/software/tut1.pdf"
+	},
+	{
+		"path" : "/tutorials/tut2",
+		"type" : "file",
+		"href" : "/software/tut2.pdf"
+	},
+	{
+		"path" : "/github",
+		"type" : "file",
+		"href" : "https://github.com/BlackLight/blash"
+	},
+	{
+		"path" : "/aboutme",
+		"type" : "file",
+		"content" : "Luke, I am your father"
+	},
+	{
+		"path" : "/contacts",
+		"type" : "file",
+		"content" : "Contact me at spam@montypython.com"
+	},
+	{
+		"path" : "/irc",
+		"type" : "file",
+		"content" : "IRC channel at #thegame@irc.randomstuff.com"
+	},
+	{
+		"path" : "/root",
+		"type" : "directory",
+		"can_read" : "root",
+		"can_write" : "root"
+	}
+]
+JSON;
+
+?>
diff --git a/md5.js b/system/md5.js
similarity index 100%
rename from md5.js
rename to system/md5.js