From b1b6206baa14c569d1ac5ff67f035e324a33935d Mon Sep 17 00:00:00 2001
From: BlackLight <blacklight@autistici.org>
Date: Tue, 11 Jan 2011 12:18:45 +0100
Subject: [PATCH] cp and mv commands implemented

---
 commands/chmod.json          |   6 +-
 commands/cp.json             |  73 ++++++++++++++++++++++++
 commands/ln.json             |  45 +--------------
 commands/logout.json         |  39 +------------
 commands/mkdir.json          |  47 +---------------
 commands/mv.json             |  73 ++++++++++++++++++++++++
 commands/nano.json           |  40 +------------
 commands/passwd.json         |   8 +--
 commands/rm.json             |  47 +---------------
 commands/rmdir.json          |  47 +---------------
 commands/su.json             |  40 +------------
 commands/touch.json          |  47 +---------------
 commands/useradd.json        |   8 +--
 commands/userdel.json        |   6 +-
 commands/users.json          |   6 +-
 modules/users/user_utils.php | 105 +++++++++++++++++++++++++++++++++++
 modules/users/users.php      |  19 +++++++
 system/blash.js              |  49 ++++++++++++++--
 system/blash.json            |   2 +
 system/files_json.php        |   2 +-
 20 files changed, 336 insertions(+), 373 deletions(-)
 create mode 100644 commands/cp.json
 create mode 100644 commands/mv.json

diff --git a/commands/chmod.json b/commands/chmod.json
index 265563c..d6ba0cc 100644
--- a/commands/chmod.json
+++ b/commands/chmod.json
@@ -48,11 +48,7 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
 				shell.auto_prompt_refresh = true;
 				shell.refreshPrompt ( false, false );
 			}
diff --git a/commands/cp.json b/commands/cp.json
new file mode 100644
index 0000000..d05b87d
--- /dev/null
+++ b/commands/cp.json
@@ -0,0 +1,73 @@
+{
+	"name" : "cp",
+
+	"info" :  {
+		"syntax" : "cp &lt;source file&gt; &lt;destination file&gt;",
+		"brief" : "Copy a file to another",
+	},
+
+	"action" : function ( arg )
+	{
+		var src = null;
+		var dest = null;
+
+		if ( !arg || arg.length == 0 )
+		{
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( arg.match ( /^\s*('|")([^'|"]+)('|")/ ))
+		{
+			src = RegExp.$2;
+			arg = arg.replace ( new RegExp ( '^\s*' + RegExp.$1 + RegExp.$2 + RegExp.$3 + '\s*' ), '' );
+		} else if ( arg.match ( /^\s*([^\s]+)/ )) {
+			src = RegExp.$1;
+			arg = arg.replace ( new RegExp ( '^\s*' + RegExp.$1 + '\s*' ), '' );
+		} else {
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( !src || arg.length == 0 )
+		{
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( arg.match ( /^\s*('|")([^'|"]+)('|")/ ))
+		{
+			dest = RegExp.$2;
+		} else {
+			arg.match ( /^\s*(.*)$/ );
+			dest = RegExp.$1;
+		}
+
+		src = shell.expandPath ( src );
+		dest = shell.expandPath ( dest );
+
+		shell.auto_prompt_refresh = false;
+
+		var users_php = window.location.href;
+		users_php = users_php.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/users.php' );
+		params = 'action=cp&src=' + escape ( src ) + '&dest=' + escape ( dest );
+
+		var http = new XMLHttpRequest();
+		http.open ( "POST", users_php, true );
+		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 )
+			{
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
+				shell.refreshPrompt ( false, false );
+				shell.auto_prompt_refresh = true;
+			}
+		}
+
+		http.send ( params );
+		shell.cmdOut.innerHTML = '';
+	}
+}
+
diff --git a/commands/ln.json b/commands/ln.json
index 09fbf34..a6e3445 100644
--- a/commands/ln.json
+++ b/commands/ln.json
@@ -65,49 +65,8 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				}
-
-				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 + ')' );
-
-						// Remove duplicates
-						var tmp = new Array();
-
-						for ( var i in shell.files )
-						{
-							var contains = false;
-
-							for ( var j=0; j < tmp.length && !contains; j++ )
-							{
-								if ( shell.files[i].path == tmp[j].path )
-								{
-									contains = true;
-								}
-							}
-
-							if ( !contains )
-							{
-								tmp.push ( shell.files[i] );
-							}
-						}
-
-						shell.files = tmp;
-					}
-				}
-
-				http2.send ( null );
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
 				shell.auto_prompt_refresh = true;
 				shell.refreshPrompt ( false, false );
 			}
