Moving RBeEF to trunk
git-svn-id: https://beef.googlecode.com/svn/trunk@503 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9
This commit is contained in:
70517
public/javascript/ext-all.js
Normal file
70517
public/javascript/ext-all.js
Normal file
File diff suppressed because it is too large
Load Diff
7
public/javascript/ext-base.js
Normal file
7
public/javascript/ext-base.js
Normal file
File diff suppressed because one or more lines are too long
30
public/javascript/ext-beef.js
Normal file
30
public/javascript/ext-beef.js
Normal file
@@ -0,0 +1,30 @@
|
||||
Ext.beef = function(){
|
||||
var msgCt;
|
||||
|
||||
function createBox(t, s){
|
||||
return ['<div class="msg">',
|
||||
'<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
|
||||
'<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><h3>', t, '</h3>', s, '</div></div></div>',
|
||||
'<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
|
||||
'</div>'].join('');
|
||||
}
|
||||
return {
|
||||
msg : function(title, format){
|
||||
if(!msgCt){
|
||||
msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div'}, true);
|
||||
}
|
||||
msgCt.alignTo(document, 't-t');
|
||||
var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1));
|
||||
var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true);
|
||||
m.slideIn('t').pause(1).ghost("t", {remove:true});
|
||||
},
|
||||
|
||||
init : function(){
|
||||
|
||||
var lb = Ext.get('lib-bar');
|
||||
if(lb){
|
||||
lb.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
}();
|
||||
74
public/javascript/ui/authentication.js
Normal file
74
public/javascript/ui/authentication.js
Normal file
@@ -0,0 +1,74 @@
|
||||
Ext.onReady(function() {
|
||||
|
||||
var bd = Ext.getBody();
|
||||
|
||||
// xntrik 9/7/10 this is the functionality to try and submit the form
|
||||
submitAuthForm = function() {
|
||||
login_form.getForm().submit({
|
||||
waitMsg: 'Logging in ...',
|
||||
success: function() {
|
||||
window.location.href = '/ui/panel'
|
||||
},
|
||||
failure: function() {
|
||||
Ext.MessageBox.alert('Message', 'Error with username or password')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var login_form = new Ext.FormPanel({
|
||||
|
||||
url: 'authentication/login',
|
||||
formId: 'login_form',
|
||||
labelWidth: 125,
|
||||
frame: true,
|
||||
title: 'Authentication',
|
||||
bodyStyle:'padding:5px 5px 0',
|
||||
width: 350,
|
||||
|
||||
defaults: {
|
||||
width: 175,
|
||||
inputType: 'password'
|
||||
},
|
||||
|
||||
defaultType: 'textfield',
|
||||
|
||||
items: [{
|
||||
fieldLabel: 'Username',
|
||||
name: 'username-cfrm',
|
||||
inputType: 'textfield',
|
||||
id: 'user',
|
||||
listeners: { // xntrik 9/7/10 added this listener so form submits on enter
|
||||
specialkey: function(field,e) {
|
||||
if (e.getKey() == e.ENTER) {
|
||||
submitAuthForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
},{
|
||||
fieldLabel: 'Password',
|
||||
name: 'password-cfrm',
|
||||
inputType: 'password',
|
||||
id: 'pass',
|
||||
listeners: { // xntrik 9/7/10 added this listener so form submits on enter
|
||||
specialkey: function(field,e) {
|
||||
if (e.getKey() == e.ENTER) {
|
||||
submitAuthForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
buttons: [{
|
||||
text: 'Login',
|
||||
handler: function() { // xntrik 9/7/10 removed the logic here, placed
|
||||
//in function above and added here
|
||||
submitAuthForm();
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
login_form.render('centered');
|
||||
document.getElementById('user').focus(); //xntrik 27/7/10 - this doesn't throw warnings now
|
||||
|
||||
});
|
||||
53
public/javascript/ui/panel/AboutWindow.js
Normal file
53
public/javascript/ui/panel/AboutWindow.js
Normal file
@@ -0,0 +1,53 @@
|
||||
AboutWindow = function() {
|
||||
|
||||
about = " \
|
||||
<p> \
|
||||
BeEF, the Browser Exploitation Framework is a professional security \
|
||||
tool provided for lawful research and testing purposes. It allows \
|
||||
the experienced penetration tester or system administrator additional \
|
||||
attack vectors when assessing the posture of a target. The user of \
|
||||
BeEF will control which browser will launch which module and at \
|
||||
which target.\
|
||||
<p><br> \
|
||||
BeEF hooks one or more web browsers as beachheads for the launching \
|
||||
of directed modules in real-time. Each browser is likely to be \
|
||||
within a different security context. This provides additional vectors \
|
||||
that can be exploited by security professionals. \
|
||||
<p><br> \
|
||||
<b>Authors:</b><br> \
|
||||
- Wade Alcorn (Founder, Architect)<br> \
|
||||
- Benjamin Mosse (Main developer) \
|
||||
</p> \
|
||||
";
|
||||
|
||||
var button = Ext.get('open-about-menu');
|
||||
var about_open = false;
|
||||
|
||||
button.on('click', function(){
|
||||
if(!about_open) {
|
||||
var content = new Ext.Panel({
|
||||
region: 'center',
|
||||
padding: '3 3 3 3',
|
||||
html: about
|
||||
});
|
||||
|
||||
var win = new Ext.Window({
|
||||
title: 'About BeEF',
|
||||
closable:true,
|
||||
width:600,
|
||||
height:250,
|
||||
plain:true,
|
||||
layout: 'border',
|
||||
shadow: true,
|
||||
items: [content]
|
||||
});
|
||||
|
||||
win.on('close', function() {
|
||||
about_open = false;
|
||||
});
|
||||
|
||||
win.show(this);
|
||||
about_open = true;
|
||||
}
|
||||
})
|
||||
};
|
||||
75
public/javascript/ui/panel/DataGrid.js
Normal file
75
public/javascript/ui/panel/DataGrid.js
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
DataGrid = function(url, page, base) {
|
||||
this.page = page;
|
||||
this.url = url;
|
||||
this.base = typeof(base) != 'undefined' ? base : {};
|
||||
|
||||
this.store = new Ext.data.JsonStore({
|
||||
root: 'logs',
|
||||
autoDestroy: true,
|
||||
url: this.url,
|
||||
storeId: 'myStore',
|
||||
baseParams: this.base,
|
||||
autoLoad: {params:{start:0, limit:this.page, sort:"date", dir:"DESC"}},
|
||||
idProperty: 'id',
|
||||
fields: ['id','type','event','date'],
|
||||
totalProperty: 'count',
|
||||
remoteSort: true,
|
||||
sortInfo: {field: "date", direction: "DESC"}
|
||||
});
|
||||
|
||||
this.bbar = new Ext.PagingToolbar({
|
||||
pageSize: this.page,
|
||||
store: this.store,
|
||||
displayInfo: true,
|
||||
displayMsg: 'Displaying logs {0} - {1} of {2}',
|
||||
emptyMsg: 'No logs to display'
|
||||
});
|
||||
|
||||
this.columns = [{
|
||||
id: 'log-id',
|
||||
header: 'Id',
|
||||
hidden: true,
|
||||
dataIndex: 'id',
|
||||
sortable: false
|
||||
}, {
|
||||
id: 'log-type',
|
||||
header: "Type",
|
||||
dataIndex: 'type',
|
||||
sortable: true,
|
||||
width: 60,
|
||||
renderer: function(value, metaData, record, rowIndex, colIndex, store) {
|
||||
return "<b>" + value + "</b>";
|
||||
}
|
||||
}, {
|
||||
id: 'log-event',
|
||||
header: "Event",
|
||||
dataIndex: 'event',
|
||||
sortable:true,
|
||||
width: 420,
|
||||
renderer: this.formatTitle
|
||||
}, {
|
||||
id: 'log-date',
|
||||
header: "Date",
|
||||
dataIndex: 'date',
|
||||
width: 80,
|
||||
renderer: this.formatDate,
|
||||
sortable:true
|
||||
}];
|
||||
|
||||
DataGrid.superclass.constructor.call(this, {
|
||||
region: 'center',
|
||||
id: 'topic-grid',
|
||||
loadMask: {msg:'Loading Feed...'},
|
||||
|
||||
sm: new Ext.grid.RowSelectionModel({
|
||||
singleSelect:true
|
||||
}),
|
||||
|
||||
viewConfig: {
|
||||
forceFit:true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Ext.extend(DataGrid, Ext.grid.GridPanel, {});
|
||||
20
public/javascript/ui/panel/Logout.js
Normal file
20
public/javascript/ui/panel/Logout.js
Normal file
@@ -0,0 +1,20 @@
|
||||
DoLogout = function() {
|
||||
|
||||
var button = Ext.get('do-logout-menu');
|
||||
|
||||
after_logout = function() {
|
||||
// will redirect the UA to the login
|
||||
window.location.href = '/ui/panel'
|
||||
}
|
||||
|
||||
button.on('click', function(){
|
||||
Ext.Ajax.request({
|
||||
url: '/ui/authentication/logout',
|
||||
method: 'POST',
|
||||
params: 'nonce=' + Ext.get("nonce").dom.value,
|
||||
success: after_logout,
|
||||
failure: after_logout
|
||||
});
|
||||
|
||||
})
|
||||
};
|
||||
66
public/javascript/ui/panel/MainPanel.js
Normal file
66
public/javascript/ui/panel/MainPanel.js
Normal file
@@ -0,0 +1,66 @@
|
||||
MainPanel = function(){
|
||||
this.preview = new Ext.Panel({
|
||||
id: 'preview',
|
||||
region: 'south',
|
||||
cls:'preview',
|
||||
autoScroll: true,
|
||||
listeners: PanelViewer.LinkInterceptor,
|
||||
|
||||
tbar: [{
|
||||
id:'tab',
|
||||
text: 'View in New Tab',
|
||||
iconCls: 'new-tab',
|
||||
disabled:true,
|
||||
handler : this.openTab,
|
||||
scope: this
|
||||
}],
|
||||
|
||||
clear: function(){
|
||||
this.body.update('');
|
||||
var items = this.topToolbar.items;
|
||||
items.get('tab').disable();
|
||||
items.get('win').disable();
|
||||
}
|
||||
});
|
||||
|
||||
this.grid = new DataGrid('/ui/logs/all.json',25);
|
||||
this.grid.border = false;
|
||||
this.welcome_tab = new WelcomeTab;
|
||||
|
||||
MainPanel.superclass.constructor.call(this, {
|
||||
id:'main-tabs',
|
||||
activeTab:0,
|
||||
region:'center',
|
||||
margins:'0 5 5 0',
|
||||
resizeTabs:true,
|
||||
tabWidth:150,
|
||||
minTabWidth: 120,
|
||||
enableTabScroll: true,
|
||||
plugins: new Ext.ux.TabCloseMenu(),
|
||||
items: [{
|
||||
id:'welcome-view',
|
||||
title:'Getting Started',
|
||||
layout:'border',
|
||||
hideMode:'offsets',
|
||||
closable:true,
|
||||
plain:true,
|
||||
shadow:true,
|
||||
items:[
|
||||
this.welcome_tab
|
||||
]},{
|
||||
id:'logs-view',
|
||||
layout:'border',
|
||||
title:'Logs',
|
||||
hideMode:'offsets',
|
||||
items:[
|
||||
this.grid
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Ext.extend(MainPanel, Ext.TabPanel);
|
||||
|
||||
|
||||
Ext.reg('appmainpanel', MainPanel);
|
||||
74
public/javascript/ui/panel/PanelStatusBar.js
Normal file
74
public/javascript/ui/panel/PanelStatusBar.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The Beef_StatusBar class provides the functionality of the status bar
|
||||
* at the bottom of each tab in the UI
|
||||
*
|
||||
* @param: {String} unique string for setting the status bar id.
|
||||
*
|
||||
*/
|
||||
|
||||
Beef_StatusBar = function(unique_id) {
|
||||
|
||||
var update_fail_wait = 2000; // delay before showing ready status
|
||||
var update_sent_wait = 1000; // delay before showing ready status
|
||||
|
||||
Beef_StatusBar.superclass.constructor.call(this, {
|
||||
id: 'commands-bbar-zombie-' + unique_id,
|
||||
|
||||
// defaults to use when the status is cleared:
|
||||
defaultText: 'Ready',
|
||||
defaultIconCls: 'x-status-valid',
|
||||
|
||||
// values to set initially:
|
||||
text: 'Ready',
|
||||
iconCls: 'x-status-valid',
|
||||
|
||||
// update status bar to ready
|
||||
update_ready: function(str) {
|
||||
var display_str = str || "Ready";
|
||||
this.setStatus({
|
||||
text: display_str,
|
||||
iconCls: 'x-status-valid'
|
||||
});
|
||||
},
|
||||
|
||||
// update status bar to fail
|
||||
update_fail: function(str){
|
||||
var display_str = str || "Error!";
|
||||
|
||||
this.setStatus({
|
||||
text: display_str,
|
||||
iconCls: 'x-status-error',
|
||||
clear: {
|
||||
wait: update_fail_wait,
|
||||
anim: true,
|
||||
useDefaults: true
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// update status bar to sending
|
||||
update_sending: function(str) {
|
||||
var display_str = str || "Sending...";
|
||||
this.showBusy(display_str);
|
||||
},
|
||||
|
||||
// update status bar to sent
|
||||
update_sent: function(str) {
|
||||
var display_str = str || "Sent";
|
||||
this.setStatus({
|
||||
text: display_str,
|
||||
iconCls: 'x-status-valid',
|
||||
clear: {
|
||||
wait: update_sent_wait,
|
||||
anim: true,
|
||||
useDefaults: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Ext.extend(Beef_StatusBar, Ext.ux.StatusBar, {} );
|
||||
|
||||
27
public/javascript/ui/panel/PanelViewer.js
Normal file
27
public/javascript/ui/panel/PanelViewer.js
Normal file
@@ -0,0 +1,27 @@
|
||||
PanelViewer = {};
|
||||
var mainPanel, zombiesPanel;
|
||||
|
||||
Ext.onReady(function() {
|
||||
|
||||
Ext.QuickTips.init();
|
||||
|
||||
zombiesPanel = new ZombiesPanel();
|
||||
mainPanel = new MainPanel();
|
||||
|
||||
var viewport = new Ext.Viewport({
|
||||
layout:'border',
|
||||
items:[
|
||||
new Ext.BoxComponent({
|
||||
region:'north',
|
||||
el: 'header',
|
||||
height: 32
|
||||
}),
|
||||
zombiesPanel,
|
||||
mainPanel
|
||||
]
|
||||
});
|
||||
|
||||
new DoLogout();
|
||||
new AboutWindow();
|
||||
new ZombiesMgr(zombiesPanel);
|
||||
});
|
||||
26
public/javascript/ui/panel/WelcomeTab.js
Normal file
26
public/javascript/ui/panel/WelcomeTab.js
Normal file
@@ -0,0 +1,26 @@
|
||||
WelcomeTab = function() {
|
||||
|
||||
welcome = " \
|
||||
<div style='font:13px tahoma,arial,helvetica,sans-serif'> \
|
||||
<p><img src='/ui/public/images/icons/beef.gif' /> <span style='font:bold 15px tahoma,arial,helvetica,sans-serif'>BeEF - The Browser Exploitation Framework</span></p><br /> \
|
||||
<p>Welcome to BeEF!</p><br /> \
|
||||
<p>Before being able to fully explore the framework you will have to 'hook' a browser. To begin with you can point a browser towards the demo page <a href='/demos/basic.html' target='_blank'>here</a>.</p><br /> \
|
||||
<p>After a browser is hooked into the framework they will appear in the 'Hooked Browsers' panel on the left. Hooked browsers will appear in either an online or offline state, depending on how recently they have polled the framework. To interact with a hooked browser simply click it, a new tab will appear.</p><br /> \
|
||||
<p>Each hooked browser tab has a number of sub-tabs, described below:</p> \
|
||||
<p><ul><li><span style='font:bold 13px tahoma,arial,helvetica,sans-serif'>Main:</span> Display information about the hooked browser after you've run some command modules</li> \
|
||||
<li><span style='font:bold 13px tahoma,arial,helvetica,sans-serif'>Logs:</span> Displays recent log entries related to this particular hooked browser.</li> \
|
||||
<li><span style='font:bold 13px tahoma,arial,helvetica,sans-serif'>Commands:</span> This tab is where modules can be executed against the hooked browser. This is where most of the BeEF functionality resides.</li> \
|
||||
<li><span style='font:bold 13px tahoma,arial,helvetica,sans-serif'>Requester:</span> The Requester tab is a special module that allows you to submit arbitrary HTTP requests on behalf of the hooked browser.</li></ul></p><br /> \
|
||||
<p>For more information visit: <a href='http://code.google.com/p/beef/'>http://code.google.com/p/beef/</a></p><br />\
|
||||
</div> \
|
||||
";
|
||||
|
||||
WelcomeTab.superclass.constructor.call(this, {
|
||||
region:'center',
|
||||
padding:'10 10 10 10',
|
||||
html: welcome,
|
||||
border: false
|
||||
});
|
||||
};
|
||||
|
||||
Ext.extend(WelcomeTab,Ext.Panel, {});
|
||||
31
public/javascript/ui/panel/ZombieTab.js
Normal file
31
public/javascript/ui/panel/ZombieTab.js
Normal file
@@ -0,0 +1,31 @@
|
||||
ZombieTab = function(zombie) {
|
||||
|
||||
main_tab = new ZombieTab_MainTab(zombie);
|
||||
log_tab = new ZombieTab_LogTab(zombie);
|
||||
commands_tab = new ZombieTab_Commands(zombie);
|
||||
requester_tab = new ZombieTab_Requester(zombie);
|
||||
|
||||
//-------------------------------------------
|
||||
ZombieTab.superclass.constructor.call(this, {
|
||||
id: zombie.session,
|
||||
activeTab: 0,
|
||||
loadMask: {msg:'Loading browser...'},
|
||||
title: zombie.ip,
|
||||
autoScroll: true,
|
||||
closable: true,
|
||||
viewConfig: {
|
||||
forceFit: true,
|
||||
type: 'fit'
|
||||
},
|
||||
items:[main_tab, log_tab, commands_tab, requester_tab]
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Ext.extend(ZombieTab, Ext.TabPanel, {
|
||||
listeners: {
|
||||
close: function(panel) {
|
||||
panel.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
82
public/javascript/ui/panel/ZombiesMgr.js
Normal file
82
public/javascript/ui/panel/ZombiesMgr.js
Normal file
@@ -0,0 +1,82 @@
|
||||
var ZombiesMgr = function(zombies) {
|
||||
|
||||
var selectedZombie = null;
|
||||
|
||||
var addZombie = function(zombie){
|
||||
selectedZombie = zombie;
|
||||
}
|
||||
|
||||
var delZombie = function(zombie){
|
||||
if (selectedZombie.session == zombie.session) {
|
||||
selectedZombie = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var getZombie = function(){
|
||||
return selectedZombie;
|
||||
}
|
||||
|
||||
// this is a helper class to create a zombie object from a JSON hash index
|
||||
var zombieFactory = function(index, zombie_array){
|
||||
text = zombie_array[index]["ip"]
|
||||
text = "<img src='/ui/public/images/icons/"+escape(zombie_array[index]["os_icon"])+"' style='padding-top:3px;' width='13px' height='13px'/> "+text
|
||||
|
||||
var new_zombie = {
|
||||
'id' : index,
|
||||
'ip' : zombie_array[index]["ip"],
|
||||
'session' : zombie_array[index]["session"],
|
||||
'text': text,
|
||||
'icon': '/ui/public/images/icons/'+escape(zombie_array[index]["browser_icon"]),
|
||||
'domain' : zombie_array[index]["domain"]
|
||||
};
|
||||
|
||||
return new_zombie;
|
||||
}
|
||||
|
||||
var updateZombies = function(){
|
||||
Ext.Ajax.request({
|
||||
url: '/ui/zombies/select/offline/simple.json',
|
||||
success: function(response) {
|
||||
var offline_zombies = Ext.util.JSON.decode(response.responseText);
|
||||
|
||||
zombies.compareAndRemove(offline_zombies, false);
|
||||
|
||||
for(var i in offline_zombies) {
|
||||
|
||||
var zombie = zombieFactory(i, offline_zombies);
|
||||
|
||||
zombies.addZombie(zombie, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: '/ui/zombies/select/online/simple.json',
|
||||
success: function(response){
|
||||
var online_zombies = Ext.util.JSON.decode(response.responseText);
|
||||
|
||||
zombies.compareAndRemove(online_zombies, true);
|
||||
for(var i in online_zombies) {
|
||||
|
||||
var zombie = zombieFactory(i, online_zombies);
|
||||
|
||||
zombies.addZombie(zombie, true);
|
||||
}
|
||||
|
||||
if(zombies.online_zombies.childNodes.length > 0) {
|
||||
zombies.online_zombies.expand(true);
|
||||
}
|
||||
|
||||
if(zombies.offline_zombies.childNodes.length > 0) {
|
||||
zombies.offline_zombies.expand(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ext.TaskMgr.start({
|
||||
run: updateZombies,
|
||||
interval: 8000
|
||||
});
|
||||
}
|
||||
119
public/javascript/ui/panel/ZombiesPanel.js
Normal file
119
public/javascript/ui/panel/ZombiesPanel.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* The zombie panel located on the left hand side of the interface.
|
||||
*/
|
||||
ZombiesPanel = function() {
|
||||
|
||||
ZombiesPanel.superclass.constructor.call(this, {
|
||||
id:'zombie-tree',
|
||||
region:'west',
|
||||
title:'Hooked Browsers',
|
||||
split:true,
|
||||
width: 225,
|
||||
minSize: 175,
|
||||
maxSize: 400,
|
||||
collapsible: true,
|
||||
margins:'0 0 5 5',
|
||||
cmargins:'0 5 5 5',
|
||||
rootVisible:false,
|
||||
lines:false,
|
||||
autoScroll:true,
|
||||
root: new Ext.tree.TreeNode('My Zombies'),
|
||||
collapseFirst:false
|
||||
});
|
||||
|
||||
this.online_zombies = this.root.appendChild(
|
||||
new Ext.tree.TreeNode({
|
||||
text:'Online Browsers',
|
||||
cls:'online-zombies-node',
|
||||
expanded:true
|
||||
})
|
||||
);
|
||||
|
||||
this.offline_zombies = this.root.appendChild(
|
||||
new Ext.tree.TreeNode({
|
||||
text:'Offline Browsers',
|
||||
cls:'offline-zombies-node',
|
||||
expanded:false
|
||||
})
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The Tree panel that contains the zombie list.
|
||||
*/
|
||||
Ext.extend(ZombiesPanel, Ext.tree.TreePanel, {
|
||||
|
||||
listeners: {
|
||||
click: function(node, e) {
|
||||
if(!node.leaf) return;
|
||||
|
||||
if(!mainPanel.get(node.attributes.session)) {
|
||||
mainPanel.add(new ZombieTab(node.attributes));
|
||||
}
|
||||
|
||||
mainPanel.activate(node.attributes.session);
|
||||
}
|
||||
},
|
||||
|
||||
selectZombie: function(url){
|
||||
this.getNodeById(url).select();
|
||||
},
|
||||
|
||||
addZombie : function(attrs, online){
|
||||
if(online) {
|
||||
var z_id = 'zombie-online-' + attrs.session;
|
||||
} else {
|
||||
var z_id = 'zombie-offline-' + attrs.session;
|
||||
}
|
||||
|
||||
var exists = this.getNodeById(z_id);
|
||||
|
||||
if(exists){
|
||||
return;
|
||||
}
|
||||
|
||||
Ext.apply(attrs, {
|
||||
iconCls: 'feed-icon',
|
||||
leaf:true,
|
||||
id: z_id
|
||||
});
|
||||
|
||||
var node = new Ext.tree.TreeNode(attrs);
|
||||
|
||||
if(online) {
|
||||
Ext.apply(attrs, {cls: 'zombie-online'});
|
||||
this.online_zombies.appendChild(node);
|
||||
} else {
|
||||
Ext.apply(attrs, {cls: 'zombie-offline'});
|
||||
this.offline_zombies.appendChild(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
removeAll : function(){
|
||||
this.online_zombies.removeAll();
|
||||
this.offline_zombies.removeAll();
|
||||
},
|
||||
|
||||
compareAndRemove : function(zombies, online) {
|
||||
for(var i in zombies) {
|
||||
if(online) {
|
||||
var z = 'zombie-offline-' + zombies[i].session;;
|
||||
} else {
|
||||
var z = 'zombie-online-' + zombies[i].session;;
|
||||
}
|
||||
|
||||
var exists = this.getNodeById(z);
|
||||
|
||||
if(exists) {
|
||||
if(!online) {
|
||||
this.online_zombies.removeChild(exists);
|
||||
} else {
|
||||
this.offline_zombies.removeChild(exists);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
324
public/javascript/ui/panel/common.js
Normal file
324
public/javascript/ui/panel/common.js
Normal file
@@ -0,0 +1,324 @@
|
||||
var zombie_execute_button_text = 'Execute'
|
||||
var zombie_reexecute_button_text = 'Re-execute'
|
||||
var re_execute_command_title = 'Re-execute command'
|
||||
|
||||
/**
|
||||
* Generates fields for the form used to send command modules.
|
||||
*
|
||||
* @param: {Object} the form to generate the field in.
|
||||
* @param: {String/Object} the field name or it's definition as an object.
|
||||
* @param: {String} the value that field should have.
|
||||
* @param: {Boolean} set to true if you want the field to be disabled.
|
||||
* @param: {Object} the targeted Zombie.
|
||||
* @param: {Object} the status bar.
|
||||
*/
|
||||
function genExploitFormControl(form, input, value, disabled, zombie, sb) {
|
||||
var input_name;
|
||||
var input_type = 'TextField';
|
||||
|
||||
if(typeof input == 'string') {
|
||||
input_name = input;
|
||||
var field = new Ext.form.TextField({
|
||||
id: 'form-zombie-'+zombie.session+'-field-'+input_name,
|
||||
fieldLabel: input_name,
|
||||
name: 'txt_'+input_name,
|
||||
width: 175,
|
||||
allowBlank:false
|
||||
});
|
||||
|
||||
if(value) field.setValue(value);
|
||||
|
||||
if(disabled) field.setDisabled(true);
|
||||
|
||||
form.add(field);
|
||||
}
|
||||
else if(typeof input == 'object') {
|
||||
var field = null, input_def;
|
||||
|
||||
if(typeof input[0] == 'object') input = input[0];
|
||||
|
||||
if (!input['name']) return;
|
||||
if (!input['ui_label']) input['ui_label'] = input['name'];
|
||||
if (!input['type']) input['type'] = 'textfield';
|
||||
if (!input['value']) input['value'] = '';
|
||||
|
||||
input_id = 'form-zombie-'+zombie.session+'-field-'+input['name'];
|
||||
input_def = {id: input_id, name: 'txt_'+input['name'], fieldLabel: input['ui_label'], allowBlank:false, value: input['value']};
|
||||
|
||||
switch(input['type'].toLowerCase()) {
|
||||
case 'textarea':
|
||||
field = new Ext.form.TextArea(input_def);
|
||||
break;
|
||||
case 'hidden':
|
||||
field = new Ext.form.Hidden(input_def);
|
||||
break;
|
||||
default:
|
||||
field = new Ext.form.TextField(input_def);
|
||||
break;
|
||||
}
|
||||
|
||||
for(definition in input) {
|
||||
if(input[definition] && (typeof input[definition] == 'string') && (definition != 'type')
|
||||
&& (definition != 'name')) {
|
||||
field[definition] = input[definition];
|
||||
}
|
||||
}
|
||||
|
||||
if(value) field.setValue(value);
|
||||
|
||||
if(disabled) field.setDisabled(true);
|
||||
|
||||
form.add(field);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a panel for an command module that already has been executed.
|
||||
*
|
||||
* @param: {Object} the Panel in the UI to generate the form into.
|
||||
* @param: {Integer} the command id to generate the panel for.
|
||||
* @param: {Object} the targeted Zombie.
|
||||
* @param: {Object} the status bar.
|
||||
*/
|
||||
function genExisingExploitPanel(panel, command_id, zombie, sb) {
|
||||
if(typeof panel != 'object') {
|
||||
Ext.beef.msg('Bad!', 'Incorrect panel chosen.');
|
||||
return;
|
||||
}
|
||||
|
||||
sb.showBusy(); // status bar update
|
||||
panel.removeAll();
|
||||
|
||||
Ext.Ajax.request({
|
||||
url:'/ui/modules/select/command.json?command_id='+command_id,
|
||||
loadMask: true,
|
||||
success: function(resp) {
|
||||
var xhr = Ext.decode(resp.responseText);
|
||||
|
||||
if(!xhr || !xhr.definition) {
|
||||
Ext.beef.msg('Error!', 'We could not retrieve the definition of that command module...');
|
||||
return;
|
||||
}
|
||||
|
||||
if(xhr.command_module_name == 'some specific command module') {
|
||||
//HERE we will develop specific panels for the command modules that require it.
|
||||
return;
|
||||
}
|
||||
|
||||
var form = new Ext.form.FormPanel({
|
||||
url: '/ui/modules/commandmodule/reexecute',
|
||||
id: 'form-command-module-zombie-'+zombie.session,
|
||||
border: false,
|
||||
labelWidth: 75,
|
||||
defaultType: 'textfield',
|
||||
title: re_execute_command_title,
|
||||
bodyStyle: 'padding: 5px;',
|
||||
|
||||
items: [new Ext.form.Hidden({name: 'command_id', value: command_id})],
|
||||
|
||||
buttons:[{
|
||||
text: zombie_reexecute_button_text,
|
||||
handler: function() {
|
||||
var form = Ext.getCmp('form-command-module-zombie-'+zombie.session);
|
||||
if(!form || !form.getForm().isValid()) return;
|
||||
|
||||
sb.update_sending('Sending commands to ' + zombie.ip + '...'); // status bar update
|
||||
|
||||
var command_module_form = form.getForm(); // form to be submitted when execute button is pressed on an command module tab
|
||||
command_module_form.submit({
|
||||
params: { // insert the nonce with the form
|
||||
nonce: Ext.get ("nonce").dom.value
|
||||
},
|
||||
success: function() {
|
||||
sb.update_sent("Commands sent to zombie " + zombie.ip); // status bar update
|
||||
},
|
||||
failure: function() {
|
||||
sb.update_fail("Error!"); // status bar update
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
Ext.each(xhr.definition.Data, function(input) {
|
||||
var value;
|
||||
|
||||
if(typeof input == 'string') {
|
||||
value = xhr.data[input];
|
||||
}
|
||||
|
||||
if(typeof input == 'object' && typeof input[0] == 'object') {
|
||||
value = xhr.data[input[0]['name']]
|
||||
}
|
||||
|
||||
genExploitFormControl(form, input, value, false, zombie, sb);
|
||||
});
|
||||
|
||||
var grid_store = new Ext.data.JsonStore({
|
||||
url: '/ui/modules/select/command_results.json?command_id='+command_id,
|
||||
storeId: 'command-results-store-zombie-'+zombie.session,
|
||||
root: 'results',
|
||||
remoteSort: false,
|
||||
autoDestroy: true,
|
||||
fields: [
|
||||
{name: 'date', type: 'date', dateFormat: 'timestamp'},
|
||||
{name: 'data'}
|
||||
]
|
||||
});
|
||||
|
||||
Ext.TaskMgr.start({
|
||||
run: function(task) {
|
||||
var grid = Ext.getCmp('command-results-grid-zombie-'+zombie.session);
|
||||
//here we have to check for the existance of the grid before reloading
|
||||
//because we do not want to reload the store if the tab has been closed.
|
||||
if(grid_store && grid) {
|
||||
grid_store.reload();
|
||||
}
|
||||
},
|
||||
interval: 4000
|
||||
});
|
||||
|
||||
var grid = new Ext.grid.GridPanel({
|
||||
id: 'command-results-grid-zombie-'+zombie.session,
|
||||
store: grid_store,
|
||||
border: false,
|
||||
hideHeaders: true,
|
||||
title: 'Command results',
|
||||
|
||||
viewConfig: {
|
||||
forceFit:true
|
||||
},
|
||||
|
||||
columns:[new Ext.grid.RowNumberer({width: 20}), {
|
||||
dataIndex: 'date',
|
||||
sortable: false,
|
||||
renderer: function(value, p, record) {
|
||||
html = String.format("<div style='color:#385F95;text-align:right;'>{0}</div>", value);
|
||||
html += '<p>';
|
||||
|
||||
for(index in record.data.data) {
|
||||
result = record.data.data[index];
|
||||
index = index.toString().replace('_', ' ');
|
||||
|
||||
html += String.format('<b>{0}</b>: {1}<br>', index, result);
|
||||
}
|
||||
|
||||
html += '</p>';
|
||||
return html;
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
grid.store.load();
|
||||
|
||||
var accordion = new Ext.Panel({
|
||||
id: 'command-results-accordion-zombie-'+zombie.session,
|
||||
layout:'accordion',
|
||||
border: false,
|
||||
items: [grid, form]
|
||||
});
|
||||
|
||||
panel.add(accordion);
|
||||
panel.doLayout();
|
||||
|
||||
sb.update_ready(); // status bar update
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a panel for an command module.
|
||||
*
|
||||
* @param: {Object} the Panel in the UI to generate the form into.
|
||||
* @param: {String} the path to the command module file in the framework.
|
||||
* @param: {String} the name of the command module.
|
||||
* @param: {Object} the targeted Zombie.
|
||||
* @param: {Object} the status bar.
|
||||
*/
|
||||
function genNewExploitPanel(panel, command_module_id, command_module_name, zombie, sb) {
|
||||
if(typeof panel != 'object') {
|
||||
Ext.beef.msg('Bad!', 'Incorrect panel chosen.');
|
||||
return;
|
||||
}
|
||||
|
||||
var xgrid = Ext.getCmp('command-module-grid-zombie-'+zombie.session);
|
||||
var sb = Ext.getCmp('commands-bbar-zombie-'+zombie.session);
|
||||
|
||||
if(command_module_name == 'some special command module') {
|
||||
//HERE we will develop specific panels for the command modules that require it.
|
||||
} else {
|
||||
Ext.Ajax.request({
|
||||
loadMask: true,
|
||||
url: '/ui/modules/select/commandmodule.json?command_module_id='+command_module_id,
|
||||
success: function(resp) {
|
||||
var module = Ext.decode(resp.responseText);
|
||||
|
||||
if(!module) {
|
||||
Ext.beef.msg('Error!', 'We could not retrieve the definition of that command_module...');
|
||||
return;
|
||||
}
|
||||
|
||||
module = module.command_modules[1];
|
||||
panel.removeAll();
|
||||
|
||||
var form = new Ext.form.FormPanel({
|
||||
url: '/ui/modules/commandmodule/new',
|
||||
|
||||
id: 'form-command-module-zombie-'+zombie.session,
|
||||
border: false,
|
||||
labelWidth: 75,
|
||||
defaultType: 'textfield',
|
||||
title: module.Name,
|
||||
bodyStyle: 'padding: 5px;',
|
||||
|
||||
items: [
|
||||
new Ext.form.Hidden({name: 'zombie_session', value: zombie.session}),
|
||||
new Ext.form.Hidden({name: 'command_module_id', value: command_module_id}),
|
||||
new Ext.form.DisplayField({
|
||||
name: 'command_module_description',
|
||||
fieldLabel: 'Description',
|
||||
fieldClass: 'command-module-panel-description',
|
||||
value: module.Description
|
||||
})
|
||||
],
|
||||
|
||||
buttons:[{
|
||||
text: zombie_execute_button_text,
|
||||
handler: function() {
|
||||
var form = Ext.getCmp('form-command-module-zombie-'+zombie.session), command_module_params = new Array();
|
||||
if(!form || !form.getForm().isValid()) return;
|
||||
|
||||
sb.update_sending('Sending commands to ' + zombie.ip + '...'); // status bar update
|
||||
|
||||
var command_module_form = form.getForm(); // form to be submitted when execute button is pressed on an command module tab
|
||||
command_module_form.submit({
|
||||
params: { // insert the nonce with the form
|
||||
nonce: Ext.get ("nonce").dom.value
|
||||
},
|
||||
success: function() {
|
||||
xgrid.i = 0;
|
||||
xgrid.store.reload({ //reload the command module grid
|
||||
params: { // insert the nonce with the request to reload the grid
|
||||
nonce: Ext.get ("nonce").dom.value
|
||||
}
|
||||
});
|
||||
sb.update_sent("Commands sent to zombie " + zombie.ip); // status bar update
|
||||
},
|
||||
failure: function() {
|
||||
sb.update_fail("Error!"); // status bar update
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
Ext.each(module.Data, function(input){genExploitFormControl(form, input, null, false, zombie, sb)});
|
||||
|
||||
panel.add(form);
|
||||
panel.doLayout();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
129
public/javascript/ui/panel/tabs/ZombieTabCommands.js
Normal file
129
public/javascript/ui/panel/tabs/ZombieTabCommands.js
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* The command tab panel. Listing the list of commands sent to the zombie.
|
||||
* Loaded in /ui/panel/index.html
|
||||
*/
|
||||
ZombieTab_Commands = function(zombie) {
|
||||
|
||||
var command_module_config = new Ext.Panel({
|
||||
id: 'zombie-command-module-config-'+zombie.session,
|
||||
region: 'center',
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
html: "<div class='x-grid-empty'>Please select an command module in the command module tree on the left</div>"
|
||||
});
|
||||
|
||||
var command_module_grid = new Ext.grid.GridPanel({
|
||||
store: new Ext.data.JsonStore({
|
||||
url: '/ui/modules/commandmodule/commands.json',
|
||||
params: { // insert the nonce with the form
|
||||
nonce: Ext.get ("nonce").dom.value
|
||||
},
|
||||
autoDestroy: false,
|
||||
autoLoad: false,
|
||||
root: 'commands',
|
||||
fields: ['label', 'creationdate', 'id', 'object_id'],
|
||||
sortInfo: {field: 'id', direction: 'ASC'}
|
||||
}),
|
||||
|
||||
id: 'command-module-grid-zombie-'+zombie.session,
|
||||
sortable: true,
|
||||
autoWidth: false,
|
||||
region: 'west',
|
||||
stripeRows: true,
|
||||
autoScroll: true,
|
||||
border: false,
|
||||
width: 260,
|
||||
bodyStyle: 'border-right: 1px solid #99BBE8; border-left: 1px solid #99BBE8;',
|
||||
i:0,
|
||||
|
||||
view: new Ext.grid.GridView({
|
||||
forceFit: true,
|
||||
emptyText: "No Command found",
|
||||
enableRowBody:true
|
||||
}),
|
||||
|
||||
columns: [
|
||||
{header: 'id', width: 35, sortable: true, dataIndex: 'id'},
|
||||
{header: 'date', width: 100, sortable: true, dataIndex: 'creationdate'},
|
||||
{header: 'label', sortable: true, dataIndex: 'label', renderer: function(value) { if(value==null) {command_module_grid.i +=1; return 'command '+command_module_grid.i;} else return value;}},
|
||||
{header: 'object_id', sortable: true, dataIndex: 'object_id', hidden: true, menuDisabled: true}
|
||||
]
|
||||
});
|
||||
|
||||
command_module_grid.on('rowclick', function(grid, rowIndex, e) {
|
||||
var r = grid.getStore().getAt(rowIndex).data;
|
||||
var command_id = r.object_id || null;
|
||||
|
||||
if(!command_id) return;
|
||||
|
||||
genExisingExploitPanel(command_module_config, command_id, zombie, commands_statusbar);
|
||||
});
|
||||
|
||||
var command_module_tree = new Ext.tree.TreePanel({
|
||||
border: false,
|
||||
region: 'west',
|
||||
width: 190,
|
||||
useArrows: true,
|
||||
autoScroll: true,
|
||||
animate: true,
|
||||
containerScroll: true,
|
||||
rootVisible: false,
|
||||
root: {nodeType: 'async'},
|
||||
loader: new Ext.tree.TreeLoader({
|
||||
dataUrl: '/ui/modules/select/commandmodules/tree.json',
|
||||
baseParams: {zombie_session: zombie.session}
|
||||
}),
|
||||
listeners: {
|
||||
'click': function(node) {
|
||||
if(!node.leaf) return;
|
||||
|
||||
commands_statusbar.showBusy('Loading ' + node.text);
|
||||
|
||||
command_module_grid.i = 0;
|
||||
command_module_grid.store.baseParams = {command_module_id: node.attributes.id, zombie_session: zombie.session};
|
||||
command_module_grid.store.reload({ //reload the command module grid
|
||||
params: { // insert the nonce with the request to reload the grid
|
||||
nonce: Ext.get ("nonce").dom.value
|
||||
}
|
||||
});
|
||||
|
||||
genNewExploitPanel(command_module_config, node.id, node.text, zombie, commands_statusbar);
|
||||
commands_statusbar.showValid('Ready');
|
||||
},
|
||||
'afterrender' : function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var commands_statusbar = new Beef_StatusBar(zombie.session);
|
||||
|
||||
ZombieTab_Commands.superclass.constructor.call(this, {
|
||||
id: 'zombie-'+zombie.session+'-command-module-panel',
|
||||
title:'Commands',
|
||||
layout: 'fit',
|
||||
region: 'center',
|
||||
autScroll: true,
|
||||
items: {
|
||||
layout: 'border',
|
||||
border: false,
|
||||
items: [command_module_tree,
|
||||
new Ext.Panel({
|
||||
id: 'zombie-command-module-west-'+zombie.session,
|
||||
region: 'center',
|
||||
layout: 'fit',
|
||||
border: false,
|
||||
items: {
|
||||
layout: 'border',
|
||||
border: false,
|
||||
items: [command_module_grid, command_module_config]
|
||||
}
|
||||
})]
|
||||
},
|
||||
|
||||
bbar: commands_statusbar
|
||||
});
|
||||
|
||||
var sb = Ext.getCmp('command-module-bbar-zombie-'+zombie.session);
|
||||
};
|
||||
|
||||
Ext.extend(ZombieTab_Commands, Ext.Panel, {});
|
||||
22
public/javascript/ui/panel/tabs/ZombieTabLogs.js
Normal file
22
public/javascript/ui/panel/tabs/ZombieTabLogs.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* The log Tab panel for the selected zombie.
|
||||
*/
|
||||
ZombieTab_LogTab = function(zombie) {
|
||||
|
||||
var zombieLog = new DataGrid('/ui/logs/zombie.json',25,{session:zombie.session});
|
||||
zombieLog.border = false;
|
||||
|
||||
ZombieTab_LogTab.superclass.constructor.call(this, {
|
||||
id: 'zombie-log-tab' + zombie.session,
|
||||
layout: 'fit',
|
||||
title: 'Logs',
|
||||
items: {
|
||||
layout: 'border',
|
||||
border: false,
|
||||
items:[zombieLog]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Ext.extend(ZombieTab_LogTab, Ext.Panel, {} );
|
||||
|
||||
83
public/javascript/ui/panel/tabs/ZombieTabMain.js
Normal file
83
public/javascript/ui/panel/tabs/ZombieTabMain.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* The main Tab panel for the selected zombie.
|
||||
*/
|
||||
ZombieTab_MainTab = function(zombie) {
|
||||
|
||||
var store_summary = new Ext.data.GroupingStore({
|
||||
url: '/ui/modules/select/zombie_summary.json',
|
||||
baseParams: {zombie_session: zombie.session} ,
|
||||
reader: new Ext.data.JsonReader({
|
||||
root: 'results'
|
||||
},[
|
||||
{name: 'data'},
|
||||
{name: 'category'},
|
||||
{name: 'from'}
|
||||
]),
|
||||
|
||||
autoLoad: true,
|
||||
sortInfo:{field: 'from', direction: "ASC"},
|
||||
groupField:'category'
|
||||
});
|
||||
|
||||
var grid_summary = new Ext.grid.GridPanel({
|
||||
store: store_summary,
|
||||
border: false,
|
||||
region: 'center',
|
||||
layout: 'fit',
|
||||
hideHeaders: true,
|
||||
|
||||
view: new Ext.grid.GroupingView({
|
||||
forceFit:true,
|
||||
groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',
|
||||
emptyText: "No Record found: this tab gets populated after you've ran some command modules.",
|
||||
enableRowBody:true
|
||||
}),
|
||||
|
||||
viewConfig: {
|
||||
forceFit:true
|
||||
},
|
||||
|
||||
columns:[
|
||||
{
|
||||
header: 'information',
|
||||
dataIndex: 'data',
|
||||
renderer: function(value, p, record) {
|
||||
html = ''
|
||||
|
||||
for(index in value) {
|
||||
result = value[index];
|
||||
index = index.toString().replace('_', ' ');
|
||||
|
||||
html += String.format('<b>{0}</b>: {1}<br>', index, result);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
|
||||
{header: 'command_module', dataIndex:'from', width: 25, renderer: function(value){return value;}},
|
||||
{header: 'Category', dataIndex:'category', hidden: true, renderer: function(value){return value;}}
|
||||
]
|
||||
});
|
||||
|
||||
ZombieTab_MainTab.superclass.constructor.call(this, {
|
||||
id: 'zombie-main-tab'+zombie.session,
|
||||
layout: 'fit',
|
||||
title: 'Main',
|
||||
|
||||
items: {
|
||||
layout:'border',
|
||||
border: false,
|
||||
items:[grid_summary]
|
||||
},
|
||||
|
||||
listeners:{
|
||||
activate : function(maintab){
|
||||
maintab.items.items[0].items.items[0].store.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Ext.extend(ZombieTab_MainTab, Ext.Panel, {});
|
||||
237
public/javascript/ui/panel/tabs/ZombieTabRequester.js
Normal file
237
public/javascript/ui/panel/tabs/ZombieTabRequester.js
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* The request Tab panel for the selected zombie.
|
||||
* Loaded in /ui/panel/index.html
|
||||
*/
|
||||
ZombieTab_Requester = function(zombie) {
|
||||
|
||||
// The status bar.
|
||||
var commands_statusbar = new Beef_StatusBar('requester-bbar-zombie-'+zombie.session);
|
||||
|
||||
|
||||
/*
|
||||
* The panel used to forge raw HTTP requests.
|
||||
********************************************/
|
||||
var requests_panel = new Ext.Panel({
|
||||
id: 'requester-forge-requests-zombie-'+zombie.session,
|
||||
title: 'Forge Request',
|
||||
layout: 'fit'
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* The panel that displays the history of all requests performed.
|
||||
********************************************/
|
||||
var history_panel_store = new Ext.data.JsonStore({
|
||||
storeId: 'requester-history-store-zombie-'+zombie.session,
|
||||
url: '/ui/requester/history.json',
|
||||
remoteSort: false,
|
||||
autoDestroy: true,
|
||||
root: 'history',
|
||||
|
||||
fields: ['domain', 'date', 'id', 'has_ran', 'path'],
|
||||
sortInfo: {field: 'date', direction: 'DESC'},
|
||||
|
||||
baseParams: {
|
||||
nonce: Ext.get("nonce").dom.value,
|
||||
zombie_session: zombie.session
|
||||
}
|
||||
});
|
||||
|
||||
var history_panel_grid = new Ext.grid.GridPanel({
|
||||
id: 'requester-history-grid-zombie-'+zombie.session,
|
||||
store: history_panel_store,
|
||||
border: false,
|
||||
loadMask: {msg:'Loading History...'},
|
||||
|
||||
viewConfig: {
|
||||
forceFit:true
|
||||
},
|
||||
|
||||
view: new Ext.grid.GridView({
|
||||
forceFit: true,
|
||||
emptyText: "No History",
|
||||
enableRowBody:true
|
||||
}),
|
||||
|
||||
columns: [
|
||||
{header: 'id', width: 10, sortable: true, dataIndex: 'id', hidden: true},
|
||||
{header: 'domain', sortable: true, dataIndex: 'domain'},
|
||||
{header: 'path', sortable: true, dataIndex: 'path'},
|
||||
{header: 'response', width: 20, sortable: true, dataIndex: 'has_ran'},
|
||||
{header: 'date', width: 50, sortable: true, dataIndex: 'date'}
|
||||
],
|
||||
|
||||
listeners: {
|
||||
rowclick: function(grid, rowIndex) {
|
||||
var tab_panel = Ext.getCmp('zombie-requester-tab-zombie-'+zombie.session);
|
||||
var r = grid.getStore().getAt(rowIndex).data;
|
||||
|
||||
if(!r.has_ran) {
|
||||
commands_statusbar.update_fail("Response for this request has not been received yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!tab_panel.get('requester-response-'+r.id)) {
|
||||
genResultTab(r, zombie, commands_statusbar);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var history_panel = new Ext.Panel({
|
||||
id: 'requester-history-panel-zombie-'+zombie.session,
|
||||
title: 'History',
|
||||
items:[history_panel_grid],
|
||||
layout: 'fit',
|
||||
|
||||
listeners: {
|
||||
activate: function(history_panel) {
|
||||
history_panel.items.items[0].store.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Function generating the requests panel to send raw requests
|
||||
//-------------------------------------------------------------
|
||||
function genRawRequestPanel(zombie, bar, value) {
|
||||
var form = new Ext.FormPanel({
|
||||
title: 'Forge Raw HTTP Request',
|
||||
id: 'requester-request-form-zombie'+zombie.session,
|
||||
url: '/ui/requester/send',
|
||||
hideLabels : true,
|
||||
border: false,
|
||||
padding: '3px 5px 0 5px',
|
||||
|
||||
items:[{
|
||||
xtype: 'textarea',
|
||||
id: 'raw-request-zombie-'+zombie.session,
|
||||
name: 'raw_request',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
allowBlank: false
|
||||
}],
|
||||
|
||||
buttons: [{
|
||||
text: 'Send',
|
||||
handler: function() {
|
||||
var form = Ext.getCmp('requester-request-form-zombie'+zombie.session).getForm();
|
||||
|
||||
bar.update_sending('Sending request to ' + zombie.ip + '...');
|
||||
|
||||
form.submit({
|
||||
params: {
|
||||
nonce: Ext.get("nonce").dom.value,//insert the nonce with the form
|
||||
zombie_session: zombie.session
|
||||
},
|
||||
success: function() {
|
||||
bar.update_sent("Request sent to hooked browser " + zombie.ip);
|
||||
},
|
||||
failure: function() {
|
||||
bar.update_fail("Error! Invalid http request.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
if(!value) {
|
||||
value = "GET /demos/secret_page.html HTTP/1.1\n";
|
||||
|
||||
if(zombie.domain) {
|
||||
value += "Host: "+zombie.domain.split(':')[0]+"\n";
|
||||
} else {
|
||||
value += "Host: \n";
|
||||
}
|
||||
}
|
||||
|
||||
form.get('raw-request-zombie-'+zombie.session).value = value;
|
||||
|
||||
panel = Ext.getCmp('requester-forge-requests-zombie-'+zombie.session);
|
||||
panel.setTitle('Forge Request');
|
||||
panel.add(form);
|
||||
};
|
||||
|
||||
// Function generating the panel that shows the results of a request
|
||||
// This function is called when the user clicks on a row in the grid
|
||||
// showing the results in the history.
|
||||
//------------------------------------------------------------------
|
||||
function genResultTab(request, zombie, bar) {
|
||||
var tab_panel = Ext.getCmp('zombie-requester-tab-zombie-'+zombie.session);
|
||||
|
||||
bar.update_sending('Getting response...');
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: '/ui/requester/response.json',
|
||||
loadMask: true,
|
||||
|
||||
params: {
|
||||
nonce: Ext.get("nonce").dom.value,
|
||||
http_id: request.id
|
||||
},
|
||||
|
||||
success: function(response) {
|
||||
var xhr = Ext.decode(response.responseText);
|
||||
|
||||
var tab_result_response = new Ext.Panel({
|
||||
title: 'Reponse',
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
padding: '5px 5px 5px 5px',
|
||||
items:[new Ext.form.TextArea({id: 'requester-response-res-'+request.id, value: xhr.result.response})]
|
||||
});
|
||||
|
||||
var tab_result_request = new Ext.Panel({
|
||||
title: 'Request',
|
||||
border: false,
|
||||
layout: 'fit',
|
||||
padding: '5px 5px 5px 5px',
|
||||
items:[new Ext.form.TextArea({id: 'requester-response-req-'+request.id, value: xhr.result.request})]
|
||||
});
|
||||
|
||||
var tab_result_accordion = new Ext.Panel({
|
||||
id: 'requester-response-'+request.id,
|
||||
title: request.path,
|
||||
split: true,
|
||||
border: false,
|
||||
layout:'accordion',
|
||||
closable: true,
|
||||
items:[tab_result_response, tab_result_request]
|
||||
});
|
||||
|
||||
tab_panel.add(tab_result_accordion);
|
||||
tab_panel.activate(tab_result_accordion.id);
|
||||
|
||||
bar.update_sent("Displaying response.");
|
||||
},
|
||||
|
||||
failure: function() {
|
||||
bar.update_fail("Error! Could you retrieve the response.");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
ZombieTab_Requester.superclass.constructor.call(this, {
|
||||
id: 'zombie-requester-tab-zombie-'+zombie.session,
|
||||
title: 'Requester',
|
||||
activeTab: 0,
|
||||
viewConfig: {
|
||||
forceFit: true,
|
||||
type: 'fit'
|
||||
},
|
||||
|
||||
items: [history_panel, requests_panel],
|
||||
|
||||
bbar: commands_statusbar,
|
||||
|
||||
listeners: {
|
||||
afterrender : function(){
|
||||
genRawRequestPanel(zombie, commands_statusbar);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Ext.extend(ZombieTab_Requester, Ext.TabPanel, {});
|
||||
446
public/javascript/ux/StatusBar.js
Normal file
446
public/javascript/ux/StatusBar.js
Normal file
@@ -0,0 +1,446 @@
|
||||
/*!
|
||||
* Ext JS Library 3.1.1
|
||||
* Copyright(c) 2006-2010 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.StatusBar
|
||||
* <p>Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}. In addition to
|
||||
* supporting the standard {@link Ext.Toolbar} interface for adding buttons, menus and other items, the StatusBar
|
||||
* provides a greedy status element that can be aligned to either side and has convenient methods for setting the
|
||||
* status text and icon. You can also indicate that something is processing using the {@link #showBusy} method.</p>
|
||||
* <pre><code>
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
id: 'my-status',
|
||||
|
||||
// defaults to use when the status is cleared:
|
||||
defaultText: 'Default status text',
|
||||
defaultIconCls: 'default-icon',
|
||||
|
||||
// values to set initially:
|
||||
text: 'Ready',
|
||||
iconCls: 'ready-icon',
|
||||
|
||||
// any standard Toolbar items:
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
|
||||
// Update the status bar later in code:
|
||||
var sb = Ext.getCmp('my-status');
|
||||
sb.setStatus({
|
||||
text: 'OK',
|
||||
iconCls: 'ok-icon',
|
||||
clear: true // auto-clear after a set interval
|
||||
});
|
||||
|
||||
// Set the status bar to show that something is processing:
|
||||
sb.showBusy();
|
||||
|
||||
// processing....
|
||||
|
||||
sb.clearStatus(); // once completeed
|
||||
</code></pre>
|
||||
* @extends Ext.Toolbar
|
||||
* @constructor
|
||||
* Creates a new StatusBar
|
||||
* @param {Object/Array} config A config object
|
||||
*/
|
||||
Ext.ux.StatusBar = Ext.extend(Ext.Toolbar, {
|
||||
/**
|
||||
* @cfg {String} statusAlign
|
||||
* The alignment of the status element within the overall StatusBar layout. When the StatusBar is rendered,
|
||||
* it creates an internal div containing the status text and icon. Any additional Toolbar items added in the
|
||||
* StatusBar's {@link #items} config, or added via {@link #add} or any of the supported add* methods, will be
|
||||
* rendered, in added order, to the opposite side. The status element is greedy, so it will automatically
|
||||
* expand to take up all sapce left over by any other items. Example usage:
|
||||
* <pre><code>
|
||||
// Create a left-aligned status bar containing a button,
|
||||
// separator and text item that will be right-aligned (default):
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
defaultText: 'Default status text',
|
||||
id: 'status-id',
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
|
||||
// By adding the statusAlign config, this will create the
|
||||
// exact same toolbar, except the status and toolbar item
|
||||
// layout will be reversed from the previous example:
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
defaultText: 'Default status text',
|
||||
id: 'status-id',
|
||||
statusAlign: 'right',
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
</code></pre>
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} defaultText
|
||||
* The default {@link #text} value. This will be used anytime the status bar is cleared with the
|
||||
* <tt>useDefaults:true</tt> option (defaults to '').
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} defaultIconCls
|
||||
* The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon).
|
||||
* This will be used anytime the status bar is cleared with the <tt>useDefaults:true</tt> option (defaults to '').
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} text
|
||||
* A string that will be <b>initially</b> set as the status message. This string
|
||||
* will be set as innerHTML (html tags are accepted) for the toolbar item.
|
||||
* If not specified, the value set for <code>{@link #defaultText}</code>
|
||||
* will be used.
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} iconCls
|
||||
* A CSS class that will be <b>initially</b> set as the status bar icon and is
|
||||
* expected to provide a background image (defaults to '').
|
||||
* Example usage:<pre><code>
|
||||
// Example CSS rule:
|
||||
.x-statusbar .x-status-custom {
|
||||
padding-left: 25px;
|
||||
background: transparent url(images/custom-icon.gif) no-repeat 3px 2px;
|
||||
}
|
||||
|
||||
// Setting a default icon:
|
||||
var sb = new Ext.ux.StatusBar({
|
||||
defaultIconCls: 'x-status-custom'
|
||||
});
|
||||
|
||||
// Changing the icon:
|
||||
sb.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom'
|
||||
});
|
||||
</code></pre>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @cfg {String} cls
|
||||
* The base class applied to the containing element for this component on render (defaults to 'x-statusbar')
|
||||
*/
|
||||
cls : 'x-statusbar',
|
||||
/**
|
||||
* @cfg {String} busyIconCls
|
||||
* The default <code>{@link #iconCls}</code> applied when calling
|
||||
* <code>{@link #showBusy}</code> (defaults to <tt>'x-status-busy'</tt>).
|
||||
* It can be overridden at any time by passing the <code>iconCls</code>
|
||||
* argument into <code>{@link #showBusy}</code>.
|
||||
*/
|
||||
busyIconCls : 'x-status-busy',
|
||||
|
||||
//BEEF ADDED
|
||||
errorIconCls : 'x-status-error',
|
||||
validIconCls : 'x-status-valid',
|
||||
|
||||
/**
|
||||
* @cfg {String} busyText
|
||||
* The default <code>{@link #text}</code> applied when calling
|
||||
* <code>{@link #showBusy}</code> (defaults to <tt>'Loading...'</tt>).
|
||||
* It can be overridden at any time by passing the <code>text</code>
|
||||
* argument into <code>{@link #showBusy}</code>.
|
||||
*/
|
||||
busyText : 'Loading...',
|
||||
/**
|
||||
* @cfg {Number} autoClear
|
||||
* The number of milliseconds to wait after setting the status via
|
||||
* <code>{@link #setStatus}</code> before automatically clearing the status
|
||||
* text and icon (defaults to <tt>5000</tt>). Note that this only applies
|
||||
* when passing the <tt>clear</tt> argument to <code>{@link #setStatus}</code>
|
||||
* since that is the only way to defer clearing the status. This can
|
||||
* be overridden by specifying a different <tt>wait</tt> value in
|
||||
* <code>{@link #setStatus}</code>. Calls to <code>{@link #clearStatus}</code>
|
||||
* always clear the status bar immediately and ignore this value.
|
||||
*/
|
||||
autoClear : 5000,
|
||||
|
||||
/**
|
||||
* @cfg {String} emptyText
|
||||
* The text string to use if no text has been set. Defaults to
|
||||
* <tt>' '</tt>). If there are no other items in the toolbar using
|
||||
* an empty string (<tt>''</tt>) for this value would end up in the toolbar
|
||||
* height collapsing since the empty string will not maintain the toolbar
|
||||
* height. Use <tt>''</tt> if the toolbar should collapse in height
|
||||
* vertically when no text is specified and there are no other items in
|
||||
* the toolbar.
|
||||
*/
|
||||
emptyText : ' ',
|
||||
|
||||
// private
|
||||
activeThreadId : 0,
|
||||
|
||||
// private
|
||||
initComponent : function(){
|
||||
if(this.statusAlign=='right'){
|
||||
this.cls += ' x-status-right';
|
||||
}
|
||||
Ext.ux.StatusBar.superclass.initComponent.call(this);
|
||||
},
|
||||
|
||||
// private
|
||||
afterRender : function(){
|
||||
Ext.ux.StatusBar.superclass.afterRender.call(this);
|
||||
|
||||
var right = this.statusAlign == 'right';
|
||||
this.currIconCls = this.iconCls || this.defaultIconCls;
|
||||
this.statusEl = new Ext.Toolbar.TextItem({
|
||||
cls: 'x-status-text ' + (this.currIconCls || ''),
|
||||
text: this.text || this.defaultText || ''
|
||||
});
|
||||
|
||||
if(right){
|
||||
this.add('->');
|
||||
this.add(this.statusEl);
|
||||
}else{
|
||||
this.insert(0, this.statusEl);
|
||||
this.insert(1, '->');
|
||||
}
|
||||
this.doLayout();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the
|
||||
* status that was set after a specified interval.
|
||||
* @param {Object/String} config A config object specifying what status to set, or a string assumed
|
||||
* to be the status text (and all other options are defaulted as explained below). A config
|
||||
* object containing any or all of the following properties can be passed:<ul>
|
||||
* <li><tt>text</tt> {String} : (optional) The status text to display. If not specified, any current
|
||||
* status text will remain unchanged.</li>
|
||||
* <li><tt>iconCls</tt> {String} : (optional) The CSS class used to customize the status icon (see
|
||||
* {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.</li>
|
||||
* <li><tt>clear</tt> {Boolean/Number/Object} : (optional) Allows you to set an internal callback that will
|
||||
* automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not
|
||||
* specified, the new status will not be auto-cleared and will stay until updated again or cleared using
|
||||
* {@link #clearStatus}. If <tt>true</tt> is passed, the status will be cleared using {@link #autoClear},
|
||||
* {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed,
|
||||
* it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value.
|
||||
* All other options will be defaulted as with the boolean option. To customize any other options,
|
||||
* you can pass an object in the format:<ul>
|
||||
* <li><tt>wait</tt> {Number} : (optional) The number of milliseconds to wait before clearing
|
||||
* (defaults to {@link #autoClear}).</li>
|
||||
* <li><tt>anim</tt> {Number} : (optional) False to clear the status immediately once the callback
|
||||
* executes (defaults to true which fades the status out).</li>
|
||||
* <li><tt>useDefaults</tt> {Number} : (optional) False to completely clear the status text and iconCls
|
||||
* (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).</li>
|
||||
* </ul></li></ul>
|
||||
* Example usage:<pre><code>
|
||||
// Simple call to update the text
|
||||
statusBar.setStatus('New status');
|
||||
|
||||
// Set the status and icon, auto-clearing with default options:
|
||||
statusBar.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom',
|
||||
clear: true
|
||||
});
|
||||
|
||||
// Auto-clear with custom options:
|
||||
statusBar.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom',
|
||||
clear: {
|
||||
wait: 8000,
|
||||
anim: false,
|
||||
useDefaults: false
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setStatus : function(o){
|
||||
o = o || {};
|
||||
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
if(o.text !== undefined){
|
||||
this.setText(o.text);
|
||||
}
|
||||
if(o.iconCls !== undefined){
|
||||
this.setIcon(o.iconCls);
|
||||
}
|
||||
|
||||
if(o.clear){
|
||||
var c = o.clear,
|
||||
wait = this.autoClear,
|
||||
defaults = {useDefaults: true, anim: true};
|
||||
|
||||
if(typeof c == 'object'){
|
||||
c = Ext.applyIf(c, defaults);
|
||||
if(c.wait){
|
||||
wait = c.wait;
|
||||
}
|
||||
}else if(typeof c == 'number'){
|
||||
wait = c;
|
||||
c = defaults;
|
||||
}else if(typeof c == 'boolean'){
|
||||
c = defaults;
|
||||
}
|
||||
|
||||
c.threadId = this.activeThreadId;
|
||||
this.clearStatus.defer(wait, this, [c]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation.
|
||||
* @param {Object} config (optional) A config object containing any or all of the following properties. If this
|
||||
* object is not specified the status will be cleared using the defaults below:<ul>
|
||||
* <li><tt>anim</tt> {Boolean} : (optional) True to clear the status by fading out the status element (defaults
|
||||
* to false which clears immediately).</li>
|
||||
* <li><tt>useDefaults</tt> {Boolean} : (optional) True to reset the text and icon using {@link #defaultText} and
|
||||
* {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).</li>
|
||||
* </ul>
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
clearStatus : function(o){
|
||||
o = o || {};
|
||||
|
||||
if(o.threadId && o.threadId !== this.activeThreadId){
|
||||
// this means the current call was made internally, but a newer
|
||||
// thread has set a message since this call was deferred. Since
|
||||
// we don't want to overwrite a newer message just ignore.
|
||||
return this;
|
||||
}
|
||||
|
||||
var text = o.useDefaults ? this.defaultText : this.emptyText,
|
||||
iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : '';
|
||||
|
||||
if(o.anim){
|
||||
// animate the statusEl Ext.Element
|
||||
this.statusEl.el.fadeOut({
|
||||
remove: false,
|
||||
useDisplay: true,
|
||||
scope: this,
|
||||
callback: function(){
|
||||
this.setStatus({
|
||||
text: text,
|
||||
iconCls: iconCls
|
||||
});
|
||||
|
||||
this.statusEl.el.show();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
// hide/show the el to avoid jumpy text or icon
|
||||
this.statusEl.hide();
|
||||
this.setStatus({
|
||||
text: text,
|
||||
iconCls: iconCls
|
||||
});
|
||||
this.statusEl.show();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status text directly. For more flexible options see {@link #setStatus}.
|
||||
* @param {String} text (optional) The text to set (defaults to '')
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setText : function(text){
|
||||
this.activeThreadId++;
|
||||
this.text = text || '';
|
||||
if(this.rendered){
|
||||
this.statusEl.setText(this.text);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the current status text.
|
||||
* @return {String} The status text
|
||||
*/
|
||||
getText : function(){
|
||||
return this.text;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status icon directly. For more flexible options see {@link #setStatus}.
|
||||
* See {@link #iconCls} for complete details about customizing the icon.
|
||||
* @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed)
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setIcon : function(cls){
|
||||
this.activeThreadId++;
|
||||
cls = cls || '';
|
||||
|
||||
if(this.rendered){
|
||||
if(this.currIconCls){
|
||||
this.statusEl.removeClass(this.currIconCls);
|
||||
this.currIconCls = null;
|
||||
}
|
||||
if(cls.length > 0){
|
||||
this.statusEl.addClass(cls);
|
||||
this.currIconCls = cls;
|
||||
}
|
||||
}else{
|
||||
this.currIconCls = cls;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status text and icon to special values that are pre-configured to indicate
|
||||
* a "busy" state, usually for loading or processing activities.
|
||||
* @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a
|
||||
* string to use as the status text (in which case all other options for setStatus will be defaulted). Use the
|
||||
* <tt>text</tt> and/or <tt>iconCls</tt> properties on the config to override the default {@link #busyText}
|
||||
* and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and
|
||||
* {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}.
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
showBusy : function(o){
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
o = Ext.applyIf(o || {}, {
|
||||
text: this.busyText,
|
||||
iconCls: this.busyIconCls
|
||||
});
|
||||
return this.setStatus(o);
|
||||
},
|
||||
|
||||
//BEEF ADDED
|
||||
showError : function(o){
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
o = Ext.applyIf(o || {}, {
|
||||
text: this.busyText,
|
||||
iconCls: this.errorIconCls
|
||||
});
|
||||
return this.setStatus(o);
|
||||
},
|
||||
|
||||
showValid : function(o){
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
o = Ext.applyIf(o || {}, {
|
||||
text: this.busyText,
|
||||
iconCls: this.validIconCls
|
||||
});
|
||||
return this.setStatus(o);
|
||||
}
|
||||
});
|
||||
Ext.reg('statusbar', Ext.ux.StatusBar);
|
||||
58
public/javascript/ux/TabCloseMenu.js
Normal file
58
public/javascript/ux/TabCloseMenu.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* Ext JS Library 3.1.1
|
||||
* Copyright(c) 2006-2010 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.TabCloseMenu
|
||||
* @extends Object
|
||||
* Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs.
|
||||
*
|
||||
* @ptype tabclosemenu
|
||||
*/
|
||||
Ext.ux.TabCloseMenu = function(){
|
||||
var tabs, menu, ctxItem;
|
||||
this.init = function(tp){
|
||||
tabs = tp;
|
||||
tabs.on('contextmenu', onContextMenu);
|
||||
};
|
||||
|
||||
function onContextMenu(ts, item, e){
|
||||
if(!menu){ // create context menu on first right click
|
||||
menu = new Ext.menu.Menu({
|
||||
items: [{
|
||||
id: tabs.id + '-close',
|
||||
text: 'Close Tab',
|
||||
handler : function(){
|
||||
tabs.remove(ctxItem);
|
||||
}
|
||||
},{
|
||||
id: tabs.id + '-close-others',
|
||||
text: 'Close Other Tabs',
|
||||
handler : function(){
|
||||
tabs.items.each(function(item){
|
||||
if(item.closable && item != ctxItem){
|
||||
tabs.remove(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}]});
|
||||
}
|
||||
ctxItem = item;
|
||||
var items = menu.items;
|
||||
items.get(tabs.id + '-close').setDisabled(!item.closable);
|
||||
var disableOthers = true;
|
||||
tabs.items.each(function(){
|
||||
if(this != item && this.closable){
|
||||
disableOthers = false;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
items.get(tabs.id + '-close-others').setDisabled(disableOthers);
|
||||
e.stopEvent();
|
||||
menu.showAt(e.getPoint());
|
||||
}
|
||||
};
|
||||
|
||||
Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu);
|
||||
Reference in New Issue
Block a user