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