diff --git a/commands/logout.json b/commands/logout.json
index 5eb8054..a3902f3 100644
--- a/commands/logout.json
+++ b/commands/logout.json
@@ -38,44 +38,7 @@
 		{
 			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 + ')' );
-
-						// Remove duplicates
-						var tmp = new Array();
-
-						for ( var i in shell.files )
-						{
-							var contains = false;
-
-							for ( var j=0; j < tmp.length && !contains; j++ )
-							{
-								if ( shell.files[i].path == tmp[j].path )
-								{
-									contains = true;
-								}
-							}
-
-							if ( !contains )
-							{
-								tmp.push ( shell.files[i] );
-							}
-						}
-
-						shell.files = tmp;
-					}
-				}
-
-				http2.send ( null );
+				shell.refreshFiles();
 			}
 		}
 
diff --git a/commands/mkdir.json b/commands/mkdir.json
index 938038e..6dc7844 100644
--- a/commands/mkdir.json
+++ b/commands/mkdir.json
@@ -31,51 +31,8 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				} else {
-					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 + ')' );
-
-							// Remove duplicates
-							var tmp = new Array();
-
-							for ( var i in shell.files )
-							{
-								var contains = false;
-
-								for ( var j=0; j < tmp.length && !contains; j++ )
-								{
-									if ( shell.files[i].path == tmp[j].path )
-									{
-										contains = true;
-									}
-								}
-
-								if ( !contains )
-								{
-									tmp.push ( shell.files[i] );
-								}
-							}
-
-							shell.files = tmp;
-						}
-					}
-
-					http2.send ( null );
-					shell.cmdOut.innerHTML = '';
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
 				shell.refreshPrompt ( false, false );
 				shell.auto_prompt_focus = true;
 				shell.auto_prompt_refresh = true;
diff --git a/commands/mv.json b/commands/mv.json
new file mode 100644
index 0000000..8c54934
--- /dev/null
+++ b/commands/mv.json
@@ -0,0 +1,73 @@
+{
+	"name" : "mv",
+
+	"info" :  {
+		"syntax" : "mv &lt;source file&gt; &lt;destination file&gt;",
+		"brief" : "Move a file to another",
+	},
+
+	"action" : function ( arg )
+	{
+		var src = null;
+		var dest = null;
+
+		if ( !arg || arg.length == 0 )
+		{
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( arg.match ( /^\s*('|")([^'|"]+)('|")/ ))
+		{
+			src = RegExp.$2;
+			arg = arg.replace ( new RegExp ( '^\s*' + RegExp.$1 + RegExp.$2 + RegExp.$3 + '\s*' ), '' );
+		} else if ( arg.match ( /^\s*([^\s]+)/ )) {
+			src = RegExp.$1;
+			arg = arg.replace ( new RegExp ( '^\s*' + RegExp.$1 + '\s*' ), '' );
+		} else {
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( !src || arg.length == 0 )
+		{
+			return "Usage: " + this.info.syntax + "<br/>\n";
+		}
+
+		if ( arg.match ( /^\s*('|")([^'|"]+)('|")/ ))
+		{
+			dest = RegExp.$2;
+		} else {
+			arg.match ( /^\s*(.*)$/ );
+			dest = RegExp.$1;
+		}
+
+		src = shell.expandPath ( src );
+		dest = shell.expandPath ( dest );
+
+		shell.auto_prompt_refresh = false;
+
+		var users_php = window.location.href;
+		users_php = users_php.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/users.php' );
+		params = 'action=mv&src=' + escape ( src ) + '&dest=' + escape ( dest );
+
+		var http = new XMLHttpRequest();
+		http.open ( "POST", users_php, true );
+		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 )
+			{
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
+				shell.refreshPrompt ( false, false );
+				shell.auto_prompt_refresh = true;
+			}
+		}
+
+		http.send ( params );
+		shell.cmdOut.innerHTML = '';
+	}
+}
+
diff --git a/commands/nano.json b/commands/nano.json
index 3c32612..83eda06 100644
--- a/commands/nano.json
+++ b/commands/nano.json
@@ -388,46 +388,8 @@
 				if ( http.responseText.length > 0 )
 				{
 					shell.editor_status.innerHTML = http.responseText;
+					shell.refreshFiles();
 					setTimeout ( 'shell.editor_status.innerHTML = shell.default_editor_status', 1500 );
-
-					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 + ')' );
-
-							// Remove duplicates
-							var tmp = new Array();
-
-							for ( var i in shell.files )
-							{
-								var contains = false;
-
-								for ( var j=0; j < tmp.length && !contains; j++ )
-								{
-									if ( shell.files[i].path == tmp[j].path )
-									{
-										contains = true;
-									}
-								}
-
-								if ( !contains )
-								{
-									tmp.push ( shell.files[i] );
-								}
-							}
-
-							shell.files = tmp;
-						}
-					}
-
-					http2.send ( null );
 				}
 			}
 		}
