400 lines
13 KiB
Ruby
400 lines
13 KiB
Ruby
module Msf
|
|
module Ui
|
|
module Gtk2
|
|
|
|
#
|
|
# Implement a basic window
|
|
#
|
|
class SkeletonBrowser < Gtk::Window
|
|
|
|
COL_TYPE, COL_PATH, COL_DISPLAY_NAME, COL_IS_DIR, COL_PIXBUF = (0..5).to_a
|
|
DOWNLOAD_TARGET_TABLE = ["DOWNLOAD", Gtk::Drag::TARGET_SAME_APP, 0]
|
|
UPLOAD_TARGET_TABLE = ["UPLOAD", Gtk::Drag::TARGET_SAME_APP, 0]
|
|
|
|
include Msf::Ui::Gtk2::MyControls
|
|
|
|
attr_accessor :parent_local, :parent_remote, :remote_path, :local_path, :model_remote, :model_local
|
|
attr_reader :file_pixbuf, :folder_pixbuf
|
|
|
|
def initialize(title = nil, local = "", remote = "")
|
|
super(Gtk::Window::TOPLEVEL)
|
|
set_title("#{title}")
|
|
|
|
signal_connect("key_press_event") do |widget, event|
|
|
if event.state.control_mask? and event.keyval == Gdk::Keyval::GDK_q
|
|
destroy
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
signal_connect("delete_event") do |widget, event|
|
|
destroy
|
|
true
|
|
end
|
|
|
|
# Define the size and border
|
|
set_default_size(1000, 600)
|
|
set_border_width(10)
|
|
|
|
# Define the models (navigation, view)
|
|
# TODO: model for navigation
|
|
@model_local = Gtk::ListStore.new(String, String, String, TrueClass, Gdk::Pixbuf)
|
|
@model_remote = Gtk::ListStore.new(String, String, String, TrueClass, Gdk::Pixbuf)
|
|
|
|
# Define thes parents
|
|
@parent_local = local
|
|
@parent_remote = remote
|
|
@parent_remote_init = remote
|
|
|
|
# Define the icons for folders and files
|
|
@file_pixbuf = Gdk::Pixbuf.new(driver.get_image("msf_file.png"))
|
|
@folder_pixbuf = Gdk::Pixbuf.new(driver.get_image("msf_folder.png"))
|
|
@local_folder_pixbuf = Gdk::Pixbuf.new(driver.get_image("msf_local_folder.png"))
|
|
|
|
@model_local.set_default_sort_func do |a, b|
|
|
if !a[COL_IS_DIR] and b[COL_IS_DIR]
|
|
1
|
|
elsif a[COL_IS_DIR] and !b[COL_IS_DIR]
|
|
-1
|
|
else
|
|
a[COL_DISPLAY_NAME] <=> b[COL_DISPLAY_NAME]
|
|
end
|
|
end
|
|
@model_local.set_sort_column_id(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING)
|
|
|
|
@model_remote.set_default_sort_func do |a, b|
|
|
if !a[COL_IS_DIR] and b[COL_IS_DIR]
|
|
1
|
|
elsif a[COL_IS_DIR] and !b[COL_IS_DIR]
|
|
-1
|
|
else
|
|
a[COL_DISPLAY_NAME] <=> b[COL_DISPLAY_NAME]
|
|
end
|
|
end
|
|
@model_remote.set_sort_column_id(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING)
|
|
|
|
# Main hbox
|
|
hbox = Gtk::HBox.new(false, 0)
|
|
add(hbox)
|
|
|
|
# Left and right vbox
|
|
vbox_left = Gtk::VBox.new(false, 0)
|
|
vbox_right = Gtk::VBox.new(false, 0)
|
|
vbox_paned = Gtk::HPaned.new
|
|
vbox_paned.set_size_request(900, -1)
|
|
hbox.pack_start(vbox_paned, true, true, 0)
|
|
vbox_paned.pack1(vbox_left, true, false)
|
|
vbox_paned.pack2(vbox_right, true, false)
|
|
|
|
#
|
|
# Local
|
|
#
|
|
# Label, Entry and Signal for the path selection
|
|
vbox_left.pack_start( menu_toolbar(), false, false )
|
|
hbox_path = Gtk::HBox.new(false, 0)
|
|
vbox_left.pack_start(hbox_path, false, true, 0)
|
|
local_label_path = Gtk::Label.new("Local Path :")
|
|
hbox_path.pack_start(local_label_path, false, false, 0)
|
|
@local_path = Gtk::Entry.new
|
|
@local_path.set_text(@parent_local)
|
|
hbox_path.pack_start(@local_path, true, true, 0)
|
|
@local_path.signal_connect('activate') do
|
|
local_ls(@local_path.text)
|
|
end
|
|
|
|
# Add the view in the scrolled window
|
|
sw_local = Gtk::ScrolledWindow.new
|
|
sw_local.shadow_type = Gtk::SHADOW_ETCHED_IN
|
|
sw_local.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
|
vbox_left.pack_start(sw_local, true, true, 0)
|
|
|
|
@iconview_local = Gtk::IconView.new(@model_local)
|
|
@iconview_local.selection_mode = Gtk::SELECTION_MULTIPLE
|
|
@iconview_local.orientation = Gtk::ORIENTATION_VERTICAL
|
|
@iconview_local.text_column = COL_DISPLAY_NAME
|
|
@iconview_local.pixbuf_column = COL_PIXBUF
|
|
@iconview_local.signal_connect("item_activated") do |iview, path|
|
|
iter = @model_local.get_iter(path)
|
|
if ( iter[COL_DISPLAY_NAME] and iter[COL_IS_DIR] )
|
|
local_ls(@parent_local + File::SEPARATOR + iter[COL_DISPLAY_NAME])
|
|
end
|
|
end
|
|
# Enable drag'n drop if Gtk+ 2.8.0
|
|
if @iconview_local.respond_to?(:enable_model_drag_source)
|
|
setup_drop(@iconview_local)
|
|
setup_drag_local()
|
|
end
|
|
|
|
#
|
|
# Remote iconview
|
|
#
|
|
# Label, Entry and Signal for the path selection
|
|
vbox_right.pack_start( menu_toolbar("meterpreter"), false, false )
|
|
hbox_path = Gtk::HBox.new(false, 0)
|
|
vbox_right.pack_start(hbox_path, false, true, 0)
|
|
label_path = Gtk::Label.new("Remote Path :")
|
|
hbox_path.pack_start(label_path, false, false, 0)
|
|
@remote_path = Gtk::Entry.new
|
|
@remote_path.set_text(@client.fs.dir.getwd)
|
|
hbox_path.pack_start(@remote_path, true, true, 0)
|
|
@remote_path.signal_connect('activate') do
|
|
remote_ls(@remote_path.text)
|
|
end
|
|
|
|
# Add the view in the scrolled window
|
|
sw_remote = Gtk::ScrolledWindow.new
|
|
sw_remote.shadow_type = Gtk::SHADOW_ETCHED_IN
|
|
sw_remote.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
|
vbox_right.pack_start(sw_remote, true, true, 0)
|
|
|
|
@iconview_remote = Gtk::IconView.new(@model_remote)
|
|
@iconview_remote.selection_mode = Gtk::SELECTION_MULTIPLE
|
|
@iconview_remote.text_column = COL_DISPLAY_NAME
|
|
@iconview_remote.pixbuf_column = COL_PIXBUF
|
|
@iconview_remote.signal_connect("item_activated") do |iview, path|
|
|
iter = @model_remote.get_iter(path)
|
|
if ( iter[COL_DISPLAY_NAME] and iter[COL_IS_DIR] )
|
|
remote_ls(@parent_remote + "\\" + iter[COL_DISPLAY_NAME])
|
|
end
|
|
end
|
|
# Enable drag'n drop if Gtk+ 2.8.0
|
|
if @iconview_remote.respond_to?(:enable_model_drag_source)
|
|
setup_drop(@iconview_remote)
|
|
setup_drag_remote()
|
|
end
|
|
|
|
sw_remote.add(@iconview_remote)
|
|
sw_local.add(@iconview_local)
|
|
@iconview_remote.grab_focus
|
|
|
|
show_all
|
|
end # initialize
|
|
|
|
#
|
|
# Build a toolbar menu
|
|
#
|
|
def menu_toolbar(context="")
|
|
toolbar = Gtk::Toolbar.new
|
|
|
|
up_button = Gtk::ToolButton.new(Gtk::Stock::GO_UP)
|
|
up_button.important = true
|
|
up_button.sensitive = true
|
|
toolbar.insert(-1, up_button)
|
|
up_button.signal_connect("clicked") do
|
|
if (context == "meterpreter")
|
|
parent = dirname_up(@parent_remote)
|
|
remote_ls(parent)
|
|
else
|
|
parent = File.dirname(@parent_local)
|
|
local_ls(parent)
|
|
end
|
|
# up_button.sensitive = @parent != "/"
|
|
end
|
|
home_button = Gtk::ToolButton.new(Gtk::Stock::HOME)
|
|
home_button.important = true
|
|
toolbar.insert(-1, home_button)
|
|
home_button.signal_connect("clicked") do
|
|
if (context == "meterpreter")
|
|
parent = @parent_remote_init
|
|
remote_ls(parent)
|
|
else
|
|
parent = GLib.home_dir
|
|
local_ls(parent)
|
|
end
|
|
up_button.sensitive = true
|
|
end
|
|
|
|
refresh_button = Gtk::ToolButton.new(Gtk::Stock::REFRESH)
|
|
refresh_button.important = true
|
|
toolbar.insert(-1, refresh_button)
|
|
refresh_button.signal_connect("clicked") do
|
|
if (context == "meterpreter")
|
|
remote_ls()
|
|
else
|
|
local_ls()
|
|
end
|
|
up_button.sensitive = true
|
|
|
|
end
|
|
|
|
return toolbar
|
|
end # menu_toolbar
|
|
|
|
#
|
|
# Return an array containing all the selected widgets
|
|
#
|
|
def selected_fs(view)
|
|
fs = []
|
|
view.selected_each do |iconview, path|
|
|
iter = view.model.get_iter(path)
|
|
if iter.get_value(COL_TYPE) == "local"
|
|
fs << iter.get_value(COL_PATH) + File::SEPARATOR + iter.get_value(COL_DISPLAY_NAME)
|
|
else
|
|
fs << iter.get_value(COL_PATH) + "\\" + iter.get_value(COL_DISPLAY_NAME)
|
|
end
|
|
end
|
|
fs.select { |x| x != nil }
|
|
end
|
|
|
|
#
|
|
# Drag stuff for local view
|
|
#
|
|
def setup_drag_local
|
|
Gtk::Drag.source_set(@iconview_local,
|
|
Gdk::Window::BUTTON1_MASK | Gdk::Window::BUTTON2_MASK,
|
|
[UPLOAD_TARGET_TABLE],
|
|
Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
|
|
|
|
@iconview_local.signal_connect("drag_data_get") do |widget, context, selection_data, info, time|
|
|
files = selected_fs(@iconview_local).map do |file| file.to_s end
|
|
unless files.empty?
|
|
selection_data.set(Gdk::Selection::TYPE_STRING, files.join(','))
|
|
end
|
|
end
|
|
end
|
|
|
|
#
|
|
# Drag stuff for remote view
|
|
#
|
|
def setup_drag_remote
|
|
Gtk::Drag.source_set(@iconview_remote,
|
|
Gdk::Window::BUTTON1_MASK | Gdk::Window::BUTTON2_MASK,
|
|
[DOWNLOAD_TARGET_TABLE],
|
|
Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
|
|
|
|
@iconview_remote.signal_connect("drag_data_get") do |widget, context, selection_data, info, time|
|
|
files = selected_fs(@iconview_remote).map do |file| file.to_s end
|
|
unless files.empty?
|
|
selection_data.set(Gdk::Selection::TYPE_STRING, files.join(','))
|
|
end
|
|
end
|
|
end
|
|
|
|
#
|
|
# Drop stuff
|
|
#
|
|
def setup_drop(view)
|
|
Gtk::Drag.dest_set(view,
|
|
Gtk::Drag::DEST_DEFAULT_MOTION | Gtk::Drag::DEST_DEFAULT_HIGHLIGHT,
|
|
[UPLOAD_TARGET_TABLE, DOWNLOAD_TARGET_TABLE],
|
|
Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
|
|
|
|
view.signal_connect("drag-data-received") do |w, dc, x, y, selectiondata, info, time|
|
|
dc.targets.each do |target|
|
|
if selectiondata.type == Gdk::Selection::TYPE_STRING
|
|
if target.name == "DOWNLOAD"
|
|
cmd_download(selectiondata.data)
|
|
local_ls
|
|
elsif target.name == "UPLOAD"
|
|
cmd_upload(selectiondata.data)
|
|
remote_ls
|
|
end
|
|
end
|
|
end
|
|
end
|
|
view.signal_connect("drag-drop") do |w, dc, x, y, time|
|
|
Gtk::Drag.get_data(w, dc, dc.targets[0], time)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Return files widgets specified by the given directory on the remote machine
|
|
#
|
|
def remote_ls(*args)
|
|
raise NotImplementedError, "Subclass must implement remote_ls()"
|
|
end # remote_ls
|
|
|
|
#
|
|
# Return files widgets specified by the given directory on the local machine
|
|
#
|
|
def local_ls(*args)
|
|
begin
|
|
@model_local.clear
|
|
path = args[0] || @parent_local
|
|
path = dirname(path)
|
|
@local_path.set_text(path)
|
|
|
|
Dir.entries(path).each do |file|
|
|
if FileTest.directory?(path + File::SEPARATOR + file)
|
|
is_dir = true
|
|
else
|
|
is_dir = false
|
|
end
|
|
iter = @model_local.append
|
|
iter[COL_DISPLAY_NAME] = GLib.filename_to_utf8(file)
|
|
iter[COL_PATH] = path
|
|
iter[COL_IS_DIR] = is_dir
|
|
iter[COL_PIXBUF] = is_dir ? @folder_pixbuf : @file_pixbuf
|
|
iter[COL_TYPE] = "local"
|
|
end
|
|
@parent_local = path
|
|
|
|
# If not possible return a *warning***
|
|
rescue ::Exception => e
|
|
MsfDialog::Warning.new(self, "Local Browser", e.to_s)
|
|
local_ls
|
|
end
|
|
end #local_ls
|
|
|
|
#
|
|
# Dummy function for download method
|
|
#
|
|
def cmd_download
|
|
raise NotImplementedError, "Subclass must implement cmd_download()"
|
|
end
|
|
|
|
#
|
|
# Dummy function for upload method
|
|
#
|
|
def cmd_upload
|
|
raise NotImplementedError, "Subclass must implement cmd_upload()"
|
|
end
|
|
|
|
#
|
|
# Parsing for local entry
|
|
#
|
|
def dirname(path)
|
|
sep = File::SEPARATOR
|
|
path =~ /(.*)#{sep}(.*)#{sep}(.|..)$/
|
|
|
|
if ($3 == ".")
|
|
return $1 + sep + $2
|
|
elsif ($3 == "..")
|
|
return $1
|
|
else
|
|
return path
|
|
end
|
|
end # dirname
|
|
|
|
#
|
|
# Parsing for remote entry
|
|
#
|
|
def dirname_meter(path)
|
|
path =~ /(.*)\\(.*)\\(.|..)$/
|
|
|
|
if ($3 == ".")
|
|
return $1 + "\\" + $2
|
|
elsif ($3 == "..")
|
|
return $1
|
|
else
|
|
return path
|
|
end
|
|
end # dirname_meter
|
|
|
|
#
|
|
# Parsing for remote entry (up button)
|
|
#
|
|
def dirname_up(path)
|
|
path =~ /(.*)\\(.*)$/
|
|
|
|
return $1 || path
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|