Tab completion has been added to the web console

git-svn-id: file:///home/svn/framework3/trunk@4290 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2007-01-20 22:19:32 +00:00
parent 951a91d32c
commit 8d06aad5b4
6 changed files with 175 additions and 67 deletions

View File

@ -20,11 +20,71 @@ class ConsoleController < ApplicationController
@console = $msfweb.consoles[@cid]
if(params[:cmd])
out = ''
if (params[:cmd].strip.length > 0)
@console.write(params[:cmd] + "\n")
out = @console.execute(params[:cmd])
end
send_data(@console.read(), :type => "application/octet-stream")
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
pro = @console.prompt.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
script = "// Metasploit Web Console Data\n"
script += "var con_prompt = unescape('#{pro}');\n"
script += "var con_update = unescape('#{out}');\n"
send_data(script, :type => "text/javascript")
end
if(params[:tab])
opts = []
cmdl = params[:tab]
out = ""
if (params[:tab].strip.length > 0)
opts = @console.tab_complete(params[:tab]) || []
end
if (opts.length == 1)
cmdl = opts[0]
else
if (opts.length == 0)
# aint got nothin
else
cmd_top = opts[0]
depth = 0
while (depth < cmd_top.length)
match = true
opts.each do |line|
next if line[depth] == cmd_top[depth]
match = false
break
end
break if not match
depth += 1
end
if (depth > 0)
cmdl = cmd_top[0, depth]
end
out = "\n" + opts.map{ |c| " >> " + c }.join("\n")
end
end
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
pro = @console.prompt.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
tln = cmdl.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
script = "// Metasploit Web Console Data\n"
script += "var con_prompt = unescape('#{pro}');\n"
script += "var con_update = unescape('#{out}');\n"
script += "var con_tabbed = unescape('#{tln}');\n"
send_data(script, :type => "text/javascript")
end
end
end

View File

@ -22,19 +22,29 @@
</div>
</pre>
<textarea
rows="1"
id="console_prompt"
class="prompt"
disabled="true"
></textarea>
<textarea
<table class="console_command_bar" border=0 padding=4 cellspacing=0 width='100%'>
<tr>
<td
nowrap='true'
valign='top'
id="console_prompt"
>
<%=h @console.prompt %>
</td>
<td nowrap='true' width='100%'>
<textarea
id="console_input"
class="input"
wrap="off"
onkeydown="return console_keydown(event)"
onkeydown="return console_keydown(event)"
onkeypress="return console_keypress(event)"
rows="1"
></textarea>
</td>
</tr>
</table>
</div>
</body>

View File

