Qt: fix use-after-free on error while saving exported packets
[metze/wireshark/wip.git] / ui / qt / main_window_slots.cpp
index 40b338a0e6cbf669e7c62c52b15a9e1a637f42a6..5ffed4b0a52baad3b53aa64f81f5be8ba8c81083 100644 (file)
@@ -4,19 +4,7 @@
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <config.h>
@@ -89,6 +77,7 @@ DIAG_ON(frame-larger-than=)
 
 #include <ui/qt/utils/variant_pointer.h>
 #include <ui/qt/widgets/drag_drop_toolbar.h>
+#include "ui/qt/widgets/wireshark_file_dialog.h"
 
 #ifdef HAVE_SOFTWARE_UPDATE
 #include "ui/software_update.h"
@@ -118,9 +107,7 @@ DIAG_ON(frame-larger-than=)
 #include "export_object_action.h"
 #include "export_object_dialog.h"
 #include "export_pdu_dialog.h"
-#ifdef HAVE_EXTCAP
 #include "extcap_options_dialog.h"
-#endif
 #include "file_set_dialog.h"
 #include "filter_action.h"
 #include "filter_dialog.h"
@@ -178,7 +165,7 @@ DIAG_ON(frame-larger-than=)
 
 // XXX You must uncomment QT_WINEXTRAS_LIB lines in CMakeList.txt and
 // cmakeconfig.h.in.
-// #if defined(QT_WINEXTRAS_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+// #if defined(QT_WINEXTRAS_LIB)
 // #include <QWinJumpList>
 // #include <QWinJumpListCategory>
 // #include <QWinJumpListItem>
@@ -223,7 +210,7 @@ bool MainWindow::openCaptureFile(QString cf_path, QString read_filter, unsigned
             goto finish;
         }
 