diff --git a/commands/passwd.json b/commands/passwd.json
index 842e7b8..d584e31 100644
--- a/commands/passwd.json
+++ b/commands/passwd.json
@@ -72,13 +72,7 @@
 				{
 					if ( http.readyState == 4 && http.status == 200 )
 					{
-						if ( http.responseText.length > 0 )
-						{
-							shell.cmdOut.innerHTML = http.responseText;
-						} else {
-							shell.cmdOut.innerHTML = '';
-						}
-
+						shell.cmdOut.innerHTML = http.responseText;
 						shell.refreshPrompt ( false, false );
 					}
 				}
diff --git a/commands/rm.json b/commands/rm.json
index dc7a50d..3b1f307 100644
--- a/commands/rm.json
+++ b/commands/rm.json
@@ -31,51 +31,8 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				} else {
-					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 + ')' );
-
-							// Remove duplicates
-							var tmp = new Array();
-
-							for ( var i in shell.files )
-							{
-								var contains = false;
-
-								for ( var j=0; j < tmp.length && !contains; j++ )
-								{
-									if ( shell.files[i].path == tmp[j].path )
-									{
-										contains = true;
-									}
-								}
-
-								if ( !contains )
-								{
-									tmp.push ( shell.files[i] );
-								}
-							}
-
-							shell.files = tmp;
-						}
-					}
-
-					http2.send ( null );
-					shell.cmdOut.innerHTML = '';
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
 				shell.refreshPrompt ( false, false );
 				shell.auto_prompt_focus = true;
 				shell.auto_prompt_refresh = true;
diff --git a/commands/rmdir.json b/commands/rmdir.json
index 861bee7..0101379 100644
--- a/commands/rmdir.json
+++ b/commands/rmdir.json
@@ -31,51 +31,8 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				} else {
-					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 + ')' );
-
-							// Remove duplicates
-							var tmp = new Array();
-
-							for ( var i in shell.files )
-							{
-								var contains = false;
-
-								for ( var j=0; j < tmp.length && !contains; j++ )
-								{
-									if ( shell.files[i].path == tmp[j].path )
-									{
-										contains = true;
-									}
-								}
-
-								if ( !contains )
-								{
-									tmp.push ( shell.files[i] );
-								}
-							}
-
-							shell.files = tmp;
-						}
-					}
-
-					http2.send ( null );
-					shell.cmdOut.innerHTML = '';
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
 				shell.refreshPrompt ( false, false );
 				shell.auto_prompt_focus = true;
 				shell.auto_prompt_refresh = true;
diff --git a/commands/su.json b/commands/su.json
index ff7441c..31f9103 100644
--- a/commands/su.json
+++ b/commands/su.json
@@ -73,45 +73,7 @@
 					{
 						var user = RegExp.$1;
 						shell.user = user;
-
-						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 + ')' );
-
-								// Remove duplicates
-								var tmp = new Array();
-
-								for ( var i in shell.files )
-								{
-									var contains = false;
-
-									for ( var j=0; j < tmp.length && !contains; j++ )
-									{
-										if ( shell.files[i].path == tmp[j].path )
-										{
-											contains = true;
-										}
-									}
-
-									if ( !contains )
-									{
-										tmp.push ( shell.files[i] );
-									}
-								}
-
-								shell.files = tmp;
-							}
-						}
-
-						http2.send ( null );
+						shell.refreshFiles();
 
 						var xml2 = new XMLHttpRequest();
 						xml2.open ( "POST", users_php, true );
diff --git a/commands/touch.json b/commands/touch.json
index 1a04152..3098314 100644
--- a/commands/touch.json
+++ b/commands/touch.json
@@ -31,51 +31,8 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				} else {
-					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 + ')' );
-
-							// Remove duplicates
-							var tmp = new Array();
-
-							for ( var i in shell.files )
-							{
-								var contains = false;
-
-								for ( var j=0; j < tmp.length && !contains; j++ )
-								{
-									if ( shell.files[i].path == tmp[j].path )
-									{
-										contains = true;
-									}
-								}
-
-								if ( !contains )
-								{
-									tmp.push ( shell.files[i] );
-								}
-							}
-
-							shell.files = tmp;
-						}
-					}
-
-					http2.send ( null );
-					shell.cmdOut.innerHTML = '';
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
+				shell.refreshFiles();
 				shell.refreshPrompt ( false, false );
 				shell.auto_prompt_focus = true;
 				shell.auto_prompt_refresh = true;