@ -10,6 +10,12 @@ var console_input; // Object to console input
var console_output; // Object to console output
var console_prompt; // Object to console prompt
// Placeholders
var con_prompt = "";
var con_update = "";
var con_tabbed = "";
function console_refocus() {
console_input.blur();
console_input.focus();
@ -25,46 +31,36 @@ function console_printline(s, type) {
}
}
var prompt = 'msf> ';
var console_commands = {
print : function print(s) {
console_printline(s, "info");
},
clear: function clear() {
var child_preserve = 3;
while (console_output.childNodes[child_preserve])
console_output.removeChild(console_output.childNodes[child_preserve]);
}
}
function console_tabcomplete() {
// TODO: get console_input.value, send to process_cmd with mode=tabcomplete
// retrieve array of possible matches
// put them to output container
// done.
}
function console_execute() {
if (console_commands[console_input.value]) {
f = console_commands[console_input.value];
alert(f);
}
}
function console_update_output(req) {
console_printline(req.responseText);
console_input.focus();
try { eval(req.responseText); } catch(e){ alert(req.responseText); }
window.status = "";
console_printline(con_update);
console_prompt.innerHTML = con_prompt;
console_refocus();
}
function console_keydown(e) {
function console_update_tabs(req) {
try { eval(req.responseText); } catch(e){ console_output.innerHTML = req.responseText; }
window.status = "";
console_printline(con_update);
console_prompt.innerHTML = con_prompt;
console_input.value = con_tabbed;
console_refocus();
}
if (e.keyCode == 8) {
window.title = console_input.value;
}
if (e.keyCode == 13) { // enter
function console_keypress(e) {
if (e.keyCode == 13) { // enter
console_history.push(console_input.value);
try { console_execute(); } catch(er) { alert(er); };
console_printline("\n" + con_prompt + ' ' + console_input.value)
window.status = "Executing command, please wait..."
new Ajax.Updater("console_update", document.location, {
asynchronous:true,
@ -72,19 +68,31 @@ function console_keydown(e) {
parameters:"cmd=" + escape(console_input.value),
onComplete:console_update_output
});
console_input.value = "";
console_input.focus();
console_prompt = prompt;
return false;
}
} else if (e.keyCode == 38) { // up
}
function console_keydown(e) {
if (e.keyCode == 38) { // up
// TODO: place upper cmd in history on console_input.value
alert('UP');
} else if (e.keyCode == 40) { // down
// TODO: place lower cmd in history on console_input.value
alert('DOWN');
} else if (e.keyCode == 9) { // tab
console_tabcomplete();
setTimeout(function() { console_refocus(); }, 0);
window.status = "Finding possible commands..."
new Ajax.Updater("console_update", document.location, {
asynchronous:true,
evalScripts:true,
parameters:"tab=" + escape(console_input.value),
onComplete:console_update_tabs
});
}
}
@ -95,8 +103,8 @@ function console_init() {
console_output = document.getElementById("console_output");
console_prompt = document.getElementById("console_prompt");
console_prompt.value = prompt;
console_input.focus();
console_refocus();
return true;
}

View File

@ -15,7 +15,6 @@ html,body {
}
#console_window {
width: 100%;
margin: 1em;
background: #000000;
padding: 1em;
@ -37,22 +36,19 @@ html,body {
}
.input {
border: none;
font: inherit;
font-weight: bold;
color: #fff;
padding: 0;
margin-top: 1em;
background: #000000;
border: 0;
color: white;
width: 400px;
}
.prompt {
width: 3em;
margin-top: 1em;
border: none;
padding: 0;
background: #000000;
color: white;
font-weight: bold;
text-align: right;
}
.console_command_bar {
background: #000000;
}

View File

@ -21,7 +21,7 @@ class WebConsole
attr_accessor :console_id
attr_accessor :last_access
attr_accessor :framework
attr_accessor :thread
class WebConsolePipe < Rex::IO::BidirectionalPipe
@ -29,7 +29,7 @@ class WebConsole
attr_accessor :output
attr_accessor :prompt
attr_accessor :killed
def eof?
self.pipe_input.eof?
end
@ -67,7 +67,7 @@ class WebConsole
# Initialize the console with our pipe
self.console = Msf::Ui::Console::Driver.new(
'msf>',
'msf',
'>',
{
'Framework' => self.framework,
@ -76,7 +76,7 @@ class WebConsole
}
)
Thread.new { self.console.run }
self.thread = Thread.new { self.console.run }
update_access()
end
@ -95,9 +95,24 @@ class WebConsole
self.pipe.write_input(buf)
end
def execute(cmd)
self.console.run_single(cmd)
self.read
end
def prompt
$stderr.puts(self.pipe.prompt)
self.pipe.prompt
end
def tab_complete(cmd)
self.console.tab_complete(cmd)
end
def shutdown
self.pipe.killed = true
self.pipe.close
self.thread.kill
end
end

View File

@ -49,6 +49,25 @@ class BidirectionalPipe < Rex::Ui::Text::Input
buf.print(msg)
}
end
def print_error(msg)
print_line('[-] ' + msg)
end
def print_line(msg)
print(msg + "\n")
end
def print_good(msg)
print_line('[+] ' + msg)
end
def flush
end
def print_status(msg)
print_line('[*] ' + msg)
end
protected