diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2017-08-06 22:27:09 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2017-08-06 22:28:12 +0200 |
| commit | ea8148077fa28964e3b582cd6e0bb9cfea850b66 (patch) | |
| tree | 1c8679be716830d6f1835aaa50d7f18b55528c1e /src | |
| parent | 178bb3a1ceab88f29aa7d0ceb453e76de172fd27 (diff) | |
Add support for protocols in monitor-gui
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 14 | ||||
| -rw-r--r-- | src/gui_gtk.cc | 177 | ||||
| -rw-r--r-- | src/gui_main.hh | 1 | ||||
| -rw-r--r-- | src/gui_progress.hh | 38 | ||||
| -rw-r--r-- | src/gui_qt.cc | 160 | ||||
| -rw-r--r-- | src/monitor-gui.cc | 146 |
6 files changed, 495 insertions, 41 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3ace3ea..1579db3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,7 +74,7 @@ libmonitor_gui_a_SOURCES = monitor-gui.cc gui_config.cc gui_config.hh \ gui_about.hh gui_file.hh gui_main.hh \ gui_window.hh gui_formapply.hh gui_menu.hh \ gui_statusbar.hh gui_form.hh gui_listmodel.hh \ - gui_message.hh gui_textwnd.hh + gui_message.hh gui_textwnd.hh gui_progress.hh libmonitor_gui_a_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \ @THREAD_CFLAGS@ @PCAP_CFLAGS@ @@ -90,9 +90,9 @@ protocol_LDADD = libprotocol.a libhexdump.a libattrstr.a libtp.a @THREAD_LIBS@ \ @ZLIB_LIBS@ @BZIP2_LIBS@ tp_monitor_gtk_SOURCES = gui_gtk.cc -tp_monitor_gtk_LDADD = libmonitor_gui.a libattrstr.a libproxy.a libmonitor.a \ - libhexdump.a libtp.a @GTK_LIBS@ @THREAD_LIBS@ \ - @PCAP_LIBS@ +tp_monitor_gtk_LDADD = libmonitor_gui.a libprotocol.a libattrstr.a libproxy.a \ + libmonitor.a libhexdump.a libtp.a @GTK_LIBS@ \ + @THREAD_LIBS@ @PCAP_LIBS@ @ZLIB_LIBS@ @BZIP2_LIBS@ if HAVE_SSL tp_monitor_gtk_LDADD += libmitm.a @SSL_LIBS@ endif @@ -100,9 +100,9 @@ tp_monitor_gtk_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \ @GTK_CFLAGS@ @THREAD_CFLAGS@ -Wno-unused-function tp_monitor_qt_SOURCES = gui_qt.cc -tp_monitor_qt_LDADD = libmonitor_gui.a libattrstr.a libmonitor.a libproxy.a \ - libhexdump.a libtp.a @QT_LIBS@ @THREAD_LIBS@ \ - @PCAP_LIBS@ +tp_monitor_qt_LDADD = libmonitor_gui.a libprotocol.a libattrstr.a libmonitor.a \ + libhexdump.a libproxy.a libtp.a @QT_LIBS@ @THREAD_LIBS@ \ + @PCAP_LIBS@ @ZLIB_LIBS@ @BZIP2_LIBS@ if HAVE_SSL tp_monitor_qt_LDADD += libmitm.a @SSL_LIBS@ endif diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc index 20a6147..c02bdf6 100644 --- a/src/gui_gtk.cc +++ b/src/gui_gtk.cc @@ -17,6 +17,7 @@ #include "gui_main.hh" #include "gui_menu.hh" #include "gui_message.hh" +#include "gui_progress.hh" #include "gui_statusbar.hh" #include "gui_textwnd.hh" #include "looper.hh" @@ -635,6 +636,16 @@ public: return package_.get(); } + void set_content(std::string const& name, AttributedText* text) override; + + AttributedText* content() const { + return content_; + } + + std::string const& content_name() const { + return content_name_; + } + ListModel* gtklistmodel() const { return listmodel_.get(); } @@ -789,6 +800,8 @@ private: shared_gobject<MainApp> app_; shared_gobject<ListModel> listmodel_; std::unique_ptr<AttributedText> package_; + AttributedText* content_; + std::string content_name_; std::unique_ptr<GtkConfig> config_; Observers<GuiMain::Listener*> observers_; GuiMenu* menu_; @@ -1777,6 +1790,109 @@ protected: gulong changed_handler_; }; +class GtkGuiProgress : public virtual GuiProgress, public GtkGuiWindow { +public: + GtkGuiProgress(std::string const& title, std::string const& text, + float min, float max) + : title_(title), text_(text), min_(min), max_(max), value_(min), + dialog_(nullptr), progress_(nullptr) { + } + + ~GtkGuiProgress() override { + if (dialog_) gtk_widget_destroy(dialog_); + } + + void set_title(std::string const& title) override { + title_ = title; + GtkGuiWindow::set_title(title); + } + std::string const& title() const override { + return title_; + } + + void* impl() const override { + return dialog_; + } + + void add_listener(GuiProgress::Listener* listener) override { + observers_.insert(listener); + } + + void remove_listener(GuiProgress::Listener* listener) override { + observers_.erase(listener); + } + + void set_progress(float value) override { + value_ = value; + if (progress_) { + sync_value(); + } + } + + void show(GuiWindow* parent) override { + if (dialog_) { + gtk_window_present(GTK_WINDOW(dialog_)); + assert(false); + return; + } + + dialog_ = gtk_dialog_new(); + g_signal_connect(G_OBJECT(dialog_), "delete-event", + G_CALLBACK(delete_event), this); + gtk_window_set_title(GTK_WINDOW(dialog_), title_.c_str()); + gtk_window_set_modal(GTK_WINDOW(dialog_), true); + auto wnd = reinterpret_cast<GtkWindow*>(parent->impl()); + gtk_window_set_transient_for(GTK_WINDOW(dialog_), wnd); + auto label = gtk_label_new(text_.c_str()); + progress_ = gtk_progress_bar_new(); + sync_value(); + auto content = gtk_dialog_get_content_area(GTK_DIALOG(dialog_)); + gtk_box_pack_start(GTK_BOX(content), label, true, true, 5); + gtk_box_pack_end(GTK_BOX(content), progress_, true, true, 5); + gtk_widget_show_all(dialog_); + } + +private: + void sync_value() { + assert(progress_); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_), + (value_ - min_) / (max_ - min_)); + } + + bool notify_about_to_close() { + auto it = observers_.notify(); + while (it.has_next()) { + if (!it.next()->about_to_close(this)) return false; + } + return true; + } + + static gboolean delete_event(GtkWidget* widget, GdkEvent*, + gpointer user_data) { + auto me = reinterpret_cast<GtkGuiProgress*>(user_data); + assert(me->dialog_ == widget); + me->dialog_ = nullptr; + auto progress = me->progress_; + me->progress_ = nullptr; + if (me->notify_about_to_close()) { + gtk_widget_destroy(widget); + return false; + } + me->dialog_ = widget; + me->progress_ = progress; + return true; + } + + std::string title_; + std::string text_; + float min_; + float max_; + float value_; + Observers<GuiProgress::Listener*> observers_; + GtkWidget* dialog_; + GtkWidget* progress_; +}; + struct _MainApp { GtkApplication parent_; @@ -1789,6 +1905,8 @@ struct _MainAppWindow GtkWidget* paned_; GtkWidget* top_; GtkWidget* bottom_; + GtkWidget* bottom_content_; + GtkWidget* bottom_package_; }; G_DEFINE_TYPE(MainApp, main_app, GTK_TYPE_APPLICATION); @@ -1836,17 +1954,32 @@ MainAppWindow* main_app_window_new(MainApp *app) { gtk_container_add(GTK_CONTAINER(top_scroll), ret->top_); gtk_paned_add1(GTK_PANED(ret->paned_), top_scroll); } - ret->bottom_ = gtk_text_view_new(); - gtk_text_view_set_editable(GTK_TEXT_VIEW(ret->bottom_), false); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ret->bottom_), false); - gtk_text_view_set_monospace(GTK_TEXT_VIEW(ret->bottom_), true); + ret->bottom_package_ = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(ret->bottom_package_), false); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ret->bottom_package_), false); + gtk_text_view_set_monospace(GTK_TEXT_VIEW(ret->bottom_package_), true); if (app->main_->package()) { - gtk_text_view_set_buffer(GTK_TEXT_VIEW(ret->bottom_), + gtk_text_view_set_buffer(GTK_TEXT_VIEW(ret->bottom_package_), static_cast<GtkAttributedText*>(app->main_->package())->buffer()); } - auto bottom_scroll = gtk_scrolled_window_new(nullptr, nullptr); - gtk_container_add(GTK_CONTAINER(bottom_scroll), ret->bottom_); - gtk_paned_add2(GTK_PANED(ret->paned_), bottom_scroll); + ret->bottom_content_ = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(ret->bottom_content_), false); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ret->bottom_content_), false); + gtk_text_view_set_monospace(GTK_TEXT_VIEW(ret->bottom_content_), true); + if (app->main_->content()) { + gtk_text_view_set_buffer(GTK_TEXT_VIEW(ret->bottom_content_), + static_cast<GtkAttributedText*>(app->main_->content())->buffer()); + } + ret->bottom_ = gtk_notebook_new(); + auto scroll = gtk_scrolled_window_new(nullptr, nullptr); + gtk_container_add(GTK_CONTAINER(scroll), ret->bottom_package_); + auto label = gtk_label_new("Package"); + gtk_notebook_append_page(GTK_NOTEBOOK(ret->bottom_), scroll, label); + scroll = gtk_scrolled_window_new(nullptr, nullptr); + gtk_container_add(GTK_CONTAINER(scroll), ret->bottom_content_); + label = gtk_label_new(app->main_->content_name().c_str()); + gtk_notebook_append_page(GTK_NOTEBOOK(ret->bottom_), scroll, label); + gtk_paned_add2(GTK_PANED(ret->paned_), ret->bottom_); gtk_box_pack_start(GTK_BOX(box), ret->paned_, true, true, 0); auto statusbar = static_cast<GtkGuiStatusBar*>(app->main_->statusbar()); if (statusbar) { @@ -1989,11 +2122,30 @@ void GtkGuiMain::set_package(std::unique_ptr<AttributedText>&& text) { } else { buf = gtk_text_buffer_new(NULL); } - gtk_text_view_set_buffer(GTK_TEXT_VIEW(wnd->bottom_), buf); + gtk_text_view_set_buffer(GTK_TEXT_VIEW(wnd->bottom_package_), buf); if (!package_) g_object_unref(buf); } } +void GtkGuiMain::set_content(std::string const& name, AttributedText* text) { + content_name_ = name; + content_ = text; + auto wnd = reinterpret_cast<MainAppWindow*>(window()); + if (wnd) { + GtkTextBuffer* buf; + if (content_) { + buf = static_cast<GtkAttributedText*>(content_)->buffer(); + } else { + buf = gtk_text_buffer_new(NULL); + } + gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(wnd->bottom_), + gtk_widget_get_parent(wnd->bottom_content_), + content_name_.c_str()); + gtk_text_view_set_buffer(GTK_TEXT_VIEW(wnd->bottom_content_), buf); + if (!content_) g_object_unref(buf); + } +} + void GtkGuiMain::set_split(double split) { split_ = std::max(0.0, std::min(split, 1.0)); auto wnd = reinterpret_cast<MainAppWindow*>(window()); @@ -2449,3 +2601,10 @@ GuiTextWindow* GuiTextWindow::create(std::string const& title, AttributedText const* text) { return new GtkGuiTextWindow(title, width, height, text); } + +// static +GuiProgress* GuiProgress::create(std::string const& title, + std::string const& text, + float min, float max) { + return new GtkGuiProgress(title, text, min, max); +} diff --git a/src/gui_main.hh b/src/gui_main.hh index 1070c0d..ab8499d 100644 --- a/src/gui_main.hh +++ b/src/gui_main.hh @@ -51,6 +51,7 @@ public: virtual void select_row(size_t row) = 0; virtual void set_package(std::unique_ptr<AttributedText>&& data) = 0; + virtual void set_content(std::string const& name, AttributedText* data) = 0; virtual bool run(int argc, char** argv) = 0; diff --git a/src/gui_progress.hh b/src/gui_progress.hh new file mode 100644 index 0000000..2c92693 --- /dev/null +++ b/src/gui_progress.hh @@ -0,0 +1,38 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#ifndef GUI_PROGRESS_HH +#define GUI_PROGRESS_HH + +#include <string> +#include <vector> + +#include "gui_window.hh" + +class GuiProgress : public GuiWindow { +public: + class Listener : public GuiWindow::Listener { + public: + virtual ~Listener() {} + + virtual bool about_to_close(GuiProgress* progress) = 0; + + protected: + Listener() {} + }; + + static GuiProgress* create(std::string const& title, + std::string const& text, + float min, float max); + + virtual void add_listener(Listener* listener) = 0; + virtual void remove_listener(Listener* listener) = 0; + + virtual void show(GuiWindow* parent) = 0; + + virtual void set_progress(float progress) = 0; + +protected: + GuiProgress() {} +}; + +#endif // GUI_PROGRESS_HH diff --git a/src/gui_qt.cc b/src/gui_qt.cc index fe9d1c4..1d81bd6 100644 --- a/src/gui_qt.cc +++ b/src/gui_qt.cc @@ -48,6 +48,7 @@ #include "gui_main.hh" #include "gui_menu.hh" #include "gui_message.hh" +#include "gui_progress.hh" #include "gui_statusbar.hh" #include "gui_textwnd.hh" #include "looper.hh" @@ -522,8 +523,27 @@ public: } else { package_.clear(); } - if (bottom_) { - bottom_->setHtml(QString::fromStdString(package_)); + if (bottom_package_) { + bottom_package_->setHtml(QString::fromStdString(package_)); + } + } + + void set_content(std::string const& name, AttributedText* text) override { + if (text) { + content_ = "<span style=\"font-family: monospace;\">" + + static_cast<HtmlAttributedText*>(text)->html() + "</span>"; + content_name_ = name; + } else { + content_.clear(); + content_name_.clear(); + } + if (bottom_content_) { + bottom_content_->setHtml(QString::fromStdString(content_)); + bottom_->setTabText(1, QString::fromStdString(content_name_)); + bottom_->setTabEnabled(1, !content_.empty()); + if (text && content_last_active_) { + bottom_->setCurrentIndex(1); + } } } @@ -658,6 +678,8 @@ private: QtGuiStatusBar* statusbar_; std::unique_ptr<QGuiListModel> listmodel_; std::string package_; + std::string content_; + std::string content_name_; Observers<GuiMain::Listener*> observers_; std::unique_ptr<QMainWindow> main_; std::unique_ptr<SizeHintLayout> layout_; @@ -665,9 +687,12 @@ private: std::unique_ptr<QtGuiConfig> config_; QSplitter* splitter_; QWidget* top_; - QTextEdit* bottom_; + QTextEdit* bottom_package_; + QTextEdit* bottom_content_; + QTabWidget* bottom_; bool started_; std::vector<RunWhenStartedCallback> run_when_started_; + bool content_last_active_; }; class QtChildGuiMenu : public QtCommonMenu { @@ -1408,8 +1433,7 @@ private: } assert(!progress_); progress_ = new QProgressBar(); - progress_->setMinimum(0); - progress_->setMaximum(0); + progress_->setRange(0, 0); layout->addWidget(progress_, row++, 0, 1, columns); progress_->hide(); return row; @@ -1677,6 +1701,106 @@ private: QTextEdit* view_; }; + +class QtGuiProgress : public virtual GuiProgress, public QtGuiWindow, + public virtual OptionalCloseDialog::Delegate { +private: + static const int UNIT = 100000; +public: + QtGuiProgress(std::string const& title, std::string const& text, + float min, float max) + : title_(title), text_(text), min_(min), max_(max), value_(min), + progress_(nullptr) { + } + + ~QtGuiProgress() override { + } + + void add_listener(GuiProgress::Listener* listener) override { + observers_.insert(listener); + } + + void remove_listener(GuiProgress::Listener* listener) override { + observers_.erase(listener); + } + + void set_title(std::string const& title) override { + title_ = title; + QtGuiWindow::set_title(title); + } + std::string const& title() const override { + return title_; + } + + void set_progress(float value) override { + value_ = value; + if (progress_) progress_->setValue(value_ * UNIT); + } + + QWidget* widget() const override { + return dialog_.get(); + } + + void* impl() const override { + return QtGuiWindow::impl(); + } + + bool showWidget() override { + show(QApplication::activeWindow()); + return true; + } + + void show(GuiWindow* parent) override { + auto wnd = static_cast<QtGuiWindow*>(parent->impl()); + show(wnd->widget()); + } + + bool about_to_close() override { + return notify_about_to_close(); + } + +private: + void show(QWidget* parent) { + if (dialog_) { + assert(false); + dialog_->raise(); + dialog_->activateWindow(); + return; + } + + progress_ = new QProgressBar(); + progress_->setRange(min_ * UNIT, max_ * UNIT); + progress_->setValue(value_ * UNIT); + + dialog_.reset(new OptionalCloseDialog(parent, this)); + dialog_->setWindowTitle(QString::fromStdString(title_)); + auto layout = new QVBoxLayout(); + auto text = new QLabel(QString::fromStdString(text_)); + layout->addWidget(text); + layout->addWidget(progress_); + dialog_->setLayout(layout); + dialog_->setModal(true); + dialog_->show(); + } + + bool notify_about_to_close() { + auto it = observers_.notify(); + while (it.has_next()) { + if (!it.next()->about_to_close(this)) return false; + } + return true; + } + + std::string title_; + std::string text_; + float min_; + float max_; + float value_; + Observers<GuiProgress::Listener*> observers_; + std::unique_ptr<QDialog> dialog_; + QProgressBar* progress_; +}; + bool QtGuiMain::run(int argc, char** argv) { QApplication app(argc, argv); app.setApplicationName(QString::fromStdString(title_)); @@ -1722,9 +1846,22 @@ bool QtGuiMain::run(int argc, char** argv) { top_ = new QWidget(); } splitter_->addWidget(top_); - bottom_ = new QTextEdit(); - bottom_->setReadOnly(true); - bottom_->setHtml(QString::fromStdString(package_)); + bottom_package_ = new QTextEdit(); + bottom_package_->setReadOnly(true); + bottom_package_->setHtml(QString::fromStdString(package_)); + bottom_content_ = new QTextEdit(); + bottom_content_->setReadOnly(true); + bottom_content_->setHtml(QString::fromStdString(content_)); + bottom_ = new QTabWidget(); + bottom_->addTab(bottom_package_, "Package"); + bottom_->addTab(bottom_content_, QString::fromStdString(content_name_)); + bottom_->setTabEnabled(1, !content_.empty()); + content_last_active_ = false; + QObject::connect(bottom_, &QTabWidget::tabBarClicked, + [=](int index) { + if (index < 0) return; + content_last_active_ = index == 1; + }); splitter_->addWidget(bottom_); main_->setCentralWidget(center_.get()); if (menu_) { @@ -1978,3 +2115,10 @@ GuiTextWindow* GuiTextWindow::create(std::string const& title, AttributedText const* text) { return new QtGuiTextWindow(title, width, height, text); } + +// static +GuiProgress* GuiProgress::create(std::string const& title, + std::string const& text, + float min, float max) { + return new QtGuiProgress(title, text, min, max); +} diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc index 66c5e43..3301ada 100644 --- a/src/monitor-gui.cc +++ b/src/monitor-gui.cc @@ -5,6 +5,7 @@ #include <fcntl.h> #include <fstream> #include <memory> +#include <sstream> #include <stdarg.h> #include <string.h> #include <sys/socket.h> @@ -30,6 +31,7 @@ #include "gui_menu.hh" #include "gui_message.hh" #include "gui_main.hh" +#include "gui_progress.hh" #include "gui_statusbar.hh" #include "gui_textwnd.hh" #include "io.hh" @@ -37,12 +39,15 @@ #include "looper.hh" #include "monitor.hh" #include "observers.hh" +#include "protocols.hh" #include "proxy.hh" #include "resolver.hh" #include "ssl.hh" namespace { +size_t const MAX_DATA_SIZE = 65536; + std::string const APP_TITLE = "TransparentProxy"; std::string const ACTION_SETUP = "setup"; @@ -50,6 +55,7 @@ std::string const ACTION_CONNECT = "connect"; std::string const ACTION_DISCONNECT = "disconnect"; std::string const ACTION_EXIT = "exit"; std::string const ACTION_ABOUT = "about"; +std::string const ACTION_COPY_CONTENT = "copy_content"; std::string const ACTION_COPY_RAW = "copy_raw"; std::string const ACTION_COPY_TEXT = "copy_text"; std::string const ACTION_CLEAR = "clear"; @@ -62,6 +68,7 @@ std::string const ACTION_OPEN = "open"; std::string const ACTION_SAVE = "save"; std::string const ACTION_SAVE_AS = "save_as"; std::string const ACTION_JUMP = "jump"; +std::string const ACTION_EXPORT_CONTENT = "export_content"; std::string const ACTION_EXPORT_RAW = "export_raw"; std::string const ACTION_EXPORT_TEXT = "export_text"; @@ -389,7 +396,7 @@ private: }; class MonitorGui : GuiMenu::Listener, GuiMain::Listener, Monitor::Delegate, - GuiTextWindow::Listener { + GuiTextWindow::Listener, Protocols::Listener { private: class ConnectFormListener : public GuiFormApply::Listener { public: @@ -667,6 +674,13 @@ private: }; #endif // HAVE_SSL + class DoNotCloseListener : public GuiProgress::Listener { + public: + bool about_to_close(GuiProgress*) override { + return false; + } + }; + public: MonitorGui() : packages_(new PackageList()), @@ -674,6 +688,7 @@ public: menu_(GuiMenu::create()), statusbar_(GuiStatusBar::create()), looper_(main_->create_looper()), + protocols_(Protocols::create(4, MAX_DATA_SIZE, 10, looper_.get(), this)), has_selection_(false), has_related_(false), selection_(0), @@ -743,11 +758,15 @@ public: GuiMenu::Shortcut(GuiMenu::CTRL, 'C')); edit->add_item(ACTION_COPY_RAW, "Copy binary", GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::SHIFT, 'C')); + edit->add_item(ACTION_COPY_CONTENT, "Copy content", + GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::ALT, 'C')); edit->add_separator(); edit->add_item(ACTION_EXPORT_TEXT, "Export...", GuiMenu::Shortcut(GuiMenu::CTRL, 'E')); edit->add_item(ACTION_EXPORT_RAW, "Export binary...", GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::SHIFT, 'E')); + edit->add_item(ACTION_EXPORT_CONTENT, "Export content...", + GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::ALT, 'E')); edit->add_separator(); edit->add_item(ACTION_JUMP, "Jump to related", GuiMenu::Shortcut(GuiMenu::CTRL, ' ')); @@ -770,11 +789,7 @@ public: statusbar_->set_status("Not connected"); menu_->enable_item(ACTION_DISCONNECT, false); - menu_->enable_item(ACTION_COPY_RAW, false); - menu_->enable_item(ACTION_COPY_TEXT, false); - menu_->enable_item(ACTION_EXPORT_RAW, false); - menu_->enable_item(ACTION_EXPORT_TEXT, false); - menu_->enable_item(ACTION_JUMP, false); + lost_selection(main_.get()); menu_->add_listener(this); main_->add_listener(this); @@ -894,6 +909,13 @@ public: } auto& pkg = packages_->package(selection_); main_->add_to_clipboard(pkg.data, "application/octet-stream"); + } else if (id == ACTION_COPY_CONTENT) { + if (!has_selection_ || !selection_content_) { + assert(false); + return; + } + setup_export_content_msg(); + protocols_->content(selection_, &export_content_sstream_); } else if (id == ACTION_EXPORT_TEXT) { if (!has_selection_) { assert(false); @@ -907,6 +929,17 @@ public: } auto& pkg = packages_->package(selection_); export_text(pkg.data, raw_filter_); + } else if (id == ACTION_EXPORT_CONTENT) { + if (!has_selection_ || !selection_content_) { + assert(false); + return; + } + auto file = main_->file_dialog("Export", "", GuiFile::FILE_SAVE, + raw_filter_); + if (file.empty()) return; + export_content_fstream_.open(file); + setup_export_content_msg(); + protocols_->content(selection_, &export_content_fstream_); } else if (id == ACTION_JUMP) { if (!has_selection_) { assert(false); @@ -919,7 +952,7 @@ public: } main_->select_row(pkg.related); } else if (id == ACTION_CLEAR) { - packages_->clear(); + clear(); set_modified(true); } else if (id == ACTION_PROXY_LOG) { if (!proxy_logwnd_) { @@ -976,32 +1009,46 @@ public: void selected_row(GuiMain* main, size_t index) override { assert(main_.get() == main); - if (has_selection_ && selection_ == index) return; + if (selection_content_) { + main_->set_content("", nullptr); + protocols_->free(selection_, std::move(selection_content_)); + } has_selection_ = true; selection_ = index; auto& pkg = packages_->package(index); std::unique_ptr<AttributedText> text(AttributedText::create()); - HexDump::write(text.get(), HexDump::ADDRESS | HexDump::CHARS, pkg.data); + HexDump::write(text.get(), HexDump::ADDRESS | HexDump::CHARS, + pkg.data, 0, MAX_DATA_SIZE); main_->set_package(std::move(text)); has_related_ = pkg.related != PackageList::NONE; + menu_->enable_item(ACTION_COPY_CONTENT, false); menu_->enable_item(ACTION_COPY_RAW, true); menu_->enable_item(ACTION_COPY_TEXT, true); + menu_->enable_item(ACTION_EXPORT_CONTENT, false); menu_->enable_item(ACTION_EXPORT_RAW, true); menu_->enable_item(ACTION_EXPORT_TEXT, true); menu_->enable_item(ACTION_JUMP, has_related_); + + protocols_->text(selection_); } void lost_selection(GuiMain* main) override { assert(main_.get() == main); + if (selection_content_) { + main_->set_content("", nullptr); + protocols_->free(selection_, std::move(selection_content_)); + } has_selection_ = false; has_related_ = false; main_->set_package(nullptr); + menu_->enable_item(ACTION_COPY_CONTENT, false); menu_->enable_item(ACTION_COPY_RAW, false); menu_->enable_item(ACTION_COPY_TEXT, false); + menu_->enable_item(ACTION_EXPORT_CONTENT, false); menu_->enable_item(ACTION_EXPORT_RAW, false); menu_->enable_item(ACTION_EXPORT_TEXT, false); menu_->enable_item(ACTION_JUMP, false); @@ -1010,11 +1057,7 @@ public: void open(GuiMain*, std::string const& file) override { if (abort_if_modified()) return; file_ = file; - if (!load(file_, packages_.get())) { - file_.clear(); - } - modified_ = false; - update_title(); + finish_load(); } // GuiTextWindow::Listener @@ -1080,6 +1123,7 @@ public: void package(Monitor* monitor, ::Package const& package) override { assert(monitor == monitor_.get()); packages_->package(package); + protocols_->add(packages_->rows(), nullptr, 0); set_modified(true); if (has_selection_ && !has_related_) { auto const& pkg = packages_->package(selection_); @@ -1091,12 +1135,54 @@ public: char const* data, size_t size, bool last) override { assert(monitor == monitor_.get()); auto index = packages_->package_data(id, data, size, last); + auto const& pkg = packages_->package(index); + protocols_->update(index, pkg.data.data(), pkg.data.size()); if (has_selection_ && index == selection_) { - selected_row(nullptr, index); + selected_row(main_.get(), index); } set_modified(true); } + // Protocols::Listener + void text(Protocols* protocols, size_t id, std::string const& protocol, + std::unique_ptr<AttributedText>&& text) override { + assert(protocols == protocols_.get()); + if (id >= packages_->rows()) { + assert(false); + protocols_->free(id, std::move(text)); + protocols_->remove(id); + return; + } + if (!has_selection_ || selection_ != id) { + protocols_->free(id, std::move(text)); + return; + } + selection_content_.swap(text); + main_->set_content(protocol, selection_content_.get()); + menu_->enable_item(ACTION_COPY_CONTENT, true); + menu_->enable_item(ACTION_EXPORT_CONTENT, true); + } + + void content(Protocols* protocols, size_t id, std::string const&, + std::ostream* out) override { + assert(protocols == protocols_.get()); + if (id >= packages_->rows()) { + assert(false); + protocols_->remove(id); + return; + } + if (out == &export_content_sstream_) { + main_->add_to_clipboard(export_content_sstream_.str(), + "application/octet-stream"); + export_content_sstream_.clear(); + } else if (out == &export_content_fstream_) { + export_content_fstream_.close(); + } else { + assert(false); + } + export_content_dialog_.reset(); + } + private: void setup_monitor() { if (!resolver_) { @@ -1146,11 +1232,16 @@ private: } } + void clear() { + protocols_->clear(); + packages_->clear(); + } + void new_file() { if (abort_if_modified()) return; file_.clear(); modified_ = false; - packages_->clear(); + clear(); update_title(); } @@ -1159,11 +1250,19 @@ private: file_ = main_->file_dialog("Open File", "", GuiFile::FILE_OPEN, file_filter_); if (file_.empty()) return; - packages_->clear(); + clear(); + + finish_load(); + } + void finish_load() { if (!load(file_, packages_.get())) { file_.clear(); } + for (size_t i = 0; i < packages_->rows(); ++i) { + auto const& pkg = packages_->package(i); + protocols_->add(i, pkg.data.data(), pkg.data.size()); + } modified_ = false; update_title(); } @@ -1567,6 +1666,14 @@ private: return true; } + void setup_export_content_msg() { + export_content_dialog_.reset(GuiProgress::create("Exporting...", + "Please wait...", + .0f, .0f)); + export_content_dialog_->add_listener(new DoNotCloseListener()); + export_content_dialog_->show(main_.get()); + } + std::unique_ptr<PackageList> packages_; std::unique_ptr<GuiMain> main_; std::unique_ptr<GuiMenu> menu_; @@ -1580,11 +1687,16 @@ private: std::unique_ptr<StringLogger> proxy_logger_; std::unique_ptr<Proxy> proxy_; std::unique_ptr<GuiTextWindow> proxy_logwnd_; + std::unique_ptr<Protocols> protocols_; std::vector<GuiFile::Filter> pem_filter_; std::vector<GuiFile::Filter> crt_filter_; bool has_selection_; bool has_related_; size_t selection_; + std::unique_ptr<AttributedText> selection_content_; + std::unique_ptr<GuiProgress> export_content_dialog_; + std::stringstream export_content_sstream_; + std::ofstream export_content_fstream_; std::vector<GuiFile::Filter> file_filter_; std::string file_; bool modified_; |