diff --git a/commands/useradd.json b/commands/useradd.json
index 15c9e76..248eae6 100644
--- a/commands/useradd.json
+++ b/commands/useradd.json
@@ -59,13 +59,7 @@
 				{
 					if ( http.readyState == 4 && http.status == 200 )
 					{
-						if ( http.responseText.length > 0 )
-						{
-							shell.cmdOut.innerHTML = http.responseText;
-						} else {
-							shell.cmdOut.innerHTML = '';
-						}
-
+						shell.cmdOut.innerHTML = http.responseText;
 						shell.auto_prompt_focus = true;
 						shell.auto_prompt_refresh = true;
 						shell.refreshPrompt ( false, false );
diff --git a/commands/userdel.json b/commands/userdel.json
index 7dfc21d..91b4a71 100644
--- a/commands/userdel.json
+++ b/commands/userdel.json
@@ -29,11 +29,7 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
 				shell.auto_prompt_refresh = true;
 				shell.refreshPrompt ( false, false );
 			}
diff --git a/commands/users.json b/commands/users.json
index c8ecbef..9eb1dca 100644
--- a/commands/users.json
+++ b/commands/users.json
@@ -23,11 +23,7 @@
 		{
 			if ( http.readyState == 4 && http.status == 200 )
 			{
-				if ( http.responseText.length > 0 )
-				{
-					shell.cmdOut.innerHTML = http.responseText;
-				}
-
+				shell.cmdOut.innerHTML = http.responseText;
 				shell.auto_prompt_refresh = true;
 				shell.refreshPrompt ( false, false );
 			}
diff --git a/modules/users/user_utils.php b/modules/users/user_utils.php
index f89689f..54bc1ef 100644
--- a/modules/users/user_utils.php
+++ b/modules/users/user_utils.php
@@ -318,6 +318,111 @@ function __link ( $resource, $link, $type )
 	return "Unable to link the resource";
 }
 
+function __cp ( $src, $dest )
+{
+	include "../../system/files_json.php";
+
+	if ( !$files_json || strlen ( $files_json ) == 0 )
+	{
+		return 'Error: Empty JSON file container';
+	}
+
+	$json = json_decode ( $files_json, true );
+
+	if ( !$json )
+	{
+		return 'Error: Empty JSON file container';
+	}
+
+	$src_index  = -1;
+	$dest_index = -1;
+
+	for ( $i=0; $i < count ( $json ) && ( $src_index == -1 || $dest_index == -1 ); $i++ )
+	{
+		if ( $json[$i]['path'] == $src )
+		{
+			$src_index = $i;
+		}
+
+		if ( $json[$i]['path'] == $dest )
+		{
+			$dest_index = $i;
+		}
+	}
+
+	if ( $src_index == -1 )
+	{
+		$src = str_replace ( '<', '&lt;', $src );
+		$src = str_replace ( '>', '&gt;', $src );
+		return "cp: Cannot stat ".$src.": No such file or directory\n";
+	}
+
+	if ( $dest_index == -1 )
+	{
+		$ret =  __touch ( $dest, null );
+
+		if ( strlen ( $ret ) > 0 )
+		{
+			return $ret;
+		}
+
+		include "../../system/files_json.php";
+
+		if ( !$files_json || strlen ( $files_json ) == 0 )
+		{
+			return 'Error: Empty JSON file container';
+		}
+
+		$json = json_decode ( $files_json, true );
+
+		if ( !$json )
+		{
+			return 'Error: Empty JSON file container';
+		}
+
+		for ( $i=0; $i < count ( $json ) && $dest_index == -1; $i++ )
+		{
+			$out .= $json[$i]['path']. ", ";
+			if ( $json[$i]['path'] == $dest )
+			{
+				$dest_index = $i;
+			}
+		}
+	}
+
+	if ( $dest_index == -1 )
+	{
+		$dest = str_replace ( '<', '&lt;', $dest );
+		$dest = str_replace ( '>', '&gt;', $dest );
+		return "cp: Could not create the file $dest\n";
+	}
+
+	foreach ( array_keys ( $json[$dest_index] ) as $key )
+	{
+		if ( $key != 'path' )
+		{
+			unset ( $json[$dest_index][$key] );
+		}
+	}
+
+	foreach ( array_keys ( $json[$src_index] ) as $key )
+	{
+		if ( $key != 'path' )
+		{
+			$json[$dest_index][$key] = $json[$src_index][$key];
+		}
+	}
+
+	if ( !( $fp = fopen ( "../../system/files_json.php", "w" )))
+	{
+		return "Unable to write on directories file\n";
+	}
+
+	fwrite ( $fp, "<?php\n\n\$files_json = <<<JSON\n".__json_encode ( $json )."\nJSON;\n\n?>");
+	fclose ( $fp );
+	return false;
+}
+
 function __json_encode( $data ) {           
 	if ( is_array ($data) || is_object ($data) ) {
 		$islist = is_array ($data) && ( empty ($data) || array_keys ($data) === range (0,count($data)-1) );
diff --git a/modules/users/users.php b/modules/users/users.php
index 363992a..5fb06cf 100644
--- a/modules/users/users.php
+++ b/modules/users/users.php
@@ -394,6 +394,25 @@ switch ( $action )
 		print __link ( $resource, $link, $type );
 		break;
 
+	case 'cp':
+	case 'mv':
+		$src = $_REQUEST['src'];
+		$dest = $_REQUEST['dest'];
+
+		if ( !( $src && $dest ))
+		{
+			return false;
+		}
+
+		print __cp ( $src, $dest );
+
+		if ( $action == 'mv' )
+		{
+			print __rm ( $src );
+		}
+
+		break;
+
 	default :
 		print "Unallowed action\n";
 		break;
diff --git a/system/blash.js b/system/blash.js
index 7fcc977..85f459e 100644
--- a/system/blash.js
+++ b/system/blash.js
@@ -400,11 +400,7 @@ function blash ()
 
 			var outDiv = document.createElement ( 'span' );
 			outDiv.innerHTML = text;
-			//outDiv.innerHTML = ((value.length > 0) ? value : '') +
-			//	( value.match ( /<br\/?>\s*$/ ) ? '' : '<br/>' ) + ((out.length > 0) ? (out + '<br/>') : '') + text + (( shell.__first_cmd ) ? '<br/>' : '' );
-			//outDiv.innerHTML = outDiv.innerHTML.replace ( /<br\/?>\s*$/, '' );
 			this.window.insertBefore ( outDiv, document.getElementsByName ( "blashPrompt" )[0] );
-			// this.window.appendChild ( outDiv );
 
 			return false;
 		} else if ( key == 13 || key == 10 || ( key == 67 && evt.ctrlKey )) {
@@ -942,5 +938,50 @@ function blash ()
 
 		return false;
 	}
+
+	/**
+	 * \brief Refresh the file list for the current user
+	 */
+	this.refreshFiles = function ()
+	{
+		var files_config = window.location.href;
+		files_config = files_config.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/files.php' );
+
+		var http = new XMLHttpRequest();
+		http.open ( "GET", files_config, true );
+
+		http.onreadystatechange = function ()
+		{
+			if ( http.readyState == 4 && http.status == 200 )
+			{
+				shell.files = eval ( '(' + http.responseText + ')' );
+
+				// Remove duplicates
+				var tmp = new Array();
+
+				for ( var i in shell.files )
+				{
+					var contains = false;
+
+					for ( var j=0; j < tmp.length && !contains; j++ )
+					{
+						if ( shell.files[i].path == tmp[j].path )
+						{
+							contains = true;
+						}
+					}
+
+					if ( !contains )
+					{
+						tmp.push ( shell.files[i] );
+					}
+				}
+
+				shell.files = tmp;
+			}
+		}
+
+		http.send ( null );
+	}
 }
 
diff --git a/system/blash.json b/system/blash.json
index b420595..9008e90 100644
--- a/system/blash.json
+++ b/system/blash.json
@@ -55,6 +55,7 @@
 	"commands" : [
 		"cat",
 		"cd",
+		"cp",
 		"chmod",
 		"clear",
 		"echo",
@@ -66,6 +67,7 @@
 		"ls",
 		"man",
 		"mkdir",
+		"mv",
 		"nano",
 		"passwd",
 		"pwd",
diff --git a/system/files_json.php b/system/files_json.php
index 0e395a7..1368e18 100644
--- a/system/files_json.php
+++ b/system/files_json.php
@@ -24,11 +24,11 @@ $files_json = <<<JSON
 , {"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_write": "root", "can_read": "root"}
 , {"path": "/google", "type": "file", "owner": "root", "can_read": "@all", "can_write": "root", "href": "http://www.google.com"}
+, {"path": "/aboutme", "type": "file", "content": "Luke, I am your father"}
 ]
 
 JSON;