-        if (dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
+        if (dfilter_compile(qUtf8Printable(read_filter), &rfcode, &err_msg)) {
             cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
         } else {
             /* Not valid.  Tell the user, and go back and run the file
@@ -249,7 +236,7 @@ bool MainWindow::openCaptureFile(QString cf_path, QString read_filter, unsigned
 
         /* Try to open the capture file. This closes the current file if it succeeds. */
         CaptureFile::globalCapFile()->window = this;
-        if (cf_open(CaptureFile::globalCapFile(), cf_path.toUtf8().constData(), type, is_tempfile, &err) != CF_OK) {
+        if (cf_open(CaptureFile::globalCapFile(), qUtf8Printable(cf_path), type, is_tempfile, &err) != CF_OK) {
             /* We couldn't open it; don't dismiss the open dialog box,
                just leave it around so that the user can, after they
                dismiss the alert box popped up for the open error,
@@ -514,7 +501,7 @@ void MainWindow::layoutToolbars()
         AdditionalToolBar *iftoolbar = dynamic_cast<AdditionalToolBar *>(bar);
         if (iftoolbar) {
             bool visible = false;
-            if (g_list_find_custom(recent.gui_additional_toolbars, iftoolbar->menuName().toUtf8().constData(), (GCompareFunc) strcmp))
+            if (g_list_find_custom(recent.gui_additional_toolbars, qUtf8Printable(iftoolbar->menuName()), (GCompareFunc) strcmp))
                 visible = true;
 
             iftoolbar->setVisible(visible);
@@ -714,10 +701,7 @@ void MainWindow::captureCaptureUpdateFinished(capture_session *) {
     }
 #endif // HAVE_LIBPCAP
 }
-void MainWindow::captureCaptureFixedStarted(capture_session *) {
-#ifdef HAVE_LIBPCAP
-#endif // HAVE_LIBPCAP
-}
+
 void MainWindow::captureCaptureFixedFinished(capture_session *) {
 #ifdef HAVE_LIBPCAP
 
@@ -741,13 +725,7 @@ void MainWindow::captureCaptureFixedFinished(capture_session *) {
     }
 #endif // HAVE_LIBPCAP
 }
-void MainWindow::captureCaptureStopping(capture_session *) {
-#ifdef HAVE_LIBPCAP
 
-    capture_stopping_ = true;
-    setMenusForCaptureStopping();
-#endif // HAVE_LIBPCAP
-}
 void MainWindow::captureCaptureFailed(capture_session *) {
 #ifdef HAVE_LIBPCAP
     /* Capture isn't stopping any more. */
@@ -770,9 +748,148 @@ void MainWindow::captureCaptureFailed(capture_session *) {
 #endif // HAVE_LIBPCAP
 }
 
-
 // Callbacks from cfile.c and file.c via CaptureFile::captureFileCallback
 
+void MainWindow::captureEventHandler(CaptureEvent ev)
+{
+    switch (ev.captureContext()) {
+
+    case CaptureEvent::File:
+        switch (ev.eventType()) {
+        case CaptureEvent::Opened:
+            captureFileOpened();
+            break;
+        case CaptureEvent::Closing:
+            captureFileClosing();
+            break;
+        case CaptureEvent::Closed:
+            captureFileClosed();
+            break;
+        case CaptureEvent::Started:
+            captureFileReadStarted(tr("Loading"));
+            break;
+        case CaptureEvent::Finished:
+            captureFileReadFinished();
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Reload:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+            captureFileReadStarted(tr("Reloading"));
+            break;
+        case CaptureEvent::Finished:
+            captureFileReadFinished();
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Rescan:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+            setMenusForCaptureFile(true);
+            captureFileReadStarted(tr("Rescanning"));
+            break;
+        case CaptureEvent::Finished:
+            captureFileReadFinished();
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Retap:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+            freeze();
+            break;
+        case CaptureEvent::Finished:
+            thaw();
+            break;
+        case CaptureEvent::Flushed:
+            draw_tap_listeners(FALSE);
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Merge:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+            main_ui_->statusBar->popFileStatus();
+            main_ui_->statusBar->pushFileStatus(tr("Merging files"), QString());
+            break;
+        case CaptureEvent::Finished:
+            main_ui_->statusBar->popFileStatus();
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Save:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+        {
+            QFileInfo file_info(ev.filePath());
+            main_ui_->statusBar->popFileStatus();
+            main_ui_->statusBar->pushFileStatus(tr("Saving %1" UTF8_HORIZONTAL_ELLIPSIS).arg(file_info.baseName()));
+            break;
+        }
+        default:
+            break;
+        }
+        break;
+
+#ifdef HAVE_LIBPCAP
+    case CaptureEvent::Capture:
+        switch (ev.eventType()) {
+        case CaptureEvent::Prepared:
+            captureCapturePrepared(ev.capSession());
+            break;
+        case CaptureEvent::Stopping:
+            capture_stopping_ = true;
+            setMenusForCaptureStopping();
+            break;
+        case CaptureEvent::Failed:
+            captureCaptureFailed(ev.capSession());
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Update:
+        switch (ev.eventType()) {
+        case CaptureEvent::Started:
+            captureCaptureUpdateStarted(ev.capSession());
+            break;
+        case CaptureEvent::Finished:
+            captureCaptureUpdateFinished(ev.capSession());
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case CaptureEvent::Fixed:
+        switch (ev.eventType()) {
+        case CaptureEvent::Finished:
+            captureCaptureFixedFinished(ev.capSession());
+            break;
+        default:
+            break;
+        }
+        break;
+#endif
+    }
+}
+
 void MainWindow::captureFileOpened() {
     if (capture_file_.window() != this) return;
 
@@ -828,35 +945,6 @@ void MainWindow::captureFileReadFinished() {
     emit setDissectedCaptureFile(capture_file_.capFile());
 }
 
-void MainWindow::captureFileRetapStarted()
-{
-    // XXX Push a status message?
-    freeze();
-}
-
-void MainWindow::captureFileRetapFinished()
-{
-    thaw();
-}
-
-void MainWindow::captureFileMergeStarted()
-{
-    main_ui_->statusBar->popFileStatus();
-    QString msg = tr("Merging files");
-    QString msgtip = QString();
-    main_ui_->statusBar->pushFileStatus(msg, msgtip);
-}
-
-void MainWindow::captureFileMergeFinished()
-{
-    main_ui_->statusBar->popFileStatus();
-}
-
-void MainWindow::captureFileFlushTapsData()
-{
-    draw_tap_listeners(FALSE);
-}
-
 void MainWindow::captureFileClosing() {
     setMenusForCaptureFile(true);
     setForCapturedPackets(false);
@@ -893,13 +981,6 @@ void MainWindow::captureFileClosed() {
 #endif
 }
 
-void MainWindow::captureFileSaveStarted(const QString &file_path)
-{
-    QFileInfo file_info(file_path);
-    main_ui_->statusBar->popFileStatus();
-    main_ui_->statusBar->pushFileStatus(tr("Saving %1" UTF8_HORIZONTAL_ELLIPSIS).arg(file_info.baseName()));
-}
-
 struct filter_expression_data
 {
     MainWindow* window;
@@ -929,6 +1010,11 @@ gboolean MainWindow::filter_expression_add_action(const void *key _U_, void *val
     dfb_action->setProperty(dfe_property_label_, QString(fe->label));
     dfb_action->setProperty(dfe_property_expression_, QString(fe->expression));
 
+    if (data->actions_added) {
+        QFrame *sep = new QFrame();
+        sep->setEnabled(false);
+        data->window->filter_expression_toolbar_->addWidget(sep);
+    }
     data->window->filter_expression_toolbar_->addAction(dfb_action);
     connect(dfb_action, SIGNAL(triggered()), data->window, SLOT(displayFilterButtonClicked()));
     data->actions_added = true;
@@ -942,14 +1028,17 @@ void MainWindow::filterExpressionsChanged()
     data.window = this;
     data.actions_added = false;
 
+    // Hiding and showing seems to be the only way to get the layout to
+    // work correctly in some cases. See bug 14121 for details.
+    setUpdatesEnabled(false);
+    filter_expression_toolbar_->hide();
     filter_expression_toolbar_->clear();
 
     // XXX Add a context menu for removing and changing buttons.
     filter_expression_iterate_expressions(filter_expression_add_action, &data);
 
-    if (data.actions_added) {
-        main_ui_->displayFilterToolBar->adjustSize();
-    }
+    filter_expression_toolbar_->show();
+    setUpdatesEnabled(true);
 }
 
 //
@@ -997,6 +1086,7 @@ void MainWindow::startCapture() {
     collect_ifaces(&global_capture_opts);
 
     CaptureFile::globalCapFile()->window = this;
+    info_data_.ui.ui = this;
     if (capture_start(&global_capture_opts, &cap_session_, &info_data_, main_window_update)) {
         capture_options *capture_opts = cap_session_.capture_opts;
         GString *interface_names;
@@ -1157,15 +1247,17 @@ void MainWindow::updateRecentCaptures() {
     }
     recentMenu->clear();
 
-// #if defined(QT_WINEXTRAS_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
-//     QWinJumpList recent_jl(this);
-//     QWinJumpListCategory *recent_jlc = recent_jl.recent();
-//     if (recent_jlc) {
-//         recent_jlc->clear();
-//         recent_jlc->setVisible(true);
-//     }
-// #endif
-#if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+#if 0
+#if defined(QT_WINEXTRAS_LIB)
+     QWinJumpList recent_jl(this);
+     QWinJumpListCategory *recent_jlc = recent_jl.recent();
+     if (recent_jlc) {
+         recent_jlc->clear();
+         recent_jlc->setVisible(true);
+     }
+#endif
+#endif
+#if defined(Q_OS_MAC)
     if (!dock_menu_) {
         dock_menu_ = new QMenu();
         dock_menu_->setAsDockMenu();
@@ -1191,22 +1283,24 @@ void MainWindow::updateRecentCaptures() {
         ra->setText(action_cf_name);
         connect(ra, SIGNAL(triggered()), this, SLOT(recentActionTriggered()));
 
-// This is slow, at least on my VM here. The added links also open Wireshark
-// in a new window. It might make more sense to add a recent item when we
-// open a capture file.
-// #if defined(QT_WINEXTRAS_LIB) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
-//     if (recent_jlc) {
-//         QFileInfo fi(ri->filename);
-//         QWinJumpListItem *jli = recent_jlc->addLink(
-//             fi.fileName(),
-//             QApplication::applicationFilePath(),
-//             QStringList() << "-r" << ri->filename
-//         );
-//         // XXX set icon
-//         jli->setWorkingDirectory(QDir::toNativeSeparators(QApplication::applicationDirPath()));
-//     }
-// #endif
-#if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+/* This is slow, at least on my VM here. The added links also open Wireshark
+ * in a new window. It might make more sense to add a recent item when we
+ * open a capture file. */
+#if 0
+#if defined(QT_WINEXTRAS_LIB)
+     if (recent_jlc) {
+         QFileInfo fi(ri->filename);
+         QWinJumpListItem *jli = recent_jlc->addLink(
+             fi.fileName(),
+             QApplication::applicationFilePath(),
+             QStringList() << "-r" << ri->filename
+         );
+         // XXX set icon
+         jli->setWorkingDirectory(QDir::toNativeSeparators(QApplication::applicationDirPath()));
+     }
+#endif
+#endif
+#if defined(Q_OS_MAC)
         QAction *rda = new QAction(dock_menu_);
         QFileInfo fi(ri->filename);
         rda->setText(fi.fileName());
@@ -1290,14 +1384,14 @@ void MainWindow::setMenusForSelectedPacket()
         next_selection_history = packet_list_->haveNextHistory();
         previous_selection_history = packet_list_->havePreviousHistory();
         have_frames = capture_file_.capFile()->count > 0;
-        have_marked = frame_selected && capture_file_.capFile()->marked_count > 0;
+        have_marked = capture_file_.capFile()->marked_count > 0;
         another_is_marked = have_marked &&
-                !(capture_file_.capFile()->marked_count == 1 && capture_file_.capFile()->current_frame->flags.marked);
+                !(capture_file_.capFile()->marked_count == 1 && frame_selected && capture_file_.capFile()->current_frame->flags.marked);
         have_filtered = capture_file_.capFile()->displayed_count > 0 && capture_file_.capFile()->displayed_count != capture_file_.capFile()->count;
         have_ignored = capture_file_.capFile()->ignored_count > 0;
         have_time_ref = capture_file_.capFile()->ref_time_count > 0;
-        another_is_time_ref = frame_selected && have_time_ref &&
-                !(capture_file_.capFile()->ref_time_count == 1 && capture_file_.capFile()->current_frame->flags.ref_time);
+        another_is_time_ref = have_time_ref &&
+                !(capture_file_.capFile()->ref_time_count == 1 && frame_selected && capture_file_.capFile()->current_frame->flags.ref_time);
 
         if (capture_file_.capFile()->edt)
         {
@@ -1317,11 +1411,8 @@ void MainWindow::setMenusForSelectedPacket()
     main_ui_->actionEditNextMark->setEnabled(another_is_marked);
     main_ui_->actionEditPreviousMark->setEnabled(another_is_marked);
 
-#ifdef WANT_PACKET_EDITOR
-//    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/EditPacket",
-//                         frame_selected);
-#endif // WANT_PACKET_EDITOR
     main_ui_->actionEditPacketComment->setEnabled(frame_selected && wtap_dump_can_write(capture_file_.capFile()->linktypes, WTAP_COMMENT_PER_PACKET));
+    main_ui_->actionDeleteAllPacketComments->setEnabled((capture_file_.capFile() != NULL) && wtap_dump_can_write(capture_file_.capFile()->linktypes, WTAP_COMMENT_PER_PACKET));
 
     main_ui_->actionEditIgnorePacket->setEnabled(frame_selected);
     main_ui_->actionEditIgnoreAllDisplayed->setEnabled(have_filtered);
@@ -1462,10 +1553,11 @@ void MainWindow::setMenusForSelectedTreeRow(FieldInformation *finfo) {
     main_ui_->actionEditCopyValue->setEnabled(can_match_selected);
     main_ui_->actionEditCopyAsFilter->setEnabled(can_match_selected);
 
-    main_ui_->actionContextShowPacketBytes->setEnabled(have_packet_bytes);
+    main_ui_->actionAnalyzeShowPacketBytes->setEnabled(have_packet_bytes);
     main_ui_->actionFileExportPacketBytes->setEnabled(have_packet_bytes);
 
     main_ui_->actionViewExpandSubtrees->setEnabled(have_subtree);
+    main_ui_->actionViewCollapseSubtrees->setEnabled(have_subtree);
 
     main_ui_->actionGoGoToLinkedPacket->setEnabled(is_framenum);
 
@@ -1744,12 +1836,8 @@ void MainWindow::on_actionDisplayFilterExpression_triggered()
 void MainWindow::on_actionNewDisplayFilterExpression_triggered()
 {
     main_ui_->filterExpressionFrame->addExpression(df_combo_box_->lineEdit()->text());
-    showAccordionFrame(main_ui_->filterExpressionFrame);
 }
 
-// On Qt4 + macOS with unifiedTitleAndToolBarOnMac set it's possible to make
-// the main window obnoxiously wide.
-
 void MainWindow::displayFilterButtonClicked()
 {
     QAction *dfb_action = qobject_cast<QAction*>(sender());
@@ -1901,7 +1989,7 @@ void MainWindow::on_actionFileExportPacketBytes_triggered()
 
     if (!capture_file_.capFile() || !capture_file_.capFile()->finfo_selected) return;
 
-    file_name = QFileDialog::getSaveFileName(this,
+    file_name = WiresharkFileDialog::getSaveFileName(this,
                                              wsApp->windowTitleString(tr("Export Selected Packet Bytes")),
                                              wsApp->lastOpenDir().canonicalPath(),
                                              tr("Raw data (*.bin *.dat *.raw);;All Files (" ALL_FILES_WILDCARD ")")
@@ -1913,27 +2001,27 @@ void MainWindow::on_actionFileExportPacketBytes_triggered()
 
         data_p = tvb_get_ptr(capture_file_.capFile()->finfo_selected->ds_tvb, 0, -1) +
                 capture_file_.capFile()->finfo_selected->start;
-        fd = ws_open(file_name.toUtf8().constData(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
+        fd = ws_open(qUtf8Printable(file_name), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
         if (fd == -1) {
-            open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE);
+            open_failure_alert_box(qUtf8Printable(file_name), errno, TRUE);
             return;
         }
         if (ws_write(fd, data_p, capture_file_.capFile()->finfo_selected->length) < 0) {
-            write_failure_alert_box(file_name.toUtf8().constData(), errno);
+            write_failure_alert_box(qUtf8Printable(file_name), errno);
             ws_close(fd);
             return;
         }
         if (ws_close(fd) < 0) {
-            write_failure_alert_box(file_name.toUtf8().constData(), errno);
+            write_failure_alert_box(qUtf8Printable(file_name), errno);
             return;
         }
 
         /* Save the directory name for future file dialogs. */
-        wsApp->setLastOpenDir(&file_name);
+        wsApp->setLastOpenDir(file_name);
     }
 }
 
-void MainWindow::on_actionContextShowPacketBytes_triggered()
+void MainWindow::on_actionAnalyzeShowPacketBytes_triggered()
 {
     ShowPacketBytesDialog *spbd = new ShowPacketBytesDialog(*this, capture_file_);
     spbd->show();
@@ -1976,7 +2064,7 @@ void MainWindow::on_actionFileExportSSLSessionKeys_triggered()
     }
 
     save_title.append(wsApp->windowTitleString(tr("Export SSL Session Keys (%Ln key(s))", "", keylist_len)));
-    file_name = QFileDialog::getSaveFileName(this,
+    file_name = WiresharkFileDialog::getSaveFileName(this,
                                              save_title,
                                              wsApp->lastOpenDir().canonicalPath(),
                                              tr("SSL Session Keys (*.keys *.txt);;All Files (" ALL_FILES_WILDCARD ")")
@@ -1986,9 +2074,9 @@ void MainWindow::on_actionFileExportSSLSessionKeys_triggered()
         int fd;
 
         keylist = ssl_export_sessions();
-        fd = ws_open(file_name.toUtf8().constData(), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
+        fd = ws_open(qUtf8Printable(file_name), O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
         if (fd == -1) {
-            open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE);
+            open_failure_alert_box(qUtf8Printable(file_name), errno, TRUE);
             g_free(keylist);
             return;
         }
@@ -1997,19 +2085,19 @@ void MainWindow::on_actionFileExportSSLSessionKeys_triggered()
          * _write().  Presumably this string will be <= 4GiB long....
          */
         if (ws_write(fd, keylist, (unsigned int)strlen(keylist)) < 0) {
-            write_failure_alert_box(file_name.toUtf8().constData(), errno);
+            write_failure_alert_box(qUtf8Printable(file_name), errno);
             ws_close(fd);
             g_free(keylist);
             return;
         }
         if (ws_close(fd) < 0) {
-            write_failure_alert_box(file_name.toUtf8().constData(), errno);
+            write_failure_alert_box(qUtf8Printable(file_name), errno);
             g_free(keylist);
             return;
         }
 
         /* Save the directory name for future file dialogs. */
-        wsApp->setLastOpenDir(&file_name);
+        wsApp->setLastOpenDir(file_name);
         g_free(keylist);
     }
 }
@@ -2021,25 +2109,15 @@ void MainWindow::on_actionStatisticsHpfeeds_triggered()
 
 void MainWindow::on_actionFilePrint_triggered()
 {
-    PrintDialog pdlg(this, capture_file_.capFile());
+    capture_file *cf = capture_file_.capFile();
+    g_return_if_fail(cf);
 
+    PrintDialog pdlg(this, cf);
     pdlg.exec();
 }
 
 // Edit Menu
 
-void MainWindow::recursiveCopyProtoTreeItems(QTreeWidgetItem *item, QString &clip, int ident_level) {
-    if (!item->isExpanded()) return;
-
-    for (int i_item = 0; i_item < item->childCount(); i_item += 1) {
-        clip.append(QString("    ").repeated(ident_level));
-        clip.append(item->child(i_item)->text(0));
-        clip.append("\n");
-
-        recursiveCopyProtoTreeItems(item->child(i_item), clip, ident_level + 1);
-    }
-}
-
 // XXX This should probably be somewhere else.
 void MainWindow::actionEditCopyTriggered(MainWindow::CopySelected selection_type)
 {
@@ -2070,20 +2148,11 @@ void MainWindow::actionEditCopyTriggered(MainWindow::CopySelected selection_type
         }
         break;
     case CopyAllVisibleItems:
-        for (int i_item = 0; i_item < proto_tree_->topLevelItemCount(); i_item += 1) {
-            clip.append(proto_tree_->topLevelItem(i_item)->text(0));
-            clip.append("\n");
-
-            recursiveCopyProtoTreeItems(proto_tree_->topLevelItem(i_item), clip, 1);
-        }
-
+        clip = proto_tree_->toString();
         break;
     case CopyAllVisibleSelectedTreeItems:
-        if (proto_tree_->selectedItems().count() > 0) {
-            clip.append(proto_tree_->currentItem()->text(0));
-            clip.append("\n");
-
-            recursiveCopyProtoTreeItems(proto_tree_->currentItem(), clip, 1);
+        if (proto_tree_->selectionModel()->hasSelection()) {
+            clip = proto_tree_->toString(proto_tree_->selectionModel()->selectedIndexes().first());
         }
         break;
     }
@@ -2160,6 +2229,7 @@ void MainWindow::on_actionEditMarkPacket_triggered()
     freeze();
     packet_list_->markFrame();
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditMarkAllDisplayed_triggered()
@@ -2167,6 +2237,7 @@ void MainWindow::on_actionEditMarkAllDisplayed_triggered()
     freeze();
     packet_list_->markAllDisplayedFrames(true);
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditUnmarkAllDisplayed_triggered()
@@ -2174,6 +2245,7 @@ void MainWindow::on_actionEditUnmarkAllDisplayed_triggered()
     freeze();
     packet_list_->markAllDisplayedFrames(false);
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditNextMark_triggered()
@@ -2193,6 +2265,7 @@ void MainWindow::on_actionEditIgnorePacket_triggered()
     freeze();
     packet_list_->ignoreFrame();
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditIgnoreAllDisplayed_triggered()
@@ -2200,6 +2273,7 @@ void MainWindow::on_actionEditIgnoreAllDisplayed_triggered()
     freeze();
     packet_list_->ignoreAllDisplayedFrames(true);
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditUnignoreAllDisplayed_triggered()
@@ -2207,16 +2281,19 @@ void MainWindow::on_actionEditUnignoreAllDisplayed_triggered()
     freeze();
     packet_list_->ignoreAllDisplayedFrames(false);
     thaw();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditSetTimeReference_triggered()
 {
     packet_list_->setTimeReference();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditUnsetAllTimeReferences_triggered()
 {
     packet_list_->unsetAllTimeReferences();
+    setMenusForSelectedPacket();
 }
 
 void MainWindow::on_actionEditNextTimeReference_triggered()
@@ -2249,34 +2326,38 @@ void MainWindow::on_actionEditPacketComment_triggered()
     }
 }
 
-void MainWindow::on_actionEditConfigurationProfiles_triggered()
+void MainWindow::on_actionDeleteAllPacketComments_triggered()
 {
-    ProfileDialog cp_dialog;
+    QMessageBox msg_dialog;
 
-    cp_dialog.exec();
-}
+    msg_dialog.setIcon(QMessageBox::Question);
+    msg_dialog.setText(tr("Are you sure you want to remove all packet comments?"));
 
-void MainWindow::showPreferencesDialog(PreferencesDialog::PreferencesPane start_pane)
-{
-    PreferencesDialog pref_dialog(this);
+    msg_dialog.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+    msg_dialog.setDefaultButton(QMessageBox::Ok);
 
-    saveWindowGeometry();  // Save in case the layout panes are rearranged
+    if (msg_dialog.exec() == QMessageBox::Ok)
+    {
+        /* XXX Do we need a wait/hourglass for large files? */
+        packet_list_->deleteAllPacketComments();
+        updateForUnsavedChanges();
+    }
+}
 
-    pref_dialog.setPane(start_pane);
-    pref_dialog.exec();
+void MainWindow::on_actionEditConfigurationProfiles_triggered()
+{
+    ProfileDialog cp_dialog;
 
-    // Emitting PacketDissectionChanged directly from a QDialog can cause
-    // problems on macOS.
-    wsApp->flushAppSignals();
+    cp_dialog.exec();
 }
 
-void MainWindow::showPreferencesDialog(QString module_name)
+void MainWindow::showPreferencesDialog(QString pane_name)
 {
     PreferencesDialog pref_dialog(this);
 
     saveWindowGeometry();  // Save in case the layout panes are rearranged
 
-    pref_dialog.setPane(module_name);
+    pref_dialog.setPane(pane_name);
     pref_dialog.exec();
 
     // Emitting PacketDissectionChanged directly from a QDialog can cause
@@ -2286,7 +2367,7 @@ void MainWindow::showPreferencesDialog(QString module_name)
 
 void MainWindow::on_actionEditPreferences_triggered()
 {
-    showPreferencesDialog();
+    showPreferencesDialog(PrefsModel::APPEARANCE_PREFERENCE_TREE_NAME);
 }
 
 // View Menu
@@ -2485,7 +2566,7 @@ void MainWindow::on_actionViewNormalSize_triggered()
 
 void MainWindow::on_actionViewColorizePacketList_triggered(bool checked) {
     recent.packet_list_colorize = checked;
-    packet_list_enable_color(checked);
+    packet_list_recolor_packets();
     packet_list_->packetListModel()->resetColorized();
 }
 
@@ -2619,7 +2700,7 @@ void MainWindow::openPacketDialog(bool from_reference)
         if (framenum == 0)
             return;
 
-        fdata = frame_data_sequence_find(capture_file_.capFile()->frames, framenum);
+        fdata = frame_data_sequence_find(capture_file_.capFile()->provider.frames, framenum);
     } else {
         fdata = capture_file_.capFile()->current_frame;
     }
@@ -2789,7 +2870,11 @@ void MainWindow::applyExportObject()
     if (!export_action)
         return;
 
-    new ExportObjectDialog(*this, capture_file_, export_action->exportObject());
+    ExportObjectDialog* export_dialog = new ExportObjectDialog(*this, capture_file_, export_action->exportObject());
+
+    connect(export_dialog->getExportObjectView(), SIGNAL(goToPacket(int)),
+            packet_list_, SLOT(goToPacket(int)));
+
 }
 
 // XXX We could probably create the analyze and prepare actions
@@ -2886,13 +2971,19 @@ void MainWindow::on_actionAnalyzeReloadLuaPlugins_triggered()
     reloadLuaPlugins();
 }
 
-void MainWindow::openFollowStreamDialog(follow_type_t type) {
+void MainWindow::openFollowStreamDialog(follow_type_t type, int stream_num) {
     FollowStreamDialog *fsd = new FollowStreamDialog(*this, capture_file_, type);
     connect(fsd, SIGNAL(updateFilter(QString, bool)), this, SLOT(filterPackets(QString, bool)));
     connect(fsd, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int)));
 
     fsd->show();
-    fsd->follow(getFilter());
+    if (stream_num >= 0) {
+        // If a specific conversation was requested, then ignore any previous
+        // display filters and display all related packets.
+        fsd->follow("", true, stream_num);
+    } else {
+        fsd->follow(getFilter());
+    }
 }
 
 void MainWindow::on_actionAnalyzeFollowTCPStream_triggered()
@@ -2993,7 +3084,7 @@ void MainWindow::statCommandExpertInfo(const char *, void *)
 
     expert_dialog->setDisplayFilter(df_edit->text());
 
-    connect(expert_dialog, SIGNAL(goToPacket(int, int)),
+    connect(expert_dialog->getExpertInfoView(), SIGNAL(goToPacket(int, int)),
             packet_list_, SLOT(goToPacket(int, int)));
     connect(expert_dialog, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
             this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)));
@@ -3196,8 +3287,8 @@ void MainWindow::statCommandConversations(const char *arg, void *userdata)
     ConversationDialog *conv_dialog = new ConversationDialog(*this, capture_file_, GPOINTER_TO_INT(userdata), arg);
     connect(conv_dialog, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
             this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)));
-    connect(conv_dialog, SIGNAL(openFollowStreamDialog(follow_type_t)),
-            this, SLOT(openFollowStreamDialog(follow_type_t)));
+    connect(conv_dialog, SIGNAL(openFollowStreamDialog(follow_type_t,int)),
+            this, SLOT(openFollowStreamDialog(follow_type_t,int)));
     connect(conv_dialog, SIGNAL(openTcpStreamGraph(int)),
             this, SLOT(openTcpStreamDialog(int)));
     conv_dialog->show();
@@ -3246,6 +3337,11 @@ void MainWindow::on_actionStatisticsHTTPLoadDistribution_triggered()
     openStatisticsTreeDialog("http_srv");
 }
 
+void MainWindow::on_actionStatisticsHTTPRequestSequences_triggered()
+{
+    openStatisticsTreeDialog("http_seq");
+}
+
 void MainWindow::on_actionStatisticsPacketLengths_triggered()
 {
     openStatisticsTreeDialog("plen");
@@ -3844,7 +3940,6 @@ void MainWindow::gotoFrame(int packet_num)
     }
 }
 
-#ifdef HAVE_EXTCAP
 void MainWindow::extcap_options_finished(int result)
 {
     if (result == QDialog::Accepted) {
@@ -3856,6 +3951,7 @@ void MainWindow::extcap_options_finished(int result)
 void MainWindow::showExtcapOptionsDialog(QString &device_name)
 {
     ExtcapOptionsDialog * extcap_options_dialog = ExtcapOptionsDialog::createForDevice(device_name, this);
+    extcap_options_dialog->setModal(true);
     /* The dialog returns null, if the given device name is not a valid extcap device */
     if (extcap_options_dialog) {
         connect(extcap_options_dialog, SIGNAL(finished(int)),
@@ -3863,91 +3959,6 @@ void MainWindow::showExtcapOptionsDialog(QString &device_name)
         extcap_options_dialog->show();
     }
 }
-#endif
-
-void MainWindow::on_actionContextCopyBytesHexTextDump_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_HexDump, fieldInfo);
-}
-
-void MainWindow::on_actionContextCopyBytesHexDump_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_HexOnly, fieldInfo);
-}
-
-void MainWindow::on_actionContextCopyBytesPrintableText_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_PrintableText, fieldInfo);
-}
-
-void MainWindow::on_actionContextCopyBytesHexStream_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_HexStream, fieldInfo);
-}
-
-void MainWindow::on_actionContextCopyBytesBinary_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_Binary, fieldInfo);
-}
-
-void MainWindow::on_actionContextCopyBytesEscapedString_triggered()
-{
-    QAction *ca = qobject_cast<QAction*>(sender());
-    if (!ca) return;
-
-    IDataPrintable * fieldInfo =
-            VariantPointer<IDataPrintable>::asPtr(ca->property("idataprintable_"));
-    if ( ! fieldInfo )
-        return;
-
-    DataPrinter printer;
-    printer.toClipboard(DataPrinter::DP_EscapedString, fieldInfo);
-}
 
 void MainWindow::on_actionContextWikiProtocolPage_triggered()
 {
@@ -4063,7 +4074,7 @@ void MainWindow::filterToolbarCustomMenuHandler(const QPoint& pos)
 
 void MainWindow::filterToolbarShowPreferences()
 {
-    emit showPreferencesDialog(PreferencesDialog::ppFilterExpressions);
+    emit showPreferencesDialog(PrefsModel::FILTER_BUTTONS_PREFERENCE_TREE_NAME);
 }
 
 int MainWindow::uatRowIndexForFilterExpression(QString label, QString expression)
@@ -4121,8 +4132,8 @@ void MainWindow::filterDropped(QString description, QString filter)
     if ( filter.length() == 0 )
         return;
 
-    filter_expression_new(description.toUtf8().constData(),
-            filter.toUtf8().constData(), description.toUtf8().constData(), TRUE);
+    filter_expression_new(qUtf8Printable(description),
+            qUtf8Printable(filter), qUtf8Printable(description), TRUE);
 
     uat_save(uat_get_table_by_name("Display expressions"), &err);
     g_free(err);