diff --git a/blash.css b/blash.css
index 11bb6dd..1100a9f 100644
--- a/blash.css
+++ b/blash.css
@@ -82,3 +82,50 @@ span.match
font-weight : bold;
}
+div.editor_container
+{
+ width : 100%;
+ height : 100%;
+ overflow : hidden;
+}
+
+textarea.editor_window
+{
+ border : 0px;
+ width : 100%;
+ height : 93%;
+ background-color : black;
+ color : #888;
+ font-family : Terminus, courier, monospace, fixed;
+ font-size : 13px;
+}
+
+.editor_status
+{
+ width : 100%;
+ background-color : black;
+ color : #888;
+ font-family : Terminus, courier, monospace, fixed;
+ font-size : 13px;
+ margin-bottom : 0;
+}
+
+.editor_head
+{
+ width : 100%;
+ background-color : #222;
+ color : #888;
+ font-family : Terminus, courier, monospace, fixed;
+ font-size : 13px;
+ margin-bottom : 0;
+}
+
+input.editor_status_input
+{
+ background-color : black;
+ color : #888;
+ font-family : Terminus, courier, monospace, fixed;
+ font-size : 13px;
+ border : 0;
+}
+
diff --git a/commands/cd.json b/commands/cd.json
index 4dedda8..5a85489 100644
--- a/commands/cd.json
+++ b/commands/cd.json
@@ -10,33 +10,66 @@
{
var out = '';
- if ( !arg || arg.length == 0 )
+ if ( !arg || arg.length == 0 || arg == '~' )
{
- return "Parameter expected
\n";
- }
+ shell.auto_prompt_focus = false;
+ 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=gethome';
- var found = false;
- arg = shell.expandPath ( arg );
+ 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" );
- for ( var i=0; i < shell.files.length && !found; i++ )
- {
- if ( shell.files[i].path == arg )
+ http.onreadystatechange = function ()
{
- found = true;
-
- if ( shell.files[i].type != 'directory' )
+ if ( http.readyState == 4 && http.status == 200 )
{
- return "cd: not a directory: " + arg + "
\n";
+ if ( http.responseText.length > 0 )
+ {
+ shell.home = http.responseText;
+ shell.path = shell.home;
+ } else {
+ shell.user = shell.json.user;
+ }
+
+ shell.cmdOut.innerHTML = '';
+ shell.auto_prompt_focus = true;
+ shell.auto_prompt_refresh = true;
+ shell.refreshPrompt ( false, false );
}
}
+
+ http.send ( params );
+ } else {
+ var found = false;
+ arg = shell.expandPath ( arg );
+
+ for ( var i=0; i < shell.files.length && !found; i++ )
+ {
+ if ( shell.files[i].path == arg )
+ {
+ found = true;
+
+ if ( shell.files[i].type != 'directory' )
+ {
+ return "cd: not a directory: " + arg + "
\n";
+ }
+ }
+ }
+
+ if ( !found )
+ {
+ return "cd: No such file or directory: " + arg + "
\n";
+ }
+
+ shell.path = arg;
}
- if ( !found )
- {
- return "cd: no such file or directory: " + arg + "
\n";
- }
-
- shell.path = arg;
return out;
},
}
diff --git a/commands/nano.json b/commands/nano.json
new file mode 100644
index 0000000..cf01b75
--- /dev/null
+++ b/commands/nano.json
@@ -0,0 +1,373 @@
+{
+ "name" : "nano",
+
+ "info" : {
+ "syntax" : "nano <file name>",
+ "brief" : "Edit the content of a new or existing file",
+ },
+
+ "action" : function ( arg )
+ {
+ var file = null;
+ var parent_dir = null;
+ var newfile = false;
+
+ if ( !arg || arg.length == 0 )
+ {
+ return "nano: Parameter expected
\n";
+ }
+
+ arg = shell.expandPath ( arg );
+
+ if (!( parent_dir = shell.getParentDirectory ( arg )))
+ {
+ return "nano: Parent directory not found
\n";
+ }
+
+ if ( !( file = shell.getFile ( arg )))
+ {
+ newfile = true;
+ }
+
+ if ( !newfile )
+ {
+ if ( file['type'] == 'directory' )
+ {
+ arg = arg.replace ( //g, '>' );
+ return "nano: Cannot edit '" + arg + "': Is a directory
\n";
+ }
+ }
+
+ var users_php = window.location.href;
+ users_php = users_php.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/users.php' );
+ params = 'action=getperms&resource=' +
+ ( newfile ? escape ( parent_dir['path'] ) : escape ( arg ));
+
+ 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 )
+ {
+ if ( http.responseText.length > 0 )
+ {
+ shell.perms = eval ( '(' + http.responseText + ')' );
+ }
+ }
+ }
+
+ http.send ( params );
+
+ fname = arg.replace ( //g, '>' );
+
+ shell.fname = arg;
+ shell.editorkeypressed = this.editorkeypressed;
+ shell.confirmkey = this.confirmkey;
+ shell.bufferSave = this.bufferSave;
+
+ shell.default_editor_status = "[^X Exit] [^O WriteOut] [^W Where Is]";
+
+ if ( shell.perms )
+ {
+ if ( shell.perms.write == false )
+ {
+ shell.default_editor_status += ' [read-only]';
+ }
+ }
+
+ shell.default_editor_head = "
" +
+ "blash nano | File: " + fname + " |
";
+
+ shell.auto_prompt_focus = false;
+ shell.auto_prompt_refresh = false;
+ shell.window.removeChild ( shell.prompt );
+ shell.window.removeChild ( shell.cmdOut );
+ shell.window.innerHTML = '';
+
+ var container = document.createElement ( 'div' );
+ container.setAttribute ( 'class', 'editor_container' );
+ container.setAttribute ( 'id', 'editor_container' );
+ shell.window.appendChild ( container );
+ shell.editor_container = container;
+
+ var head = document.createElement ( 'span' );
+ head.setAttribute ( 'class', 'editor_head' );
+ head.setAttribute ( 'id', 'editor_head' );
+ head.innerHTML = shell.default_editor_head;
+ container.appendChild ( head );
+ shell.editor_head = document.getElementById ( 'editor_head' );
+
+ var editor = document.createElement ( 'textarea' );
+ editor.setAttribute ( 'class', 'editor_window' );
+ editor.setAttribute ( 'id', 'editor_window' );
+ editor.setAttribute ( 'onkeypress', 'shell.editorkeypressed ( event )' );
+ editor.innerHTML = '';
+
+ has_content = false;
+
+ if ( file )
+ {
+ if ( file.content )
+ {
+ if ( file.content.length > 0 )
+ {
+ var content = file.content.replace ( /
/g, "\n" );
+ content = content.replace ( '<', '<' );
+ content = content.replace ( '>', '>' );
+ editor.value = content;
+ has_content = true;
+ }
+ }
+ }
+
+ if ( !has_content )
+ {
+ editor.value = '';
+ shell.originalContent = '';
+ } else {
+ shell.originalContent = file.content;
+ }
+
+ container.appendChild ( editor );
+ editor.focus();
+ shell.file_changed = false;
+ shell.editor_window = document.getElementById ( 'editor_window' );
+
+ var status = document.createElement ( 'span' );
+ status.setAttribute ( 'class', 'editor_status' );
+ status.setAttribute ( 'id', 'editor_status' );
+ status.innerHTML = shell.default_editor_status;
+
+ container.appendChild ( status );
+ shell.editor_status = document.getElementById ( 'editor_status' );
+
+ return '';
+ },
+
+ "editorkeypressed" : function ( e )
+ {
+ var evt = ( window.event ) ? window.event : e;
+ var key = ( evt.charCode ) ? evt.charCode : evt.keyCode;
+ key = String.fromCharCode ( key );
+
+ if ( !shell.file_changed )
+ {
+ if ( shell.originalContent != shell.editor_window.value )
+ {
+ if ( shell.perms )
+ {
+ if ( shell.perms.write == false )
+ {
+ shell.default_editor_status += ' [read-only]';
+ shell.editor_status.innerHTML += ' [read-only]';
+ }
+ }
+
+ shell.editor_status.innerHTML += ' [modified]';
+ shell.file_changed = true;
+ }
+ }
+
+ if (( key == 'x' || key == 'X' ) && evt.ctrlKey )
+ {
+ evt.preventDefault();
+
+ if ( shell.file_changed )
+ {
+ var can_write = false;
+
+ if ( shell.perms )
+ {
+ if ( shell.perms.write == true )
+ {
+ can_write = true;
+ }
+ }
+
+ if ( can_write )
+ {
+ shell.editor_status.innerHTML = 'Save modified buffer (ANSWERING "No" WILL DESTROY CHANGES) ? ' +
+ '(Yes/No/Cancel) ';
+ } else {
+ shell.editor_status.innerHTML = 'You modified a read-only file. If you exit, the changes will ' +
+ 'be lost. Are you sure you cant to exit? (Yes/No) ';
+ }
+
+ shell.editor_status.innerHTML += '';
+
+ document.getElementById ( 'editor_status_input' ).focus();
+ } else {
+ shell.auto_prompt_focus = true;
+ shell.auto_prompt_refresh = true;
+ shell.refreshPrompt ( true );
+ }
+ } else if (( key == 'o' || key == 'O' ) && evt.ctrlKey ) {
+ evt.preventDefault();
+
+ if ( shell.file_changed )
+ {
+ var can_write = false;
+
+ if ( shell.perms )
+ {
+ if ( shell.perms.write == true )
+ {
+ can_write = true;
+ }
+ }
+
+ if ( can_write )
+ {
+ shell.bufferSave();
+ shell.file_changed = false;
+ shell.editor_status.innerHTML = shell.default_editor_status;
+ }
+ }
+ }
+ },
+
+ "confirmkey" : function ( e )
+ {
+ var can_write = false;
+ var evt = ( window.event ) ? window.event : e;
+ var key = ( evt.charCode ) ? evt.charCode : evt.keyCode;
+ key = String.fromCharCode ( key );
+
+ if ( shell.perms )
+ {
+ if ( shell.perms.write == true )
+ {
+ can_write = true;
+ }
+ }
+
+ if ( can_write )
+ {
+ switch ( key )
+ {
+ case 'c':
+ case 'C':
+ evt.preventDefault();
+ shell.editor_status.innerHTML = shell.default_editor_status + ' [modified]';
+ shell.editor_window.focus();
+ break;
+
+ case 'y':
+ case 'Y':
+ shell.bufferSave();
+
+ case 'n':
+ case 'N':
+ evt.preventDefault();
+ shell.auto_prompt_focus = true;
+ shell.auto_prompt_refresh = true;
+ shell.refreshPrompt ( true );
+ break;
+
+ default :
+ evt.preventDefault();
+ document.getElementById ( 'editor_status_input' ).value = '';
+ break;
+ }
+ } else {
+ switch ( key )
+ {
+ case 'n':
+ case 'N':
+ evt.preventDefault();
+ shell.editor_status.innerHTML = shell.default_editor_status + ' [modified]';
+ shell.editor_window.focus();
+ break;
+
+ case 'y':
+ case 'Y':
+ evt.preventDefault();
+ shell.auto_prompt_focus = true;
+ shell.auto_prompt_refresh = true;
+ shell.refreshPrompt ( true );
+ break;
+
+ default :
+ evt.preventDefault();
+ document.getElementById ( 'editor_status_input' ).value = '';
+ break;
+ }
+ }
+ },
+
+ "bufferSave" : function ()
+ {
+ var users_php = window.location.href;
+ users_php = users_php.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/users.php' );
+ params = 'action=set_content&file=' + escape ( shell.fname ) + '&content=' + escape ( document.getElementById ( 'editor_window' ).value );
+
+ 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 )
+ {
+ if ( http.responseText.length > 0 )
+ {
+ shell.editor_status.innerHTML = http.responseText;
+ setTimeout ( 'shell.editor_status.innerHTML = shell.default_editor_status', 5000 );
+
+ 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 );
+ }
+ }
+ }
+
+ http.send ( params );
+ shell.originalContent = shell.editor_window.value;
+ shell.file_changed = false;
+ }
+}
+
diff --git a/modules/users/user_utils.php b/modules/users/user_utils.php
index 9fef7ea..e088ec3 100644
--- a/modules/users/user_utils.php
+++ b/modules/users/user_utils.php
@@ -305,6 +305,94 @@ function __json_encode( $data ) {
return $json;
}
+function set_content ( $file, $content )
+{
+ $perms = json_decode ( getPerms ( $file ), true );
+ $can_write = true;
+
+ if ( !$perms['write'] )
+ {
+ $can_write = false;
+
+ if ( $perms['message'] )
+ {
+ if ( !strcasecmp ( $perms['message'], "Resource not found" ))
+ {
+ $parent = preg_replace ( "@/[^/]+$@", '', $file );
+ $perms = json_decode ( getPerms ( $parent ), true );
+
+ if ( !$perms['write'] )
+ {
+ if ( $perms['message'] )
+ {
+ if ( !strcasecmp ( $perms['message'], "Resource not found" ))
+ {
+ return "Cannot save the file: Parent directory not found";
+ } else {
+ return $perms['message'];
+ }
+ } else {
+ return "Cannot write to the file: Permission denied";
+ }
+ } else {
+ $can_write = true;
+ }
+ } else {
+ return $perms['message'];
+ }
+ } else {
+ return "Cannot write to the file: Permission denied";
+ }
+ }
+
+ $resp = __touch ( $file, null );
+ 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 ); $i++ )
+ {
+ $path = $json[$i]['path'];
+
+ if ( !$path || strlen ( $path ) == 0 )
+ {
+ continue;
+ }
+
+ if ( $path == $file )
+ {
+ $content = str_replace ( '<', '<', $content );
+ $content = str_replace ( '>', '>', $content );
+ $content = str_replace ( "\'", "'", $content );
+ $content = str_replace ( '"', "'", $content );
+ $content = str_replace ( '\\', '', $content );
+ $content = str_replace ( "\r", '', $content );
+ $content = str_replace ( "\n", '
', $content );
+
+ $json[$i]['content'] = $content;
+
+ if ( !( $fp = fopen ( "../../system/files_json.php", "w" )))
+ {
+ return "Unable to write on directories file\n";
+ }
+
+ fwrite ( $fp, "");
+ fclose ( $fp );
+ return "File successfully saved";
+ }
+ }
+}
+
function __touch ( $file, $own_perms )
{
include "../../system/files_json.php";
diff --git a/modules/users/users.php b/modules/users/users.php
index 0e0c464..be6bcee 100644
--- a/modules/users/users.php
+++ b/modules/users/users.php
@@ -241,6 +241,18 @@ switch ( $action )
print __rm ( $file );
break;
+ case 'set_content':
+ $file = $_REQUEST['file'];
+ $content = $_REQUEST['content'];
+
+ if ( !( $file && $content ))
+ {
+ return false;
+ }
+
+ print set_content ( $file, $content );
+ break;
+
default :
print "Unallowed action\n";
break;
diff --git a/system/blash.js b/system/blash.js
index c5fdfda..c4158c9 100644
--- a/system/blash.js
+++ b/system/blash.js
@@ -250,6 +250,8 @@ function blash ()
if ( key == 68 && evt.ctrlKey )
{
+ evt.preventDefault();
+
/* CTRL-d -> logout */
for ( i=0; i < this.commands.length; i++ )
{
@@ -312,6 +314,7 @@ function blash ()
return false;
} else if ( key == 76 && evt.ctrlKey ) {
// CTRL-l clears the screen
+ evt.preventDefault();
this.refreshPrompt ( true, false );
return false;
} else if ( key == 13 || key == 10 || ( key == 67 && evt.ctrlKey )) {
@@ -435,6 +438,7 @@ function blash ()
this.prompt.focus();
} else if ( key == 9 ) {
this.prompt.focus();
+ evt.preventDefault();
if ( this.prompt.value.match ( /\s(.*)$/ ))
{
@@ -613,13 +617,22 @@ function blash ()
*/
this.refreshPrompt = function ( clearTerm, clearOut )
{
- var value = this.prompt.value;
- var out = this.cmdOut.innerHTML;
+ var value = "";
+ var out = "";
var text = ( this.json.promptText ) ? this.json.promptText : "[%n@%m %W] $ ";
text = this.unescapePrompt ( text, this.json.promptSequences );
- this.window.removeChild ( this.prompt );
- this.window.removeChild ( this.cmdOut );
+ if (( this.prompt = document.getElementById ( "promptText" )))
+ {
+ value = this.prompt.innerHTML;
+ this.window.removeChild ( this.prompt );
+ }
+
+ if (( this.cmdOut = document.getElementById ( "blashCmdOut" )))
+ {
+ out = this.cmdOut.innerHTML;
+ this.window.removeChild ( this.cmdOut );
+ }
if ( clearTerm )
{
@@ -630,7 +643,7 @@ function blash ()
{
var outDiv = document.createElement ( 'span' );
outDiv.innerHTML = ((value.length > 0) ? value : '') +
- '
' + ((out.length > 0) ? (out + '
') : '') + text;
+ '
' + ((out.length > 0) ? (out + '
') : '') + text + (( shell.__first_cmd ) ? '
' : '' );
this.window.appendChild ( outDiv );
}
@@ -737,5 +750,70 @@ function blash ()
return matches;
}
+
+ /**
+ * \brief Check if a file or directory exists
+ * \return The object reference to the file or directory if it exists, false otherwise
+ */
+ this.getFile = function ( arg )
+ {
+ if ( !arg || arg.length == 0 )
+ {
+ return false;
+ }
+
+ arg = this.expandPath ( arg );
+
+ for ( var i=0; i < this.files.length; i++ )
+ {
+ if ( this.files[i].path == arg )
+ {
+ return this.files[i];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * \brief Check if the parent directory of a file exists
+ * \return The object reference to the parent directory, if it exists and it is actually a directory, false otherwise
+ */
+ this.getParentDirectory = function ( arg )
+ {
+ if ( !arg || arg.length == 0 )
+ {
+ return false;
+ }
+
+ arg = this.expandPath ( arg );
+
+ if ( arg.match ( /(\/[^\/]+)$/ ))
+ {
+ arg = arg.replace ( RegExp.$1, '' );
+ } else {
+ arg = '/';
+ }
+
+ if ( arg.length == 0 )
+ {
+ arg = '/';
+ }
+
+ for ( var i=0; i < this.files.length; i++ )
+ {
+ if ( this.files[i].path == arg )
+ {
+ if ( this.files[i].type == 'directory' )
+ {
+ return this.files[i];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
}
diff --git a/system/blash.json b/system/blash.json
index 0178c4c..8bcda31 100644
--- a/system/blash.json
+++ b/system/blash.json
@@ -64,6 +64,7 @@
"ls",
"man",
"mkdir",
+ "nano",
"passwd",
"pwd",
"rm",
diff --git a/system/files_json.php b/system/files_json.php
index b7b4026..2f5cadd 100644
--- a/system/files_json.php
+++ b/system/files_json.php
@@ -32,6 +32,7 @@ $files_json = <<lol lol asd 'lol' <asd> 'asd'"}
]
JSON;