/***************************************************************** * * * blash - An AJAX CMS for browsing your web site like a shell * * * * by BlackLight , (C) 2010 * * Web: http://0x00.ath.cx * * Released under GPL licence v.3 * * * *****************************************************************/ var shell = null; function blash () { /************ ATTRIBUTES **************/ /** Current user */ this.user = ''; /** 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" ); /** Array containing the codes of the shell commands */ this.commands = new Array(); /** Escape sequences to be parsed in the prompt text */ this.promptSequences = new Array(); /** Array containing the history of given commands */ this.history = new Array(); /** Index to the current history element */ this.history_index = -1; /** Current path */ this.path = '/'; /** Element containing the output of the command */ this.cmdOut = document.getElementById ( "blashCmdOut" ); /** Element containing the text of the prompt */ this.promptText = document.getElementById ( "promptText" ); /** Input field used as prompt */ this.prompt = document.getElementsByName ( "blashPrompt" )[0]; /** Counter of the open tags when replacing the colours in the command prompt */ this.__open_spans = 0; /** Check if this is the first command given in this session (for fixing
stuff) */ this.__first_cmd = true; /** Variable set if the prompt is re-generated automatically after a command was given */ this.auto_prompt_refresh = true; /** 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 ) { var cmd_file = window.location.href; cmd_file = cmd_file.replace ( /\/([a-zA-Z\.]+)$/, '/commands/' + cmd + ".json" ); var http = new XMLHttpRequest(); http.open ( "GET", cmd_file, true ); http.onreadystatechange = function () { if ( http.readyState == 4 && http.status == 200 ) { shell.commands.push ( eval ( '(' + http.responseText + ')' )); } } http.send ( null ); } if ( document.cookie ) { if ( document.cookie.match ( 'auth=' ) && document.cookie.match ( 'username=([^;]+);?' )) { this.user = RegExp.$1; var params = 'action=getuser'; var users_php = window.location.href; users_php = users_php.replace ( /\/([a-zA-Z\.]+)$/, '/modules/users/users.php' ); var xml = new XMLHttpRequest(); xml.open ( "POST", users_php, true ); xml.setRequestHeader ( "Content-type", "application/x-www-form-urlencoded" ); xml.setRequestHeader ( "Content-length", params.length ); xml.setRequestHeader ( "Connection", "close" ); xml.onreadystatechange = function () { if ( xml.readyState == 4 && xml.status == 200 ) { if ( xml.responseText.length > 0 ) { shell.user = xml.responseText; } else { shell.user = shell.json.user; } } } xml.send ( params ); } } this.prompt.focus(); var json_config = window.location.href; json_config = json_config.replace ( /\/([a-zA-Z\.]+)$/, '/system/blash.json' ); var http = new XMLHttpRequest(); http.open ( "GET", json_config, true ); http.onreadystatechange = function () { if ( http.readyState == 4 && http.status == 200 ) { shell.json = eval ( '(' + http.responseText + ')' ); if ( shell.user == '' ) { shell.user = shell.json.user; } shell.promptText.innerHTML = ( shell.json.promptText ) ? shell.json.promptText : "[%n@%m %W] $ "; shell.promptText.innerHTML = shell.unescapePrompt ( promptText.innerHTML, shell.json.promptSequences ); if ( shell.json.banner.length > 0 ) { var banner = document.createElement ( "span" ); banner.setAttribute ( "id", "banner" ); banner.innerHTML = shell.json.banner; shell.window.insertBefore ( banner, shell.promptText ); } for ( var i in shell.json.commands ) { 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' ) { shell.has_users = module.enabled; break; } } shell.files_json = window.location.href; if ( shell.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 ); } } http.send ( null ); this.getKey = function ( e ) { var evt = ( window.event ) ? window.event : e; var key = ( evt.charCode ) ? evt.charCode : evt.keyCode; if ( key == 68 && evt.ctrlKey ) { /* CTRL-d -> logout */ for ( i=0; i < this.commands.length; i++ ) { if ( this.commands[i].name == 'logout' ) { var out = this.commands[i].action (); if ( this.auto_prompt_refresh ) { var value = this.prompt.value; var out = this.cmdOut.innerHTML; var text = ( shell.json.promptText ) ? shell.json.promptText : "[%n@%m %W] $ "; text = shell.unescapePrompt ( text, shell.json.promptSequences ); this.window.removeChild ( this.prompt ); this.window.removeChild ( this.cmdOut ); if ( this.__first_cmd && this.prompt.value.length > 0 ) { this.window.innerHTML += value + '
' + out + text; this.__first_cmd = false; } else { if ( out ) { if ( out.match ( /^\s*\s*/ )) { out = ''; } } this.window.innerHTML += value + '
' + out + text; } this.prompt = document.createElement ( 'input' ); this.prompt.setAttribute ( 'name', 'blashPrompt' ); this.prompt.setAttribute ( 'type', 'text' ); this.prompt.setAttribute ( 'class', 'promptInput' ); this.prompt.setAttribute ( 'autocomplete', 'off' ); this.prompt.setAttribute ( 'onkeydown', 'shell.getKey ( event )' ); this.prompt.setAttribute ( 'onkeyup', 'this.focus()' ); this.prompt.setAttribute ( 'onblur', 'return false' ); this.cmdOut = document.createElement ( 'div' ); this.cmdOut.setAttribute ( 'id', 'blashCmdOut' ); this.cmdOut.setAttribute ( 'class', 'blashCmdOut' ); this.cmdOut.innerHTML = '
'; this.window.appendChild ( this.prompt ); this.window.appendChild ( this.cmdOut ); if ( this.auto_prompt_focus ) { this.prompt.focus(); } } } } return false; } else if ( key == 76 && evt.ctrlKey ) { // CTRL-l clears the screen this.refreshPrompt ( true ); return false; } else if ( key == 13 || key == 10 || ( key == 67 && evt.ctrlKey )) { if ( this.prompt.value.length != 0 && ( key != 67 || !evt.ctrlKey )) { this.prompt.value.match ( /^([^\s]+)\s*(.*)$/ ); var cmd = RegExp.$1; var arg = RegExp.$2; var cmd_found = false; this.history.push ( this.prompt.value ); this.history_index = -1; for ( i=0; i < this.commands.length && !cmd_found; i++ ) { if ( this.commands[i].name == cmd ) { cmd_found = true; var out = this.commands[i].action ( arg ); if ( out ) { if ( out.length > 0 ) { this.cmdOut.innerHTML = out; } } } } if ( !cmd_found ) { this.cmdOut.innerHTML = this.json.shellName + ": command not found: " + cmd + '
'; } } if ( this.auto_prompt_refresh ) { var value = this.prompt.value; var out = this.cmdOut.innerHTML; var text = ( shell.json.promptText ) ? shell.json.promptText : "[%n@%m %W] $ "; text = shell.unescapePrompt ( text, shell.json.promptSequences ); this.window.removeChild ( this.prompt ); this.window.removeChild ( this.cmdOut ); if ( this.__first_cmd && this.prompt.value.length > 0 ) { this.window.innerHTML += value + '
' + out + text; this.__first_cmd = false; } else { if ( out ) { if ( out.match ( /^\s*\s*/ )) { out = ''; } } this.window.innerHTML += value + '
' + out + text; } this.prompt = document.createElement ( 'input' ); this.prompt.setAttribute ( 'name', 'blashPrompt' ); this.prompt.setAttribute ( 'type', 'text' ); this.prompt.setAttribute ( 'class', 'promptInput' ); this.prompt.setAttribute ( 'autocomplete', 'off' ); this.prompt.setAttribute ( 'onkeydown', 'shell.getKey ( event )' ); this.prompt.setAttribute ( 'onkeyup', 'this.focus()' ); this.prompt.setAttribute ( 'onblur', 'return false' ); this.cmdOut = document.createElement ( 'div' ); this.cmdOut.setAttribute ( 'id', 'blashCmdOut' ); this.cmdOut.setAttribute ( 'class', 'blashCmdOut' ); this.cmdOut.innerHTML = '
'; this.window.appendChild ( this.prompt ); this.window.appendChild ( this.cmdOut ); if ( this.auto_prompt_focus ) { this.prompt.focus(); } } if ( key == 67 && evt.ctrlKey ) { return false; } } else if (( key == 38 || key == 40 ) && this.history.length > 0 ) { if ( key == 38 ) { if ( this.history_index < 0 ) { this.history_index = this.history.length - 1; this.prompt.value = this.history[ this.history_index ]; } else if ( this.history_index == 0 ) { // We're already on the first history element } else { this.history_index--; this.prompt.value = this.history[ this.history_index ]; } } else if ( key == 40 ) { if ( this.history_index < 0 ) { // We're already on the last element, don't do anything } else if ( this.history_index == this.history.length - 1 ) { this.prompt.value = ''; } else { this.history_index++; this.prompt.value = this.history[ this.history_index ]; } } // Put the cursor at the end if ( this.prompt.setSelectionRange ) { this.prompt.setSelectionRange ( this.prompt.value.length, this.prompt.value.length ); } this.prompt.focus(); } else if ( key == 9 ) { this.prompt.focus(); if ( this.prompt.value.match ( /\s(.*)$/ )) { var arg = RegExp.$1; var path = arg; var dirs = new Array(); for ( var i in this.files ) { if ( arg.match ( /^[^\/]/ ) ) { path = this.path + '/' + arg; path = path.replace ( /\/+/g, '/' ); } var re = new RegExp ( '^' + path + '[^/]*$' ); if ( this.files[i].path.match ( re )) { dirs.push ({ 'name' : this.files[i].path, 'type' : this.files[i].type, }); } } if ( dirs.length == 1 ) { this.prompt.value = this.prompt.value.replace ( arg, dirs[0].name + (( dirs[0].type == 'directory' ) ? '/' : '' )); } else { this.cmdOut.innerHTML = ''; for ( var i in dirs ) { if ( i > 0 ) { this.cmdOut.innerHTML += "
\n"; } this.cmdOut.innerHTML += dirs[i].name; } if ( dirs.length > 1 ) { // Get the longest sequence in common var sequences = new Array(); var min_len = 0; for ( var i in dirs ) { for ( var j in dirs ) { if ( i != j ) { if ( dirs[i].name.length != dirs[j].name.length ) { min_len = ( dirs[i].name.length < dirs[j].name.length ) ? dirs[i].name.length : dirs[j].name.length; } else { min_len = dirs[i].name.length; } var k = 0; for ( k = min_len-1; k >= 0; k-- ) { if ( dirs[i].name.charAt ( k ) != dirs[j].name.charAt ( k )) { break; } } var seq = ''; for ( var l=0; l < k; l++ ) { seq += dirs[i].name.charAt ( l ); } sequences.push ( seq ); } } } var seq = sequences[0]; for ( var i in sequences ) { if ( sequences[i].length < seq ) { seq = sequences[i]; } } this.prompt.value = this.prompt.value.replace ( arg, seq + (( dirs[0].type == 'directory' ) ? '/' : '' )); } } } else { var cmds = new Array(); for ( var i in this.commands ) { var re = new RegExp ( '^' + this.prompt.value ); if ( this.commands[i].name.match ( re )) { cmds.push ( this.commands[i].name ); } } if ( cmds.length == 0 ) { this.cmdOut.innerHTML = '
Sorry, no matches for `' + this.prompt.value + "'"; } else if ( cmds.length == 1 ) { this.prompt.value = cmds[0] + ' '; } else { this.cmdOut.innerHTML = ''; for ( var i in cmds ) { this.cmdOut.innerHTML += "
\n" + cmds[i]; } } } if ( this.auto_prompt_focus ) { this.prompt.focus(); setTimeout ( function() { shell.prompt.focus(); }, 1 ); if ( this.prompt.setSelectionRange ) { this.prompt.setSelectionRange ( this.prompt.value.length, this.prompt.value.length ); } } return false; } if ( this.auto_prompt_focus ) { this.prompt.focus(); } } this.unescapePrompt = function ( prompt, sequences ) { var re = new RegExp ( "([^\]?)#\{([0-9]+)\}" ); while ( prompt.match ( re )) { if ( this.__open_spans > 0 ) { prompt = prompt.replace ( re, RegExp.$1 + "
" ); } else { prompt = prompt.replace ( re, RegExp.$1 + "" ); this.__open_spans++; } } if ( this.__open_spans > 0 ) { prompt = prompt + ""; } for ( i=0; i < sequences.length; i++ ) { prompt = this.unescapePromptSequence ( prompt, sequences[i].sequence, sequences[i].text(), sequences[i].default_text ); } return prompt; } /** * \brief Refresh the shell prompt * \param clearTerm Set this variable as true if you want also to clear the terminal screen * \param clearOut Set this variable as true if you want to clear the latest output command */ this.refreshPrompt = function ( clearTerm, clearOut ) { var value = this.prompt.value; var out = this.cmdOut.innerHTML; 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 ( clearTerm ) { this.window.innerHTML = ''; } if ( !clearOut ) { if ( out.length > 0 ) { var outDiv = document.createElement ( 'span' ); outDiv.innerHTML = '
' + out + '
' + text; this.window.appendChild ( outDiv ); } } this.prompt = document.createElement ( 'input' ); this.prompt.setAttribute ( 'name', 'blashPrompt' ); this.prompt.setAttribute ( 'type', 'text' ); this.prompt.setAttribute ( 'class', 'promptInput' ); this.prompt.setAttribute ( 'autocomplete', 'off' ); this.prompt.setAttribute ( 'onkeydown', 'shell.getKey ( event )' ); this.prompt.setAttribute ( 'onkeyup', 'this.focus()' ); this.prompt.setAttribute ( 'onblur', 'return false' ); this.cmdOut = document.createElement ( 'div' ); this.cmdOut.setAttribute ( 'id', 'blashCmdOut' ); this.cmdOut.setAttribute ( 'class', 'blashCmdOut' ); this.window.appendChild ( this.prompt ); this.window.appendChild ( this.cmdOut ); this.prompt.focus(); } this.unescapePromptSequence = function ( prompt, sequence, text, default_text ) { var re = new RegExp ( "([^\]?)" + sequence, "g" ); prompt.replace ( /%W/g, this.path ); if ( prompt.match ( re )) { prompt = prompt.replace ( re, (( text ) ? RegExp.$1 + text : RegExp.$1 + default_text )); } return prompt; } this.expandPath = function ( arg ) { if ( arg.match ( /^[^\/]/ )) { arg = this.path + '/' + arg; } arg = arg.replace ( /\/+/, '/' ); if ( arg != '/' ) { arg = arg.replace ( /\/*$/, '' ); } while ( arg.match ( /^(.+?\/?\.\.)/ )) { var part = RegExp.$1; if ( arg.match ( /^(.+?)\/?\.\./ )) { if ( RegExp.$1 == '/' ) { arg = arg.replace ( part, '/' ); } else { part.match ( /^(.*)\/[^\/]*\/\.\..*$/ ); var sup = RegExp.$1; arg = arg.replace ( part, sup ); if ( arg.length == 0 ) { arg = '/'; } } } } return arg; } }