summaryrefslogtreecommitdiffstats
path: root/ui/qt/utils
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/utils')
-rw-r--r--ui/qt/utils/color_utils.cpp216
-rw-r--r--ui/qt/utils/color_utils.h92
-rw-r--r--ui/qt/utils/data_printer.cpp264
-rw-r--r--ui/qt/utils/data_printer.h60
-rw-r--r--ui/qt/utils/field_information.cpp214
-rw-r--r--ui/qt/utils/field_information.h76
-rw-r--r--ui/qt/utils/frame_information.cpp103
-rw-r--r--ui/qt/utils/frame_information.h57
-rw-r--r--ui/qt/utils/idata_printable.h34
-rw-r--r--ui/qt/utils/proto_node.cpp166
-rw-r--r--ui/qt/utils/proto_node.h63
-rw-r--r--ui/qt/utils/qt_ui_utils.cpp347
-rw-r--r--ui/qt/utils/qt_ui_utils.h280
-rw-r--r--ui/qt/utils/rtp_audio_file.cpp384
-rw-r--r--ui/qt/utils/rtp_audio_file.h93
-rw-r--r--ui/qt/utils/rtp_audio_routing.cpp110
-rw-r--r--ui/qt/utils/rtp_audio_routing.h55
-rw-r--r--ui/qt/utils/rtp_audio_routing_filter.cpp114
-rw-r--r--ui/qt/utils/rtp_audio_routing_filter.h42
-rw-r--r--ui/qt/utils/rtp_audio_silence_generator.cpp47
-rw-r--r--ui/qt/utils/rtp_audio_silence_generator.h35
-rw-r--r--ui/qt/utils/stock_icon.cpp253
-rw-r--r--ui/qt/utils/stock_icon.h40
-rw-r--r--ui/qt/utils/tango_colors.h75
-rw-r--r--ui/qt/utils/variant_pointer.h34
-rw-r--r--ui/qt/utils/wireshark_mime_data.cpp52
-rw-r--r--ui/qt/utils/wireshark_mime_data.h47
-rw-r--r--ui/qt/utils/wireshark_zip_helper.cpp283
-rw-r--r--ui/qt/utils/wireshark_zip_helper.h36
29 files changed, 3672 insertions, 0 deletions
diff --git a/ui/qt/utils/color_utils.cpp b/ui/qt/utils/color_utils.cpp
new file mode 100644
index 00000000..e7d7c6c5
--- /dev/null
+++ b/ui/qt/utils/color_utils.cpp
@@ -0,0 +1,216 @@
+/* color_utils.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/utils/color_utils.h>
+#include <ui/qt/utils/tango_colors.h>
+
+#include <QApplication>
+#include <QPalette>
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+#include <QStyleHints>
+#endif
+
+// Colors we use in various parts of the UI.
+//
+// New colors should be chosen from tango_colors.h. The expert and hidden
+// colors come from the GTK+ UI and are grandfathered in.
+//
+// At some point we should probably make these configurable along with the
+// graph and sequence colors.
+
+const QColor ColorUtils::expert_color_comment = QColor (0xb7, 0xf7, 0x74); /* Green */
+const QColor ColorUtils::expert_color_chat = QColor (0x80, 0xb7, 0xf7); /* Light blue */
+const QColor ColorUtils::expert_color_note = QColor (0xa0, 0xff, 0xff); /* Bright turquoise */
+const QColor ColorUtils::expert_color_warn = QColor (0xf7, 0xf2, 0x53); /* Yellow */
+const QColor ColorUtils::expert_color_error = QColor (0xff, 0x5c, 0x5c); /* Pale red */
+const QColor ColorUtils::expert_color_foreground = QColor (0x00, 0x00, 0x00); /* Black */
+const QColor ColorUtils::hidden_proto_item = QColor (0x44, 0x44, 0x44); /* Gray */
+
+ColorUtils::ColorUtils(QObject *parent) :
+ QObject(parent)
+{
+}
+
+//
+// A color_t has RGB values in [0,65535].
+// Qt RGB colors have RGB values in [0,255].
+//
+// 65535/255 = 257 = 0x0101, so converting from [0,255] to
+// [0,65535] involves just shifting the 8-bit value left 8 bits
+// and ORing them together.
+//
+// Converting from [0,65535] to [0,255] without rounding involves
+// just shifting the 16-bit value right 8 bits; I guess you could
+// round them by adding 0x80 to the value before shifting.
+//
+QColor ColorUtils::fromColorT (const color_t *color) {
+ if (!color) return QColor();
+ // Convert [0,65535] values to [0,255] values
+ return QColor(color->red >> 8, color->green >> 8, color->blue >> 8);
+}
+
+QColor ColorUtils::fromColorT(color_t color)
+{
+ return fromColorT(&color);
+}
+
+const color_t ColorUtils::toColorT(const QColor color)
+{
+ color_t colort;
+
+ // Convert [0,255] values to [0,65535] values
+ colort.red = (color.red() << 8) | color.red();
+ colort.green = (color.green() << 8) | color.green();
+ colort.blue = (color.blue() << 8) | color.blue();
+
+ return colort;
+}
+
+QRgb ColorUtils::alphaBlend(const QColor &color1, const QColor &color2, qreal alpha)
+{
+ alpha = qBound(qreal(0.0), alpha, qreal(1.0));
+
+ int r1 = color1.red() * alpha;
+ int g1 = color1.green() * alpha;
+ int b1 = color1.blue() * alpha;
+ int r2 = color2.red() * (1 - alpha);
+ int g2 = color2.green() * (1 - alpha);
+ int b2 = color2.blue() * (1 - alpha);
+
+ QColor alpha_color(r1 + r2, g1 + g2, b1 + b2);
+ return alpha_color.rgb();
+}
+
+QRgb ColorUtils::alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha)
+{
+ return alphaBlend(brush1.color(), brush2.color(), alpha);
+}
+
+QList<QRgb> ColorUtils::graph_colors_;
+const QList<QRgb> ColorUtils::graphColors()
+{
+ if (graph_colors_.isEmpty()) {
+ // Available graph colors
+ // XXX - Add custom
+ graph_colors_ = QList<QRgb>()
+ << tango_aluminium_6 // Bar outline (use black instead)?
+ << tango_sky_blue_5
+ << tango_butter_6
+ << tango_chameleon_5
+ << tango_scarlet_red_5
+ << tango_plum_5
+ << tango_orange_6
+ << tango_aluminium_3
+ << tango_sky_blue_3
+ << tango_butter_3
+ << tango_chameleon_3
+ << tango_scarlet_red_3
+ << tango_plum_3
+ << tango_orange_3;
+ }
+ return graph_colors_;
+}
+
+QRgb ColorUtils::graphColor(int item)
+{
+ if (graph_colors_.isEmpty()) graphColors(); // Init list.
+ return graph_colors_[item % graph_colors_.size()];
+}
+
+QList<QRgb> ColorUtils::sequence_colors_;
+QRgb ColorUtils::sequenceColor(int item)
+{
+ if (sequence_colors_.isEmpty()) {
+ // Available sequence colors. Copied from gtk/graph_analysis.c.
+ // XXX - Add custom?
+ sequence_colors_ = QList<QRgb>()
+ << qRgb(144, 238, 144)
+ << qRgb(255, 160, 123)
+ << qRgb(255, 182, 193)
+ << qRgb(250, 250, 210)
+ << qRgb(255, 255, 52)
+ << qRgb(103, 205, 170)
+ << qRgb(224, 255, 255)
+ << qRgb(176, 196, 222)
+ << qRgb(135, 206, 254)
+ << qRgb(211, 211, 211);
+ }
+ return sequence_colors_[item % sequence_colors_.size()];
+}
+
+bool ColorUtils::themeIsDark()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ return qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark;
+#else
+ return qApp->palette().windowText().color().lightness() > qApp->palette().window().color().lightness();
+#endif
+}
+
+// Qt < 5.12.6 on macOS always uses Qt::blue for the link color, which is
+// unreadable when using a dark theme. Changing the application palette
+// via ...Application::setPalette is problematic, since QGuiApplication
+// sets a flag (ApplicationPaletteExplicitlySet) which keeps us from
+// catching theme changes.
+//
+// themeLinkBrush and themeLinkStyle provide convenience routines for
+// fetching the link brush and style.
+//
+// We could also override MainApplication::palette, but keeping the
+// routines together here seemed to make more sense.
+QBrush ColorUtils::themeLinkBrush()
+{
+#if QT_VERSION < QT_VERSION_CHECK(5, 12, 6)
+ // https://bugreports.qt.io/browse/QTBUG-71740
+ if (themeIsDark()) {
+ return QBrush(tango_sky_blue_2);
+ }
+#endif
+ return qApp->palette().link();
+}
+
+QString ColorUtils::themeLinkStyle()
+{
+ QString link_style;
+
+ if (themeIsDark()) {
+ link_style = QString("<style>a:link { color: %1; }</style>")
+ .arg(themeLinkBrush().color().name());
+ }
+ return link_style;
+}
+
+const QColor ColorUtils::contrastingTextColor(const QColor color)
+{
+ bool background_is_light = color.lightness() > 127;
+ if ( (background_is_light && !ColorUtils::themeIsDark()) || (!background_is_light && ColorUtils::themeIsDark()) ) {
+ return QApplication::palette().text().color();
+ }
+ return QApplication::palette().base().color();
+}
+
+const QColor ColorUtils::hoverBackground()
+{
+ QPalette hover_palette = QApplication::palette();
+#if defined(Q_OS_MAC)
+ hover_palette.setCurrentColorGroup(QPalette::Active);
+ return hover_palette.highlight().color();
+#else
+ return ColorUtils::alphaBlend(hover_palette.window(), hover_palette.highlight(), 0.5);
+#endif
+}
+
+const QColor ColorUtils::warningBackground()
+{
+ if (themeIsDark()) {
+ return QColor(tango_butter_6);
+ }
+ return QColor(tango_butter_2);
+}
diff --git a/ui/qt/utils/color_utils.h b/ui/qt/utils/color_utils.h
new file mode 100644
index 00000000..a205cccc
--- /dev/null
+++ b/ui/qt/utils/color_utils.h
@@ -0,0 +1,92 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef COLOR_UTILS_H
+#define COLOR_UTILS_H
+
+#include <config.h>
+
+#include <glib.h>
+
+#include <epan/color_filters.h>
+
+#include <QBrush>
+#include <QColor>
+#include <QObject>
+
+class ColorUtils : public QObject
+{
+public:
+ explicit ColorUtils(QObject *parent = 0);
+
+ static QColor fromColorT(const color_t *color);
+ static QColor fromColorT(color_t color);
+ static const color_t toColorT(const QColor color);
+ static QRgb alphaBlend(const QColor &color1, const QColor &color2, qreal alpha);
+ static QRgb alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha);
+
+ // ...because they don't really fit anywhere else?
+ static const QColor expert_color_comment; /* green */
+ static const QColor expert_color_chat; /* light blue */
+ static const QColor expert_color_note; /* bright turquoise */
+ static const QColor expert_color_warn; /* yellow */
+ static const QColor expert_color_error; /* pale red */
+ static const QColor expert_color_foreground; /* black */
+ static const QColor hidden_proto_item; /* gray */
+
+ static const QList<QRgb> graphColors();
+ static QRgb graphColor(int item);
+ static QRgb sequenceColor(int item);
+
+ /** Checks if our application is in "dark mode".
+ * Dark mode is determined by comparing the application palette's window
+ * text color with the window color.
+ *
+ * @return true if we're running in dark mode, false otherwise.
+ */
+ static bool themeIsDark();
+ /**
+ * Returns an appropriate link color for the current mode.
+ * @return A brush suitable for setting a text color.
+ */
+ static QBrush themeLinkBrush();
+ /**
+ * Returns an appropriate HTML+CSS link style for the current mode.
+ * @return A "<style>a:link { color: ... ; }</style>" string
+ */
+ static QString themeLinkStyle();
+ /**
+ * Returns either QPalette::Text or QPalette::Base as appropriate for the
+ * specified foreground color
+ *
+ * @param color The background color.
+ * @return A contrasting foreground color for the current mode / theme.
+ */
+ static const QColor contrastingTextColor(const QColor color);
+
+ /**
+ * Returns an appropriate background color for hovered abstract items.
+ * @return The background color.
+ */
+ static const QColor hoverBackground();
+
+ /**
+ * Returns an appropriate warning background color for the current mode.
+ * @return The background color.
+ */
+ static const QColor warningBackground();
+
+private:
+ static QList<QRgb> graph_colors_;
+ static QList<QRgb> sequence_colors_;
+};
+
+void color_filter_qt_add_cb(color_filter_t *colorf, gpointer user_data);
+
+#endif // COLOR_UTILS_H
diff --git a/ui/qt/utils/data_printer.cpp b/ui/qt/utils/data_printer.cpp
new file mode 100644
index 00000000..36f1abd7
--- /dev/null
+++ b/ui/qt/utils/data_printer.cpp
@@ -0,0 +1,264 @@
+/* data_printer.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/utils/data_printer.h>
+#include <ui/qt/utils/variant_pointer.h>
+
+#include <ui/recent.h>
+
+#include <wsutil/utf8_entities.h>
+
+#include <stdint.h>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QString>
+#include <QMimeData>
+
+DataPrinter::DataPrinter(QObject * parent)
+: QObject(parent),
+ byteLineLength_(16)
+{}
+
+void DataPrinter::toClipboard(DataPrinter::DumpType type, IDataPrintable * printable)
+{
+ const QByteArray printData = printable->printableData();
+
+ QString clipboard_text;
+
+ switch(type)
+ {
+ case DP_CString:
+ // Beginning quote
+ clipboard_text += QString("\"");
+ for (int i = 0; i < printData.length(); i++) {
+ /* ASCII printable */
+ int ch = printData[i];
+ if (ch >= 32 && ch <= 126) {
+ clipboard_text += QChar(ch);
+ }
+ else {
+ clipboard_text += QString("\\x%1").arg((uint8_t) printData[i], 2, 16, QChar('0'));
+ }
+ }
+ // End quote
+ clipboard_text += QString("\"");
+ break;
+ case DP_HexStream:
+ for (int i = 0; i < printData.length(); i++)
+ clipboard_text += QString("%1").arg((uint8_t) printData[i], 2, 16, QChar('0'));
+ break;
+ case DP_Base64:
+#if WS_IS_AT_LEAST_GNUC_VERSION(12,1)
+DIAG_OFF(stringop-overread)
+#endif
+ clipboard_text = printData.toBase64();
+#if WS_IS_AT_LEAST_GNUC_VERSION(12,1)
+DIAG_ON(stringop-overread)
+#endif
+ break;
+ case DP_MimeData:
+ binaryDump(printData);
+ break;
+ case DP_HexDump:
+ clipboard_text = hexTextDump(printData, true);
+ break;
+ case DP_HexOnly:
+ clipboard_text = hexTextDump(printData, false);
+ break;
+ default:
+ break;
+ }
+
+ if (!clipboard_text.isEmpty()) {
+ qApp->clipboard()->setText(clipboard_text);
+ }
+}
+
+void DataPrinter::binaryDump(const QByteArray printData)
+{
+ if (!printData.isEmpty()) {
+ QMimeData *mime_data = new QMimeData;
+ // gtk/gui_utils.c:copy_binary_to_clipboard says:
+ /* XXX - this is not understood by most applications,
+ * but can be pasted into the better hex editors - is
+ * there something better that we can do?
+ */
+ // As of 2015-07-30, pasting into Frhed works on Windows. Pasting into
+ // Hex Editor Neo and HxD does not.
+ mime_data->setData("application/octet-stream", printData);
+ qApp->clipboard()->setMimeData(mime_data);
+ }
+}
+
+void DataPrinter::setByteLineLength(int bll)
+{
+ byteLineLength_ = bll;
+}
+
+int DataPrinter::byteLineLength() const
+{
+ return byteLineLength_;
+}
+
+int DataPrinter::hexChars()
+{
+ int row_width, chars_per_byte;
+
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ row_width = 16;
+ chars_per_byte = 3;
+ break;
+ case BYTES_BITS:
+ row_width = 8;
+ chars_per_byte = 9;
+ break;
+ case BYTES_DEC:
+ case BYTES_OCT:
+ row_width = 16;
+ chars_per_byte = 4;
+ break;
+ default:
+ ws_assert_not_reached();
+ }
+ return (row_width * chars_per_byte) + ((row_width - 1) / separatorInterval());
+}
+
+QString DataPrinter::hexTextDump(const QByteArray printData, bool showASCII)
+{
+ QString clipboard_text;
+
+ QString byteStr;
+ QString dataStr;
+
+ int cnt = 0;
+ while (cnt < printData.length())
+ {
+ byteStr += QString(" %1").arg((uint8_t) printData[cnt], 2, 16, QChar('0'));
+ if (showASCII)
+ {
+ QChar ch(printData[cnt]);
+ if (g_ascii_isprint(printData[cnt]))
+ dataStr += printData[cnt];
+ else
+ dataStr += '.';
+ }
+ cnt++;
+ }
+
+ int lines = static_cast<int>(printData.length()) / byteLineLength_;
+ if (printData.length() % byteLineLength_ > 0)
+ lines++;
+
+ for (cnt = 0; cnt < lines; cnt++)
+ {
+ int offset = cnt * 0x10;
+
+ clipboard_text += QString("%1 ").arg(offset, 4, 16, QChar('0'));
+ clipboard_text += byteStr.mid(offset * 3, byteLineLength_ * 3);
+
+ if (showASCII)
+ {
+ /* separation bytes for byte and text */
+ clipboard_text += QString(3, ' ');
+
+ /* separation bytes last line */
+ if (cnt == (lines - 1) )
+ {
+ int remSpace = byteLineLength_ - static_cast<int>(dataStr.mid(offset, byteLineLength_).length());
+ clipboard_text += QString(remSpace * 3, ' ');
+ }
+
+ /* text representation */
+ clipboard_text += dataStr.mid(offset, byteLineLength_);
+ }
+
+ clipboard_text += "\n";
+ }
+
+ return clipboard_text;
+}
+
+DataPrinter * DataPrinter::instance()
+{
+ static DataPrinter * inst = Q_NULLPTR;
+ if (inst == Q_NULLPTR)
+ inst = new DataPrinter();
+ return inst;
+}
+
+QActionGroup * DataPrinter::copyActions(QObject * copyClass, QObject * data)
+{
+ QActionGroup * actions = new QActionGroup(copyClass);
+
+ if (! data && ! dynamic_cast<IDataPrintable *>(copyClass))
+ return actions;
+
+ DataPrinter * dpi = DataPrinter::instance();
+
+ if (data)
+ actions->setProperty("idataprintable", VariantPointer<QObject>::asQVariant(data));
+ else
+ actions->setProperty("idataprintable", VariantPointer<QObject>::asQVariant(copyClass));
+
+ // Mostly duplicated from main_window.ui
+ QAction * action = new QAction(tr("Copy Bytes as Hex + ASCII Dump"), actions);
+ action->setToolTip(tr("Copy packet bytes as a hex and ASCII dump."));
+ action->setProperty("printertype", DataPrinter::DP_HexDump);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ action = new QAction(tr("…as Hex Dump"), actions);
+ action->setToolTip(tr("Copy packet bytes as a hex dump."));
+ action->setProperty("printertype", DataPrinter::DP_HexOnly);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ action = new QAction(tr("…as a Hex Stream"), actions);
+ action->setToolTip(tr("Copy packet bytes as a stream of hex."));
+ action->setProperty("printertype", DataPrinter::DP_HexStream);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ action = new QAction(tr("…as a Base64 String"), actions);
+ action->setToolTip(tr("Copy packet bytes as a base64 encoded string."));
+ action->setProperty("printertype", DataPrinter::DP_Base64);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ action = new QAction(tr("…as MIME Data"), actions);
+ action->setToolTip(tr("Copy packet bytes as application/octet-stream MIME data."));
+ action->setProperty("printertype", DataPrinter::DP_MimeData);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ action = new QAction(tr("…as C String"), actions);
+ action->setToolTip(tr("Copy packet bytes as printable ASCII characters and escape sequences."));
+ action->setProperty("printertype", DataPrinter::DP_CString);
+ connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
+
+ return actions;
+}
+
+void DataPrinter::copyIDataBytes(bool /* state */)
+{
+ if (! dynamic_cast<QAction*>(sender()))
+ return;
+
+ QAction * sendingAction = dynamic_cast<QAction *>(sender());
+ if (! sendingAction->actionGroup() || ! sendingAction->actionGroup()->property("idataprintable").isValid())
+ return;
+
+ QObject * dataObject = VariantPointer<QObject>::asPtr(sendingAction->actionGroup()->property("idataprintable"));
+ if (! dataObject || ! dynamic_cast<IDataPrintable *>(dataObject))
+ return;
+
+ int dump_type = sendingAction->property("printertype").toInt();
+
+ if (dump_type >= 0 && dump_type <= DataPrinter::DP_Base64) {
+ DataPrinter printer;
+ printer.toClipboard((DataPrinter::DumpType) dump_type, dynamic_cast<IDataPrintable *>(dataObject));
+ }
+}
diff --git a/ui/qt/utils/data_printer.h b/ui/qt/utils/data_printer.h
new file mode 100644
index 00000000..85f11c36
--- /dev/null
+++ b/ui/qt/utils/data_printer.h
@@ -0,0 +1,60 @@
+/** @file
+ *
+ * Used by ByteView and others, to create data dumps in printable
+ * form
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DATA_PRINTER_H
+#define DATA_PRINTER_H
+
+#include <config.h>
+
+#include <QObject>
+#include <QActionGroup>
+
+#include <ui/qt/utils/idata_printable.h>
+
+class DataPrinter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DataPrinter(QObject *parent = 0);
+
+ enum DumpType {
+ DP_HexDump,
+ DP_HexOnly,
+ DP_HexStream,
+ DP_CString,
+ DP_MimeData,
+ DP_Base64
+ };
+
+ void toClipboard(DataPrinter::DumpType type, IDataPrintable * printable);
+
+ void setByteLineLength(int);
+ int byteLineLength() const;
+ // Insert a space after this many bytes
+ static int separatorInterval() { return 8; }
+ // The number of hexadecimal characters per line
+ static int hexChars();
+
+ static QActionGroup * copyActions(QObject * copyClass, QObject * data = Q_NULLPTR);
+ static DataPrinter * instance();
+
+protected slots:
+ void copyIDataBytes(bool);
+
+private:
+ QString hexTextDump(const QByteArray printData, bool showASCII);
+ void binaryDump(const QByteArray printData);
+
+ int byteLineLength_;
+};
+
+#endif // DATA_PRINTER_H
diff --git a/ui/qt/utils/field_information.cpp b/ui/qt/utils/field_information.cpp
new file mode 100644
index 00000000..395bde2f
--- /dev/null
+++ b/ui/qt/utils/field_information.cpp
@@ -0,0 +1,214 @@
+/* field_information.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdint.h>
+
+#include <ui/qt/utils/field_information.h>
+
+FieldInformation::FieldInformation(field_info *fi, QObject * parent)
+:QObject(parent)
+{
+ fi_ = fi;
+ parent_fi_ = NULL;
+}
+
+FieldInformation::FieldInformation(const ProtoNode *node, QObject * parent)
+:QObject(parent)
+{
+ fi_ = NULL;
+ if (node && node->isValid()) {
+ fi_ = node->protoNode()->finfo;
+ }
+ parent_fi_ = NULL;
+}
+
+bool FieldInformation::isValid() const
+{
+ bool ret = false;
+
+ if (fi_ && fi_->hfinfo)
+ {
+ if (fi_->hfinfo->blurb != NULL && fi_->hfinfo->blurb[0] != '\0') {
+ ret = true;
+ } else {
+ ret = QString((fi_->hfinfo->name)).length() > 0;
+ }
+ }
+
+ return ret;
+}
+
+bool FieldInformation::isLink() const
+{
+ if (fi_ && fi_->hfinfo) {
+ if ((fi_->hfinfo->type == FT_FRAMENUM) ||
+ (FI_GET_FLAG(fi_, FI_URL) && FT_IS_STRING(fi_->hfinfo->type))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void FieldInformation::setParentField(field_info * par_fi)
+{
+ parent_fi_ = par_fi;
+}
+
+int FieldInformation::treeType()
+{
+ if (fi_) {
+ Q_ASSERT(fi_->tree_type >= -1 && fi_->tree_type < num_tree_types);
+ return fi_->tree_type;
+ }
+ return -1;
+}
+
+field_info * FieldInformation::fieldInfo() const
+{
+ return fi_;
+}
+
+FieldInformation::HeaderInfo FieldInformation::headerInfo() const
+{
+ HeaderInfo header;
+
+ if (fi_ && fi_->hfinfo)
+ {
+ header.name = fi_->hfinfo->name;
+ header.description = fi_->hfinfo->blurb;
+ header.abbreviation = fi_->hfinfo->abbrev;
+ header.isValid = true;
+ header.type = fi_->hfinfo->type;
+ header.parent = fi_->hfinfo->parent;
+ header.id = fi_->hfinfo->id;
+ }
+ else
+ {
+ header.name = "";
+ header.description = "";
+ header.abbreviation = "";
+ header.isValid = false;
+ header.type = FT_NONE;
+ header.parent = 0;
+ header.id = 0;
+ }
+
+ return header;
+}
+
+FieldInformation * FieldInformation::parentField() const
+{
+ return new FieldInformation(parent_fi_, parent());
+}
+
+bool FieldInformation::tvbContains(FieldInformation *child)
+{
+ if (fi_ && child && fi_->ds_tvb == child->fieldInfo()->ds_tvb)
+ return true;
+
+ return false;
+}
+
+unsigned FieldInformation::flag(unsigned mask)
+{
+ if (fi_) {
+ return FI_GET_FLAG(fi_, mask);
+ }
+ return 0;
+}
+
+const QString FieldInformation::moduleName()
+{
+ QString module_name;
+ if (isValid()) {
+ if (headerInfo().parent == -1) {
+ module_name = fi_->hfinfo->abbrev;
+ } else {
+ module_name = proto_registrar_get_abbrev(headerInfo().parent);
+ }
+ }
+ return module_name;
+}
+
+QString FieldInformation::toString()
+{
+ QByteArray display_label;
+
+ display_label.resize(80); // Arbitrary.
+ int label_len = proto_item_fill_display_label(fi_, display_label.data(), static_cast<int>(display_label.size())-1);
+ display_label.resize(label_len);
+
+ if (display_label.isEmpty()) {
+ return "[no value for field]";
+ }
+ return QString(display_label);
+}
+
+QString FieldInformation::url()
+{
+ QString url;
+ if (flag(FI_URL) && headerInfo().isValid && FT_IS_STRING(fi_->hfinfo->type)) {
+ url = toString();
+ }
+ return url;
+}
+
+FieldInformation::Position FieldInformation::position() const
+{
+ Position pos = {-1, -1};
+ if (fi_ && fi_->ds_tvb)
+ {
+ int len = (int) tvb_captured_length(fi_->ds_tvb);
+
+ pos.start = fi_->start;
+ pos.length = fi_->length;
+
+ if (pos.start < 0 || pos.length < 0 || pos.start >= len)
+ {
+ if (fi_->appendix_start >= 0 && fi_->appendix_length > 0 && fi_->appendix_start < len)
+ {
+ pos.start = fi_->appendix_start;
+ pos.length = fi_->appendix_length;
+ }
+ }
+ }
+
+ return pos;
+}
+
+FieldInformation::Position FieldInformation::appendix() const
+{
+ Position pos = {-1, -1};
+ if (fi_ && fi_->ds_tvb)
+ {
+ pos.start = fi_->appendix_start;
+ pos.length = fi_->appendix_length;
+ }
+
+ return pos;
+}
+
+const QByteArray FieldInformation::printableData()
+{
+ QByteArray data;
+
+ if (fi_ && fi_->ds_tvb)
+ {
+ FieldInformation::Position pos = position();
+ int rem_length = tvb_captured_length_remaining(fi_->ds_tvb, pos.start);
+
+ int length = pos.length;
+ if (length > rem_length)
+ length = rem_length;
+ uint8_t * dataSet = (uint8_t *)tvb_memdup(wmem_file_scope(), fi_->ds_tvb, pos.start, length);
+ data = QByteArray::fromRawData((char *)dataSet, length);
+ }
+
+ return data;
+}
diff --git a/ui/qt/utils/field_information.h b/ui/qt/utils/field_information.h
new file mode 100644
index 00000000..b0001b71
--- /dev/null
+++ b/ui/qt/utils/field_information.h
@@ -0,0 +1,76 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef FIELD_INFORMATION_H_
+#define FIELD_INFORMATION_H_
+
+#include <config.h>
+
+#include <epan/proto.h>
+
+#include <ui/qt/utils/proto_node.h>
+#include "data_printer.h"
+
+#include <QObject>
+
+class FieldInformation : public QObject, public IDataPrintable
+{
+ Q_OBJECT
+ Q_INTERFACES(IDataPrintable)
+
+public:
+
+ struct HeaderInfo
+ {
+ QString name;
+ QString description;
+ QString abbreviation;
+ bool isValid;
+ enum ftenum type;
+ int parent;
+ int id;
+ };
+
+ struct Position
+ {
+ int start;
+ int length;
+ };
+
+ explicit FieldInformation(field_info * fi, QObject * parent = Q_NULLPTR);
+ explicit FieldInformation(const ProtoNode * node, QObject * parent = Q_NULLPTR);
+
+ bool isValid() const;
+ bool isLink() const ;
+
+ field_info * fieldInfo() const;
+
+ HeaderInfo headerInfo() const;
+ Position position() const;
+ Position appendix() const;
+
+ void setParentField(field_info * fi);
+ int treeType();
+ FieldInformation * parentField() const;
+ bool tvbContains(FieldInformation *);
+ unsigned flag(unsigned mask);
+ const QString moduleName();
+ QString toString();
+ QString url();
+
+ const QByteArray printableData();
+
+private:
+
+ field_info * fi_;
+ field_info * parent_fi_;
+};
+
+
+#endif // FIELD_INFORMATION_H_
diff --git a/ui/qt/utils/frame_information.cpp b/ui/qt/utils/frame_information.cpp
new file mode 100644
index 00000000..d344bc04
--- /dev/null
+++ b/ui/qt/utils/frame_information.cpp
@@ -0,0 +1,103 @@
+/* frame_information.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <epan/epan_dissect.h>
+#include "epan/epan.h"
+#include "epan/column.h"
+#include "epan/ftypes/ftypes.h"
+
+#include "wiretap/wtap.h"
+
+#include "cfile.h"
+#include "file.h"
+#include <ui/qt/capture_file.h>
+
+#include "frame_tvbuff.h"
+
+#include <stdint.h>
+
+#include <ui/qt/utils/frame_information.h>
+
+FrameInformation::FrameInformation(CaptureFile * capfile, frame_data * fi, QObject * parent)
+:QObject(parent),
+ fi_(fi),
+ cap_file_(capfile),
+ edt_(Q_NULLPTR)
+{
+ wtap_rec_init(&rec_);
+ ws_buffer_init(&buf_, 1514);
+ loadFrameTree();
+}
+
+void FrameInformation::loadFrameTree()
+{
+ if (! fi_ || ! cap_file_ || !cap_file_->capFile())
+ return;
+
+ if (!cf_read_record(cap_file_->capFile(), fi_, &rec_, &buf_))
+ return;
+
+ edt_ = g_new0(epan_dissect_t, 1);
+
+ /* proto tree, visible. We need a proto tree if there's custom columns */
+ epan_dissect_init(edt_, cap_file_->capFile()->epan, TRUE, TRUE);
+ col_custom_prime_edt(edt_, &(cap_file_->capFile()->cinfo));
+
+ epan_dissect_run(edt_, cap_file_->capFile()->cd_t, &rec_,
+ frame_tvbuff_new_buffer(&cap_file_->capFile()->provider, fi_, &buf_),
+ fi_, &(cap_file_->capFile()->cinfo));
+ epan_dissect_fill_in_columns(edt_, TRUE, TRUE);
+}
+
+FrameInformation::~FrameInformation()
+{
+ if (edt_) {
+ epan_dissect_cleanup(edt_);
+ g_free(edt_);
+ }
+ wtap_rec_cleanup(&rec_);
+ ws_buffer_free(&buf_);
+}
+
+bool FrameInformation::isValid()
+{
+ bool ret = false;
+
+ if (fi_ && cap_file_ && edt_ && edt_->tvb)
+ {
+ ret = true;
+ }
+
+ return ret;
+}
+
+frame_data * FrameInformation::frameData() const
+{
+ return fi_;
+}
+
+int FrameInformation::frameNum() const
+{
+ if (! fi_)
+ return -1;
+ return fi_->num;
+}
+
+const QByteArray FrameInformation::printableData()
+{
+ if (!fi_ || !edt_)
+ return QByteArray();
+
+
+ int length = tvb_captured_length(edt_->tvb);
+ const char *data = (const char *)tvb_get_ptr(edt_->tvb, 0, length);
+ return QByteArray(data, length);
+}
diff --git a/ui/qt/utils/frame_information.h b/ui/qt/utils/frame_information.h
new file mode 100644
index 00000000..7bff3cb4
--- /dev/null
+++ b/ui/qt/utils/frame_information.h
@@ -0,0 +1,57 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef FRAME_INFORMATION_H_
+#define FRAME_INFORMATION_H_
+
+#include <config.h>
+
+#include <epan/proto.h>
+#include <epan/epan_dissect.h>
+#include "epan/epan.h"
+#include "epan/column.h"
+#include "epan/ftypes/ftypes.h"
+
+#include <ui/qt/capture_file.h>
+
+#include "data_printer.h"
+
+#include <QObject>
+
+class FrameInformation : public QObject, public IDataPrintable
+{
+ Q_OBJECT
+ Q_INTERFACES(IDataPrintable)
+
+public:
+
+ explicit FrameInformation(CaptureFile * cfile, frame_data * fi, QObject * parent = Q_NULLPTR);
+ virtual ~FrameInformation();
+
+ bool isValid();
+
+ frame_data * frameData() const;
+ int frameNum() const;
+
+ const QByteArray printableData();
+
+private:
+
+ frame_data * fi_;
+ CaptureFile * cap_file_;
+ epan_dissect_t * edt_;
+ wtap_rec rec_; /* Record metadata */
+ Buffer buf_; /* Record data */
+
+ void loadFrameTree();
+
+};
+
+
+#endif // FRAME_INFORMATION_H_
diff --git a/ui/qt/utils/idata_printable.h b/ui/qt/utils/idata_printable.h
new file mode 100644
index 00000000..daeddf19
--- /dev/null
+++ b/ui/qt/utils/idata_printable.h
@@ -0,0 +1,34 @@
+/** @file
+ *
+ * Interface class for classes, which provide an interface to
+ * print objects
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef IDATA_PRINTABLE_H
+#define IDATA_PRINTABLE_H
+
+#include <config.h>
+
+#include <QtPlugin>
+#include <QByteArray>
+#include <QObject>
+
+class IDataPrintable
+{
+public:
+ virtual ~IDataPrintable() {}
+
+ virtual const QByteArray printableData() = 0;
+};
+
+#define IDataPrintable_iid "org.wireshark.Qt.UI.IDataPrintable"
+
+Q_DECLARE_INTERFACE(IDataPrintable, IDataPrintable_iid)
+
+#endif // IDATA_PRINTABLE_H
diff --git a/ui/qt/utils/proto_node.cpp b/ui/qt/utils/proto_node.cpp
new file mode 100644
index 00000000..68d1f4c7
--- /dev/null
+++ b/ui/qt/utils/proto_node.cpp
@@ -0,0 +1,166 @@
+/* proto_node.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/utils/proto_node.h>
+#include <ui/qt/utils/field_information.h>
+
+#include <epan/prefs.h>
+
+ProtoNode::ProtoNode(proto_node *node, ProtoNode *parent) :
+ node_(node), parent_(parent)
+{
+ if (node_) {
+
+ int num_children = 0;
+ for (proto_node *child = node_->first_child; child; child = child->next) {
+ if (!isHidden(child)) {
+ num_children++;
+ }
+ }
+
+ m_children.reserve(num_children);
+
+ for (proto_node *child = node_->first_child; child; child = child->next) {
+ if (!isHidden(child)) {
+ m_children.append(new ProtoNode(child, this));
+ }
+ }
+ }
+}
+
+ProtoNode::~ProtoNode()
+{
+ qDeleteAll(m_children);
+}
+
+bool ProtoNode::isValid() const
+{
+ return node_;
+}
+
+bool ProtoNode::isChild() const
+{
+ return node_ && node_->parent;
+}
+
+ProtoNode* ProtoNode::parentNode()
+{
+ return parent_;
+}
+
+QString ProtoNode::labelText() const
+{
+ if (!node_) {
+ return QString();
+ }
+ field_info *fi = PNODE_FINFO(node_);
+ if (!fi) {
+ return QString();
+ }
+
+ QString label;
+ /* was a free format label produced? */
+ if (fi->rep) {
+ label = fi->rep->representation;
+ }
+ else { /* no, make a generic label */
+ gchar label_str[ITEM_LABEL_LENGTH];
+ proto_item_fill_label(fi, label_str);
+ label = label_str;
+ }
+
+ // Generated takes precedence.
+ if (proto_item_is_generated(node_)) {
+ label.prepend("[");
+ label.append("]");
+ }
+ if (proto_item_is_hidden(node_)) {
+ label.prepend("<");
+ label.append(">");
+ }
+ return label;
+}
+
+int ProtoNode::childrenCount() const
+{
+ if (!node_) return 0;
+
+ return (int)m_children.count();
+}
+
+int ProtoNode::row()
+{
+ if (!isChild()) {
+ return -1;
+ }
+
+ return (int)parent_->m_children.indexOf(const_cast<ProtoNode*>(this));
+}
+
+bool ProtoNode::isExpanded() const
+{
+ if (node_ && node_->finfo && node_->first_child && tree_expanded(node_->finfo->tree_type)) {
+ return true;
+ }
+ return false;
+}
+
+proto_node * ProtoNode::protoNode() const
+{
+ return node_;
+}
+
+ProtoNode* ProtoNode::child(int row)
+{
+ if (row < 0 || row >= m_children.size())
+ return nullptr;
+ return m_children.at(row);
+}
+
+ProtoNode::ChildIterator ProtoNode::children() const
+{
+ /* XXX: Iterate over m_children instead?
+ * Somewhat faster as m_children already excludes any hidden items. */
+ proto_node *child = node_->first_child;
+ while (child && isHidden(child)) {
+ child = child->next;
+ }
+
+ return ProtoNode::ChildIterator(child);
+}
+
+ProtoNode::ChildIterator::ChildIterator(ProtoNode::ChildIterator::NodePtr n)
+{
+ node = n;
+}
+
+bool ProtoNode::ChildIterator::hasNext()
+{
+ if (! node || node->next == Q_NULLPTR)
+ return false;
+ return true;
+}
+
+ProtoNode::ChildIterator ProtoNode::ChildIterator::next()
+{
+ do {
+ node = node->next;
+ } while (node && isHidden(node));
+ return *this;
+}
+
+ProtoNode ProtoNode::ChildIterator::element()
+{
+ return ProtoNode(node);
+}
+
+bool ProtoNode::isHidden(proto_node * node)
+{
+ return proto_item_is_hidden(node) && !prefs.display_hidden_proto_items;
+}
diff --git a/ui/qt/utils/proto_node.h b/ui/qt/utils/proto_node.h
new file mode 100644
index 00000000..d6cf3797
--- /dev/null
+++ b/ui/qt/utils/proto_node.h
@@ -0,0 +1,63 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef PROTO_NODE_H_
+#define PROTO_NODE_H_
+
+#include <config.h>
+
+#include <epan/proto.h>
+
+#include <QObject>
+#include <QVector>
+
+class ProtoNode
+{
+public:
+
+ class ChildIterator {
+ public:
+ typedef struct _proto_node * NodePtr;
+
+ ChildIterator(NodePtr n = Q_NULLPTR);
+
+ bool hasNext();
+ ChildIterator next();
+ ProtoNode element();
+
+ protected:
+ NodePtr node;
+ };
+
+ explicit ProtoNode(proto_node * node = NULL, ProtoNode *parent = nullptr);
+ ~ProtoNode();
+
+ bool isValid() const;
+ bool isChild() const;
+ bool isExpanded() const;
+
+ proto_node *protoNode() const;
+ ProtoNode *child(int row);
+ int childrenCount() const;
+ int row();
+ ProtoNode *parentNode();
+
+ QString labelText() const;
+
+ ChildIterator children() const;
+
+private:
+ proto_node * node_;
+ QVector<ProtoNode*>m_children;
+ ProtoNode *parent_;
+ static bool isHidden(proto_node * node);
+};
+
+
+#endif // PROTO_NODE_H_
diff --git a/ui/qt/utils/qt_ui_utils.cpp b/ui/qt/utils/qt_ui_utils.cpp
new file mode 100644
index 00000000..9dad9dd9
--- /dev/null
+++ b/ui/qt/utils/qt_ui_utils.cpp
@@ -0,0 +1,347 @@
+/* qt_ui_utils.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ui/qt/utils/qt_ui_utils.h>
+
+#include <epan/addr_resolv.h>
+#include <epan/range.h>
+#include <epan/to_str.h>
+#include <epan/value_string.h>
+#include <epan/prefs.h>
+
+#include <ui/recent.h>
+#include <ui/util.h>
+#include "ui/ws_ui_util.h"
+
+#include <wsutil/str_util.h>
+
+#include <ui/qt/main_application.h>
+
+#include <QAction>
+#include <QApplication>
+#include <QDateTime>
+#include <QDesktopServices>
+#include <QDir>
+#include <QFileInfo>
+#include <QFontDatabase>
+#include <QProcess>
+#include <QUrl>
+#include <QUuid>
+#include <QScreen>
+
+/*
+ * We might want to create our own "wsstring" class with convenience
+ * methods for handling g_malloc()ed strings, GStrings, and a shortcut
+ * to .toUtf8().constData().
+ */
+
+gchar *qstring_strdup(QString q_string) {
+ return g_strdup(q_string.toUtf8().constData());
+}
+
+QString gchar_free_to_qstring(gchar *glib_string) {
+ return QString(gchar_free_to_qbytearray(glib_string));
+}
+
+QByteArray gchar_free_to_qbytearray(gchar *glib_string)
+{
+ QByteArray qt_bytearray(glib_string);
+ g_free(glib_string);
+ return qt_bytearray;
+}
+
+QByteArray gstring_free_to_qbytearray(GString *glib_gstring)
+{
+ QByteArray qt_ba(glib_gstring->str);
+ g_string_free(glib_gstring, TRUE);
+ return qt_ba;
+}
+
+QByteArray gbytearray_free_to_qbytearray(GByteArray *glib_array)
+{
+ QByteArray qt_ba(reinterpret_cast<char *>(glib_array->data), glib_array->len);
+ g_byte_array_free(glib_array, TRUE);
+ return qt_ba;
+}
+
+const QString int_to_qstring(qint64 value, int field_width, int base)
+{
+ // Qt deprecated QString::sprintf in Qt 5.0, then added ::asprintf in
+ // Qt 5.5. Rather than navigate a maze of QT_VERSION_CHECKs, just use
+ // QString::arg.
+ QString int_qstr;
+
+ switch (base) {
+ case 8:
+ int_qstr = "0";
+ break;
+ case 16:
+ int_qstr = "0x";
+ break;
+ default:
+ break;
+ }
+
+ int_qstr += QString("%1").arg(value, field_width, base, QChar('0'));
+ return int_qstr;
+}
+
+const QString address_to_qstring(const _address *address, bool enclose)
+{
+ QString address_qstr = QString();
+ if (address) {
+ if (enclose && address->type == AT_IPv6) address_qstr += "[";
+ gchar *address_gchar_p = address_to_str(NULL, address);
+ address_qstr += address_gchar_p;
+ wmem_free(NULL, address_gchar_p);
+ if (enclose && address->type == AT_IPv6) address_qstr += "]";
+ }
+ return address_qstr;
+}
+
+const QString address_to_display_qstring(const _address *address)
+{
+ QString address_qstr = QString();
+ if (address) {
+ gchar *address_gchar_p = address_to_display(NULL, address);
+ address_qstr = address_gchar_p;
+ wmem_free(NULL, address_gchar_p);
+ }
+ return address_qstr;
+}
+
+const QString val_to_qstring(const guint32 val, const value_string *vs, const char *fmt)
+{
+ QString val_qstr;
+ gchar* gchar_p = val_to_str_wmem(NULL, val, vs, fmt);
+ val_qstr = gchar_p;
+ wmem_free(NULL, gchar_p);
+
+ return val_qstr;
+}
+
+const QString val_ext_to_qstring(const guint32 val, value_string_ext *vse, const char *fmt)
+{
+ QString val_qstr;
+ gchar* gchar_p = val_to_str_ext_wmem(NULL, val, vse, fmt);
+ val_qstr = gchar_p;
+ wmem_free(NULL, gchar_p);
+
+ return val_qstr;
+}
+
+const QString range_to_qstring(const range_string *range)
+{
+ QString range_qstr = QString();
+ if (range) {
+ range_qstr += QString("%1-%2").arg(range->value_min).arg(range->value_max);
+ }
+ return range_qstr;
+}
+
+const QString bits_s_to_qstring(const double bits_s)
+{
+ return gchar_free_to_qstring(
+ format_size(bits_s, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI));
+}
+
+const QString file_size_to_qstring(const gint64 size)
+{
+ return gchar_free_to_qstring(
+ format_size(size, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI));
+}
+
+const QString time_t_to_qstring(time_t ti_time)
+{
+ QDateTime date_time = QDateTime::fromSecsSinceEpoch(qint64(ti_time));
+ QString time_str = date_time.toLocalTime().toString("yyyy-MM-dd hh:mm:ss");
+ return time_str;
+}
+
+QString html_escape(const QString plain_string) {
+ return plain_string.toHtmlEscaped();
+}
+
+
+void smooth_font_size(QFont &font) {
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ QList<int> size_list = QFontDatabase::smoothSizes(font.family(), font.styleName());
+#else
+ QFontDatabase fdb;
+ QList<int> size_list = fdb.smoothSizes(font.family(), font.styleName());
+#endif
+
+ if (size_list.size() < 2) return;
+
+ int last_size = size_list.takeFirst();
+ foreach (int cur_size, size_list) {
+ if (font.pointSize() > last_size && font.pointSize() <= cur_size) {
+ font.setPointSize(cur_size);
+ return;
+ }
+ last_size = cur_size;
+ }
+}
+
+bool qActionLessThan(const QAction * a1, const QAction * a2) {
+ return a1->text().compare(a2->text()) < 0;
+}
+
+bool qStringCaseLessThan(const QString &s1, const QString &s2)
+{
+ return s1.compare(s2, Qt::CaseInsensitive) < 0;
+}
+
+void desktop_show_in_folder(const QString file_path)
+{
+ bool success = false;
+
+ // https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
+
+#if defined(Q_OS_WIN)
+ QString command = "explorer.exe";
+ QStringList arguments;
+ QString path = QDir::toNativeSeparators(file_path);
+ arguments << "/select," << path + "";
+ success = QProcess::startDetached(command, arguments);
+#elif defined(Q_OS_MAC)
+ QStringList script_args;
+ QString escaped_path = file_path;
+
+ escaped_path.replace('"', "\\\"");
+ script_args << "-e"
+ << QString("tell application \"Finder\" to reveal POSIX file \"%1\"")
+ .arg(escaped_path);
+ if (QProcess::execute("/usr/bin/osascript", script_args) == 0) {
+ success = true;
+ script_args.clear();
+ script_args << "-e"
+ << "tell application \"Finder\" to activate";
+ QProcess::execute("/usr/bin/osascript", script_args);
+ }
+#else
+ // Is there a way to highlight the file using xdg-open?
+#endif
+ if (!success) {
+ QFileInfo file_info(file_path);
+ QDesktopServices::openUrl(QUrl::fromLocalFile(file_info.dir().absolutePath()));
+ }
+}
+
+bool rect_on_screen(const QRect &rect)
+{
+ foreach (QScreen *screen, qApp->screens()) {
+ if (screen->availableGeometry().contains(rect)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void set_action_shortcuts_visible_in_context_menu(QList<QAction *> actions)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) && QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
+ // For QT_VERSION >= 5.13.0 we call styleHints()->setShowShortcutsInContextMenus(true)
+ // in WiresharkApplication.
+ // QTBUG-71471
+ // QTBUG-61181
+ foreach (QAction *action, actions) {
+ action->setShortcutVisibleInContextMenu(true);
+ }
+#else
+ Q_UNUSED(actions)
+#endif
+}
+
+QVector<rtpstream_id_t *>qvector_rtpstream_ids_copy(QVector<rtpstream_id_t *> stream_ids)
+{
+ QVector<rtpstream_id_t *>new_ids;
+
+ foreach(rtpstream_id_t *id, stream_ids) {
+ rtpstream_id_t *new_id = g_new0(rtpstream_id_t, 1);
+ rtpstream_id_copy(id, new_id);
+ new_ids << new_id;
+ }
+
+ return new_ids;
+}
+
+void qvector_rtpstream_ids_free(QVector<rtpstream_id_t *> stream_ids)
+{
+ foreach(rtpstream_id_t *id, stream_ids) {
+ rtpstream_id_free(id);
+ }
+}
+
+QString make_filter_based_on_rtpstream_id(QVector<rtpstream_id_t *> stream_ids)
+{
+ QStringList stream_filters;
+ QString filter;
+
+ foreach(rtpstream_id_t *id, stream_ids) {
+ QString ip_proto = id->src_addr.type == AT_IPv6 ? "ipv6" : "ip";
+ stream_filters << QString("(%1.src==%2 && udp.srcport==%3 && %1.dst==%4 && udp.dstport==%5 && rtp.ssrc==0x%6)")
+ .arg(ip_proto) // %1
+ .arg(address_to_qstring(&id->src_addr)) // %2
+ .arg(id->src_port) // %3
+ .arg(address_to_qstring(&id->dst_addr)) // %4
+ .arg(id->dst_port) // %5
+ .arg(id->ssrc, 0, 16);
+ }
+ if (stream_filters.length() > 0) {
+ filter = stream_filters.join(" || ");
+ }
+
+ return filter;
+}
+
+QString openDialogInitialDir()
+{
+ QString result;
+
+ switch (prefs.gui_fileopen_style) {
+
+ case FO_STYLE_LAST_OPENED:
+ /* The user has specified that we should start out in the last directory
+ we looked in. If we've already opened a file, use its containing
+ directory, if we could determine it, as the directory, otherwise
+ use the "last opened" directory saved in the preferences file if
+ there was one. */
+ /* This is now the default behaviour in file_selection_new() */
+ result = QString(get_open_dialog_initial_dir());
+ break;
+
+ case FO_STYLE_SPECIFIED:
+ /* The user has specified that we should always start out in a
+ specified directory; if they've specified that directory,
+ start out by showing the files in that dir. */
+ if (prefs.gui_fileopen_dir[0] != '\0')
+ result = QString(prefs.gui_fileopen_dir);
+ break;
+ }
+
+ QDir ld(result);
+ if (ld.exists())
+ return result;
+
+ return QString();
+}
+
+void storeLastDir(QString dir)
+{
+ /* XXX - printable? */
+ if (dir.length() > 0)
+ set_last_open_dir(qUtf8Printable(dir));
+}
+
diff --git a/ui/qt/utils/qt_ui_utils.h b/ui/qt/utils/qt_ui_utils.h
new file mode 100644
index 00000000..60ccf31d
--- /dev/null
+++ b/ui/qt/utils/qt_ui_utils.h
@@ -0,0 +1,280 @@
+/** @file
+ *
+ * Declarations of Qt-specific UI utility routines
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __QT_UI_UTILS_H__
+#define __QT_UI_UTILS_H__
+
+// xxx - copied from ui/gtk/gui_utils.h
+
+/** @file
+ * Utility functions for working with the Wireshark and GLib APIs.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#include "ui/rtp_stream.h"
+
+#include <QString>
+
+class QAction;
+class QFont;
+class QRect;
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+// These are defined elsewhere in ../gtk/
+#define RECENT_KEY_CAPTURE_FILE "recent.capture_file"
+#define RECENT_KEY_REMOTE_HOST "recent.remote_host"
+
+struct _address;
+struct epan_range;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/*
+ * Helper macro, to prevent old-style-cast warnings, when using GList in c++ code
+ */
+#define gxx_list_next(list) ((list) ? ((reinterpret_cast<GList *>(list))->next) : Q_NULLPTR)
+#define gxx_constlist_next(list) ((list) ? ((reinterpret_cast<const GList *>(list))->next) : Q_NULLPTR)
+#define gxx_list_previous(list) ((list) ? ((reinterpret_cast<GList *>(list))->prev) : Q_NULLPTR)
+#define gxx_constlist_previous(list) ((list) ? ((reinterpret_cast<const GList *>(list))->prev) : Q_NULLPTR)
+
+#define gxx_list_data(type, list) ((list) ? ((reinterpret_cast<type>(list->data))) : Q_NULLPTR)
+
+/** Create a glib-compatible copy of a QString.
+ *
+ * @param q_string A QString.
+ *
+ * @return A copy of the QString. UTF-8 allocated with g_malloc().
+ */
+gchar *qstring_strdup(QString q_string);
+
+/** Transfer ownership of a GLib character string to a newly constructed QString
+ *
+ * @param glib_string A string allocated with g_malloc() or NULL. Will be
+ * freed.
+ *
+ * @return A QString instance created from the input string.
+ */
+QString gchar_free_to_qstring(gchar *glib_string);
+
+/** Transfer ownership of a GLib character string to a newly constructed QString
+ *
+ * @param glib_string A string allocated with g_malloc() or NULL. Will be
+ * freed.
+ *
+ * @return A QByteArray instance created from the input string.
+ */
+QByteArray gchar_free_to_qbytearray(gchar *glib_string);
+
+/** Transfer ownership of a GLib character string to a newly constructed QByteArray
+ *
+ * @param glib_gstring A string allocated with g_malloc() or NULL. Will be
+ * freed.
+ *
+ * @return A QByteArray instance created from the input string.
+ */
+QByteArray gstring_free_to_qbytearray(GString *glib_gstring);
+
+/** Transfer ownership of a GbyteArray to a newly constructed QByteArray
+ *
+ * @param glib_array A GByteArray or NULL. Will be freed.
+ * @return A QByteArray instance created from the input array.
+ */
+QByteArray gbytearray_free_to_qbytearray(GByteArray *glib_array);
+
+/** Convert an integer to a formatted string representation.
+ *
+ * @param value The integer to format.
+ * @param field_width Width of the output, not including any base prefix.
+ * Output will be zero-padded.
+ * @param base Number base between 2 and 36 (limited by QString::arg).
+ *
+ * @return A QString representation of the integer
+ */
+const QString int_to_qstring(qint64 value, int field_width = 0, int base = 10);
+
+/** Convert an address to a QString using address_to_str().
+ *
+ * @param address A pointer to an address.
+ * @param enclose Enclose IPv6 addresses in square brackets.
+ *
+ * @return A QString representation of the address. May be the null string (QString())
+ */
+const QString address_to_qstring(const struct _address *address, bool enclose = false);
+
+/** Convert an address to a QString using address_to_display().
+ *
+ * @param address A pointer to an address.
+ *
+ * @return A QString representation of the address. May be the null string (QString())
+ */
+const QString address_to_display_qstring(const struct _address *address);
+
+/** Convert a value_string to a QString using val_to_str_wmem().
+ *
+ * @param val The value to convert to string.
+ * @param vs value_string array.
+ * @param fmt Formatting for value not in array.
+ *
+ * @return A QString representation of the value_string.
+ */
+const QString val_to_qstring(const guint32 val, const struct _value_string *vs, const char *fmt)
+G_GNUC_PRINTF(3, 0);
+
+/** Convert a value_string_ext to a QString using val_to_str_ext_wmem().
+ *
+ * @param val The value to convert to string.
+ * @param vse value_string_ext array.
+ * @param fmt Formatting for value not in array.
+ *
+ * @return A QString representation of the value_string_ext.
+ */
+const QString val_ext_to_qstring(const guint32 val, struct _value_string_ext *vse, const char *fmt)
+G_GNUC_PRINTF(3, 0);
+
+/** Convert a range to a QString using range_convert_range().
+ *
+ * @param range A pointer to a range_string struct.
+ *
+ * @return A QString representation of the range_string. May be the null string (QString())
+ */
+const QString range_to_qstring(const range_string *range);
+
+/** Convert a bits per second value to a human-readable QString using format_size().
+ *
+ * @param bits_s The value to convert to string.
+ *
+ * @return A QString representation of the data rate in SI units.
+ */
+const QString bits_s_to_qstring(const double bits_s);
+
+/** Convert a file size value to a human-readable QString using format_size().
+ *
+ * @param size The value to convert to string.
+ *
+ * @return A QString representation of the file size in SI units.
+ */
+const QString file_size_to_qstring(const gint64 size);
+
+/** Convert a time_t value to a human-readable QString using QDateTime.
+ *
+ * @param ti_time The value to convert.
+ *
+ * @return A QString representation of the file size in SI units.
+ */
+const QString time_t_to_qstring(time_t ti_time);
+
+/** Escape HTML metacharacters in a string.
+ *
+ * @param plain_string String to convert.
+ *
+ * @return A QString with escaped metacharacters.
+ */
+QString html_escape(const QString plain_string);
+
+/**
+ * Round the current size of a font up to its next "smooth" size.
+ * If a smooth size can't be found the font is left unchanged.
+ *
+ * @param font The font to smooth.
+ */
+void smooth_font_size(QFont &font);
+
+/**
+ * Compare the text of two QActions. Useful for passing to std::sort.
+ *
+ * @param a1 First action
+ * @param a2 Second action
+ */
+bool qActionLessThan(const QAction *a1, const QAction *a2);
+
+/**
+ * Compare two QStrings, ignoring case. Useful for passing to std::sort.
+ *
+ * @param s1 First string
+ * @param s2 Second string
+ */
+bool qStringCaseLessThan(const QString &s1, const QString &s2);
+
+/**
+ * Given the path to a file, open its containing folder in the desktop
+ * shell. Highlight the file if possible.
+ *
+ * @param file_path Path to the file.
+ */
+void desktop_show_in_folder(const QString file_path);
+
+/**
+ * Test to see if a rect is visible on screen.
+ *
+ * @param rect The rect to test, typically a "recent.gui_geometry_*" setting.
+ * @return true if the rect is completely enclosed by one of the display
+ * screens, false otherwise.
+ */
+bool rect_on_screen(const QRect &rect);
+
+/**
+ * Set the "shortcutVisibleInContextMenu" property to true for
+ * a list of qactions.
+ *
+ * @param actions The actions to make visible.
+ */
+void set_action_shortcuts_visible_in_context_menu(QList<QAction *> actions);
+
+/**
+ * Create copy of all rtpstream_ids to new QVector
+ * => caller must release it with qvector_rtpstream_ids_free()
+ *
+ * @param stream_ids List of infos
+ * @return Vector of rtpstream_ids
+ */
+QVector<rtpstream_id_t *>qvector_rtpstream_ids_copy(QVector<rtpstream_id_t *> stream_ids);
+
+/**
+ * Free all rtpstream_ids in QVector
+ *
+ * @param stream_ids List of infos
+ */
+void qvector_rtpstream_ids_free(QVector<rtpstream_id_t *> stream_ids);
+
+/**
+ * Make display filter from list of rtpstream_id
+ *
+ * @param stream_ids List of ids
+ * @return Filter or empty string
+ */
+QString make_filter_based_on_rtpstream_id(QVector<rtpstream_id_t *> stream_ids);
+
+/**
+ * @brief Return the last directory that had been opened.
+ *
+ * This can be influenced by prefs.gui_fileopen_style which will allow to either
+ * open the real last dir or have the user set one specifically.
+ *
+ * @return a reference to that directory.
+ */
+QString openDialogInitialDir();
+
+/**
+ * @brief Store the directory as last directory being used
+ */
+void storeLastDir(QString dir);
+
+#endif /* __QT_UI_UTILS__H__ */
+
+// XXX Add a routine to fetch the HWND corresponding to a widget using QPlatformIntegration
diff --git a/ui/qt/utils/rtp_audio_file.cpp b/ui/qt/utils/rtp_audio_file.cpp
new file mode 100644
index 00000000..591a63bb
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_file.cpp
@@ -0,0 +1,384 @@
+/* rtp_audio_file.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * RTP samples are stored in "sparse" file. File knows where are silence gaps
+ * and they are handled special way (not stored).
+ *
+ * File uses Frame as piece of information. One Frame match audio of one
+ * decoded packet or audio silence in between them. Frame holds information
+ * about frame type (audio/silence), its length and realtime position and
+ * sample possition (where decoded audio is really stored, with gaps omitted).
+ *
+ * There are three stages of the object use
+ * - writing data by frames during decoding of the stream
+ * - reading data by frames during creating the visual waveform
+ * - reading data by bytes/samples during audio play or audio save
+ *
+ * There is no stage indication in the object, but there are different calls
+ * used by the code. For last stage the object looks like QIODevice therefore
+ * any read of it looks like reading of sequence of bytes.
+ *
+ * If audio starts later than start of the file, first Frame contains silence
+ * record. It is leaved out at some cases.
+ */
+
+#include "rtp_audio_file.h"
+#include <ws_attributes.h>
+
+RtpAudioFile::RtpAudioFile(bool use_disk_for_temp, bool use_disk_for_frames):
+ real_pos_(0)
+ , real_size_(0)
+ , sample_pos_(0)
+ , sample_size_(0)
+{
+ QString tempname;
+
+ // ReadOnly because we write different way
+ QIODevice::open(QIODevice::ReadOnly);
+
+ tempname = "memory";
+ if (use_disk_for_temp) {
+ tempname = QString("%1/wireshark_rtp_stream").arg(QDir::tempPath());
+ sample_file_ = new QTemporaryFile(tempname, this);
+ } else {
+ sample_file_ = new QBuffer(this);
+ }
+ if (!sample_file_->open(QIODevice::ReadWrite)) {
+ // We are out of file resources
+ delete sample_file_;
+ qWarning() << "Can't create temp file in " << tempname;
+ throw -1;
+ }
+
+ tempname = "memory";
+ if (use_disk_for_frames) {
+ tempname = QString("%1/wireshark_rtp_frames").arg(QDir::tempPath());
+ sample_file_frame_ = new QTemporaryFile(tempname, this);
+ } else {
+ sample_file_frame_ = new QBuffer(this);
+ }
+ if (!sample_file_frame_->open(QIODevice::ReadWrite)) {
+ // We are out of file resources
+ delete sample_file_;
+ delete sample_file_frame_;
+ qWarning() << "Can't create frame file in " << tempname;
+ throw -1;
+ }
+}
+
+RtpAudioFile::~RtpAudioFile()
+{
+ if (sample_file_) delete sample_file_;
+ if (sample_file_frame_) delete sample_file_frame_;
+}
+
+/*
+ * Functions for writing Frames
+ */
+void RtpAudioFile::setFrameWriteStage()
+{
+ sample_file_->seek(0);
+ sample_file_frame_->seek(0);
+ real_pos_ = 0;
+ real_size_ = 0;
+ sample_pos_ = 0;
+ sample_size_ = 0;
+}
+
+void RtpAudioFile::frameUpdateRealCounters(qint64 written_bytes)
+{
+ if (real_pos_ < real_size_) {
+ // We are writing before end, calculate if we are over real_size_
+ qint64 diff = real_pos_ + written_bytes - real_size_;
+
+ if (diff > 0) {
+ // Update size
+ real_size_ += diff;
+ }
+ } else {
+ real_size_ += written_bytes;
+ }
+ real_pos_ += written_bytes;
+}
+
+void RtpAudioFile::frameUpdateSampleCounters(qint64 written_bytes)
+{
+ if (sample_pos_ < sample_size_) {
+ // We are writing before end, calculate if we are over sample_size_
+ qint64 diff = sample_pos_ + written_bytes - sample_size_;
+
+ if (diff > 0) {
+ // Update size
+ sample_size_ += diff;
+ }
+ } else {
+ sample_size_ += written_bytes;
+ }
+ sample_pos_ += written_bytes;
+}
+
+qint64 RtpAudioFile::frameWriteFrame(guint32 frame_num, qint64 real_pos, qint64 sample_pos, qint64 len, rtp_frame_type type)
+{
+ rtp_frame_info frame_info;
+
+ frame_info.real_pos = real_pos;
+ frame_info.sample_pos = sample_pos;
+ frame_info.len = len;
+ frame_info.frame_num = frame_num;
+ frame_info.type = type;
+
+ return sample_file_frame_->write((char *)&frame_info, sizeof(frame_info));
+}
+
+void RtpAudioFile::frameWriteSilence(guint32 frame_num, qint64 samples)
+{
+ if (samples < 1) return;
+
+ qint64 silence_bytes = samples * SAMPLE_BYTES;
+
+ frameWriteFrame(frame_num, real_pos_, sample_pos_, silence_bytes, RTP_FRAME_SILENCE);
+ frameUpdateRealCounters(silence_bytes);
+}
+
+qint64 RtpAudioFile::frameWriteSamples(guint32 frame_num, const char *data, qint64 max_size)
+{
+ gint64 written;
+
+ written = sample_file_->write(data, max_size);
+
+ if (written != -1) {
+ frameWriteFrame(frame_num, real_pos_, sample_pos_, written, RTP_FRAME_AUDIO);
+ frameUpdateRealCounters(written);
+ frameUpdateSampleCounters(written);
+ }
+
+ return written;
+}
+
+/*
+ * Functions for reading Frames
+ */
+
+void RtpAudioFile::setFrameReadStage(qint64 prepend_samples)
+{
+ sample_file_frame_->seek(0);
+ if (prepend_samples > 0) {
+ // Skip first frame which contains openning silence
+ sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_));
+ }
+}
+
+bool RtpAudioFile::readFrameSamples(gint32 *read_buff_bytes, SAMPLE **read_buff, spx_uint32_t *read_len, guint32 *frame_num, rtp_frame_type *type)
+{
+ rtp_frame_info frame_info;
+ guint64 read_bytes = 0;
+
+ if (!sample_file_frame_->read((char *)&frame_info, sizeof(frame_info))) {
+ // Can't read frame, some error occurred
+ return false;
+ }
+
+ *frame_num = frame_info.frame_num;
+ *type = frame_info.type;
+
+ if (frame_info.type == RTP_FRAME_AUDIO) {
+ // Resize buffer when needed
+ if (frame_info.len > *read_buff_bytes) {
+ while ((frame_info.len > *read_buff_bytes)) {
+ *read_buff_bytes *= 2;
+ }
+ *read_buff = (SAMPLE *) g_realloc(*read_buff, *read_buff_bytes);
+ }
+
+ sample_file_->seek(frame_info.sample_pos);
+ read_bytes = sample_file_->read((char *)*read_buff, frame_info.len);
+ } else {
+ // For silence we do nothing
+ read_bytes = frame_info.len;
+ }
+
+ *read_len = (spx_uint32_t)(read_bytes / SAMPLE_BYTES);
+
+ return true;
+}
+
+/*
+ * Functions for reading data during play
+ */
+void RtpAudioFile::setDataReadStage()
+{
+ sample_file_frame_->seek(0);
+ sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_));
+ real_pos_ = cur_frame_.real_pos;
+}
+
+bool RtpAudioFile::open(QIODevice::OpenMode mode)
+{
+ if (mode == QIODevice::ReadOnly) {
+ return true;
+ }
+
+ return false;
+}
+
+qint64 RtpAudioFile::size() const
+{
+ return real_size_;
+}
+
+qint64 RtpAudioFile::pos() const
+{
+ return real_pos_;
+}
+
+/*
+ * Seek starts from beginning of Frames and search one where offset belongs
+ * to. It looks inefficient, but seek is used usually just to jump to 0 or
+ * to skip first Frame where silence is stored.
+ */
+bool RtpAudioFile::seek(qint64 off)
+{
+ if (real_size_ <= off) {
+ // Can't seek above end of file
+ return false;
+ }
+
+ // Search for correct offset from first frame
+ sample_file_frame_->seek(0);
+ while (1) {
+ // Read frame
+ if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) {
+ // Can't read frame, some error occurred
+ return false;
+ }
+
+ if ((cur_frame_.real_pos + cur_frame_.len) > off) {
+ // We found correct frame
+ // Calculate offset in frame
+ qint64 diff = off - cur_frame_.real_pos;
+ qint64 new_real_pos = cur_frame_.real_pos + diff;
+ qint64 new_sample_pos = cur_frame_.sample_pos + diff;
+
+ if (cur_frame_.type == RTP_FRAME_AUDIO) {
+ // For audio frame we should to seek to correct place
+ if (!sample_file_->seek(new_sample_pos)) {
+ return false;
+ }
+ // Real seek was successful
+ real_pos_ = new_real_pos;
+ return true;
+ } else {
+ // For silence frame we blindly confirm it
+ real_pos_ = new_real_pos;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+qint64 RtpAudioFile::sampleFileSize()
+{
+ return real_size_;
+}
+
+void RtpAudioFile::seekSample(qint64 samples)
+{
+ seek(sizeof(SAMPLE) * samples);
+}
+
+qint64 RtpAudioFile::readFrameData(char *data , qint64 want_read)
+{
+ // Calculate remaining data in frame
+ qint64 remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos);
+ qint64 was_read;
+
+ if (remaining < want_read) {
+ // Incorrect call, can't read more than is stored in frame
+ return -1;
+ }
+
+ if (cur_frame_.type == RTP_FRAME_AUDIO) {
+ was_read = sample_file_->read(data, want_read);
+ real_pos_ += was_read;
+ } else {
+ memset(data, 0, want_read);
+ real_pos_ += want_read;
+ was_read = want_read;
+ }
+
+ return was_read;
+}
+
+qint64 RtpAudioFile::readSample(SAMPLE *sample)
+{
+ return read((char *)sample, sizeof(SAMPLE));
+}
+
+qint64 RtpAudioFile::getTotalSamples()
+{
+ return (real_size_/(qint64)sizeof(SAMPLE));
+}
+
+qint64 RtpAudioFile::getEndOfSilenceSample()
+{
+ if (cur_frame_.type == RTP_FRAME_SILENCE) {
+ return (cur_frame_.real_pos + cur_frame_.len) / (qint64)sizeof(SAMPLE);
+ } else {
+ return -1;
+ }
+}
+
+qint64 RtpAudioFile::readData(char *data, qint64 maxSize)
+{
+ qint64 to_read = maxSize;
+ qint64 can_read;
+ qint64 was_read = 0;
+ qint64 remaining;
+
+ while (1) {
+ // Calculate remaining data in frame
+ remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos);
+ if (remaining > to_read) {
+ // Even we want to read more, we can read just till end of frame
+ can_read = to_read;
+ } else {
+ can_read = remaining;
+ }
+ if (can_read==readFrameData(data, can_read)) {
+ to_read -= can_read;
+ data += can_read;
+ was_read += can_read;
+ if (real_pos_ >= cur_frame_.real_pos + cur_frame_.len) {
+ // We exhausted the frame, read next one
+ if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) {
+ // We are at the end of the file
+ return was_read;
+ }
+ if ((cur_frame_.type == RTP_FRAME_AUDIO) && (!sample_file_->seek(cur_frame_.sample_pos))) {
+ // We tried to seek to correct place, but it failed
+ return -1;
+ }
+ }
+ if (to_read == 0) {
+ return was_read;
+ }
+ } else {
+ return -1;
+ }
+ }
+}
+
+qint64 RtpAudioFile::writeData(const char *data _U_, qint64 maxSize _U_)
+{
+ // Writing is not supported
+ return -1;
+}
+
diff --git a/ui/qt/utils/rtp_audio_file.h b/ui/qt/utils/rtp_audio_file.h
new file mode 100644
index 00000000..addc3015
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_file.h
@@ -0,0 +1,93 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTP_AUDIO_FILE_H
+#define RTP_AUDIO_FILE_H
+
+#include "config.h"
+#include <ui/rtp_media.h>
+
+#include <speex/speex_resampler.h>
+
+#include <QIODevice>
+#include <QDir>
+#include <QTemporaryFile>
+#include <QDebug>
+#include <QBuffer>
+
+struct _rtp_info;
+
+typedef enum {
+ RTP_FRAME_AUDIO = 0,
+ RTP_FRAME_SILENCE
+} rtp_frame_type;
+
+// Structure used for storing frame num during visual waveform decoding
+typedef struct {
+ qint64 real_pos;
+ qint64 sample_pos;
+ qint64 len;
+ guint32 frame_num;
+ rtp_frame_type type;
+} rtp_frame_info;
+
+
+class RtpAudioFile: public QIODevice
+{
+public:
+ explicit RtpAudioFile(bool use_disk_for_temp, bool use_disk_for_frames);
+ ~RtpAudioFile();
+
+ // Functions for writing Frames
+ void setFrameWriteStage();
+ void frameWriteSilence(guint32 frame_num, qint64 samples);
+ qint64 frameWriteSamples(guint32 frame_num, const char *data, qint64 max_size);
+
+ // Functions for reading Frames
+ void setFrameReadStage(qint64 prepend_samples);
+ bool readFrameSamples(gint32 *read_buff_bytes, SAMPLE **read_buff, spx_uint32_t *read_len, guint32 *frame_num, rtp_frame_type *type);
+
+ // Functions for reading data during play
+ void setDataReadStage();
+ bool open(QIODevice::OpenMode mode) override;
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64 off) override;
+ qint64 sampleFileSize();
+ void seekSample(qint64 samples);
+ qint64 readSample(SAMPLE *sample);
+ qint64 getTotalSamples();
+ qint64 getEndOfSilenceSample();
+
+protected:
+ // Functions for reading data during play
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 maxSize) override;
+
+private:
+ QIODevice *sample_file_; // Stores waveform samples
+ QIODevice *sample_file_frame_; // Stores rtp_packet_info per packet
+ qint64 real_pos_;
+ qint64 real_size_;
+ qint64 sample_pos_;
+ qint64 sample_size_;
+ rtp_frame_info cur_frame_;
+
+ // Functions for writing Frames
+ qint64 frameWriteFrame(guint32 frame_num, qint64 real_pos, qint64 sample_pos, qint64 len, rtp_frame_type type);
+ void frameUpdateRealCounters(qint64 written_bytes);
+ void frameUpdateSampleCounters(qint64 written_bytes);
+
+ // Functions for reading Frames
+
+ // Functions for reading data during play
+ qint64 readFrameData(char *data , qint64 want_read);
+};
+
+#endif // RTP_AUDIO_FILE_H
diff --git a/ui/qt/utils/rtp_audio_routing.cpp b/ui/qt/utils/rtp_audio_routing.cpp
new file mode 100644
index 00000000..04ced435
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_routing.cpp
@@ -0,0 +1,110 @@
+/* rtp_audio_routing.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "rtp_audio_routing.h"
+
+AudioRouting::AudioRouting(bool muted, audio_routing_channel_t channel):
+ muted_(muted),
+ channel_(channel)
+{
+}
+
+char const *AudioRouting::formatAudioRoutingToString()
+{
+ if (muted_) {
+ return "Muted";
+ } else {
+ switch (channel_) {
+ case channel_any:
+ // Should not happen ever
+ return "ERR";
+ case channel_mono:
+ return "Play";
+ case channel_stereo_left:
+ return "L";
+ case channel_stereo_right:
+ return "R";
+ case channel_stereo_both:
+ return "L+R";
+ }
+ }
+
+ // Should not happen ever
+ return "ERR";
+}
+
+AudioRouting AudioRouting::getNextChannel(bool stereo_available)
+{
+ if (stereo_available) {
+ // Stereo
+ if (muted_) {
+ return AudioRouting(AUDIO_UNMUTED, channel_stereo_left);
+ } else {
+ switch (channel_) {
+ case channel_stereo_left:
+ return AudioRouting(AUDIO_UNMUTED, channel_stereo_both);
+ case channel_stereo_both:
+ return AudioRouting(AUDIO_UNMUTED, channel_stereo_right);
+ case channel_stereo_right:
+ return AudioRouting(AUDIO_MUTED, channel_stereo_right);
+ default:
+ return AudioRouting(AUDIO_UNMUTED, channel_stereo_left);
+ }
+ }
+ } else {
+ // Mono
+ if (muted_) {
+ return AudioRouting(AUDIO_UNMUTED, channel_mono);
+ } else {
+ return AudioRouting(AUDIO_MUTED, channel_mono);
+ }
+ }
+}
+
+AudioRouting AudioRouting::convert(bool stereo_available)
+{
+ // Muting is not touched by conversion
+
+ if (stereo_available) {
+ switch (channel_) {
+ case channel_mono:
+ // Mono -> Stereo
+ return AudioRouting(muted_, channel_stereo_both);
+ case channel_any:
+ // Unknown -> Unknown
+ return AudioRouting(muted_, channel_any);
+ default:
+ // Stereo -> Stereo
+ return AudioRouting(muted_, channel_);
+ }
+ } else {
+ switch (channel_) {
+ case channel_mono:
+ // Mono -> Mono
+ return AudioRouting(muted_, channel_mono);
+ case channel_any:
+ // Unknown -> Unknown
+ return AudioRouting(muted_, channel_any);
+ default:
+ // Stereo -> Mono
+ return AudioRouting(muted_, channel_mono);
+ }
+ }
+}
+
+void AudioRouting::mergeAudioRouting(AudioRouting new_audio_routing)
+{
+ if (new_audio_routing.getChannel() == channel_any) {
+ muted_ = new_audio_routing.isMuted();
+ } else {
+ muted_ = new_audio_routing.isMuted();
+ channel_ = new_audio_routing.getChannel();
+ }
+}
+
diff --git a/ui/qt/utils/rtp_audio_routing.h b/ui/qt/utils/rtp_audio_routing.h
new file mode 100644
index 00000000..51117b16
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_routing.h
@@ -0,0 +1,55 @@
+/** @file
+ *
+ * Declarations of RTP audio routing class
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTP_AUDIO_ROUTING_H
+#define RTP_AUDIO_ROUTING_H
+
+#include "config.h"
+
+#include <QMetaType>
+
+typedef enum {
+ channel_any, // Used just for changes of mute
+ channel_mono, // Play
+ channel_stereo_left, // L
+ channel_stereo_right, // R
+ channel_stereo_both // L+R
+} audio_routing_channel_t;
+
+class AudioRouting
+{
+public:
+ AudioRouting() = default;
+ ~AudioRouting() = default;
+ AudioRouting(const AudioRouting &) = default;
+ AudioRouting &operator=(const AudioRouting &) = default;
+
+ AudioRouting(bool muted, audio_routing_channel_t channel);
+ bool isMuted() { return muted_; }
+ void setMuted(bool muted) { muted_ = muted; }
+ audio_routing_channel_t getChannel() { return channel_; }
+ void setChannel(audio_routing_channel_t channel) { channel_ = channel; }
+ char const *formatAudioRoutingToString();
+ AudioRouting getNextChannel(bool stereo_available);
+ AudioRouting convert(bool stereo_available);
+ void mergeAudioRouting(AudioRouting new_audio_routing);
+
+private:
+ bool muted_;
+ audio_routing_channel_t channel_;
+};
+Q_DECLARE_METATYPE(AudioRouting)
+
+#define AUDIO_MUTED true
+#define AUDIO_UNMUTED false
+
+
+#endif // RTP_AUDIO_ROUTING_H
diff --git a/ui/qt/utils/rtp_audio_routing_filter.cpp b/ui/qt/utils/rtp_audio_routing_filter.cpp
new file mode 100644
index 00000000..8362d3f9
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_routing_filter.cpp
@@ -0,0 +1,114 @@
+/* rtp_audio_routing_filter.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "rtp_audio_routing_filter.h"
+
+AudioRoutingFilter::AudioRoutingFilter(QIODevice *input, bool stereo_required, AudioRouting audio_routing) :
+ QIODevice(input),
+ input_(input),
+ stereo_required_(stereo_required),
+ audio_routing_(audio_routing)
+{
+ QIODevice::open(input_->openMode());
+}
+
+void AudioRoutingFilter::close()
+{
+ input_->close();
+}
+
+qint64 AudioRoutingFilter::size() const
+{
+ if (!stereo_required_)
+ {
+ return input_->size();
+ } else {
+ // For stereo we must return twice more bytes
+ return input_->size() * 2;
+ }
+}
+
+qint64 AudioRoutingFilter::pos() const
+{
+ if (!stereo_required_)
+ {
+ return input_->pos();
+ } else {
+ // For stereo we must return twice more bytes
+ return input_->pos() * 2;
+ }
+}
+
+bool AudioRoutingFilter::seek(qint64 off)
+{
+ if (!stereo_required_)
+ {
+ return input_->seek(off);
+ } else {
+ // For stereo we must return half of offset
+ return input_->seek(off / 2);
+ }
+}
+
+qint64 AudioRoutingFilter::readData(char *data, qint64 maxSize)
+{
+ if (!stereo_required_)
+ {
+ // For mono we just return data
+ return input_->read(data, maxSize);
+ } else {
+ // For stereo
+ gint64 silence = 0;
+
+ // Read half of data
+ qint64 readBytes = input_->read(data, maxSize/SAMPLE_BYTES);
+
+ // If error or no data available, just return
+ if (readBytes < 1)
+ return readBytes;
+
+ // Expand it
+ for(qint64 i = (readBytes / SAMPLE_BYTES) - 1; i > 0; i--) {
+ qint64 j = SAMPLE_BYTES * i;
+ if (audio_routing_.getChannel() == channel_stereo_left) {
+ memcpy(&data[j*2], &data[j], SAMPLE_BYTES);
+ memcpy(&data[j*2+SAMPLE_BYTES], &silence, SAMPLE_BYTES);
+ } else if (audio_routing_.getChannel() == channel_stereo_right) {
+ memcpy(&data[j*2], &silence, SAMPLE_BYTES);
+ memcpy(&data[j*2+SAMPLE_BYTES], &data[j], SAMPLE_BYTES);
+ } else if (audio_routing_.getChannel() == channel_stereo_both) {
+ memcpy(&data[j*2], &data[j], SAMPLE_BYTES);
+ memcpy(&data[j*2+SAMPLE_BYTES], &data[j], SAMPLE_BYTES);
+ } else {
+ // Should not happen ever
+ memcpy(&data[j*2], &silence, SAMPLE_BYTES*2);
+ }
+ }
+
+ return readBytes * 2;
+ }
+}
+
+qint64 AudioRoutingFilter::writeData(const char *data, qint64 maxSize)
+{
+ return input_->write(data, maxSize);
+}
+
+
+/*
+bool AudioRoutingFilter::atEnd() const
+{
+ return input_->atEnd();
+}
+
+bool AudioRoutingFilter::canReadLine() const
+{
+ return input_->canReadLine();
+}
+*/
diff --git a/ui/qt/utils/rtp_audio_routing_filter.h b/ui/qt/utils/rtp_audio_routing_filter.h
new file mode 100644
index 00000000..de48edae
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_routing_filter.h
@@ -0,0 +1,42 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTP_AUDIO_ROUTING_FILTER_H
+#define RTP_AUDIO_ROUTING_FILTER_H
+
+#include "config.h"
+
+#include <ui/rtp_media.h>
+#include <ui/qt/utils/rtp_audio_routing.h>
+
+#include <QObject>
+#include <QIODevice>
+
+class AudioRoutingFilter: public QIODevice
+{
+public:
+ explicit AudioRoutingFilter(QIODevice *input, bool stereo_required, AudioRouting audio_routing);
+ ~AudioRoutingFilter() { }
+
+ void close() override;
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64 off) override;
+
+protected:
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 maxSize) override;
+
+private:
+ QIODevice *input_;
+ bool stereo_required_;
+ AudioRouting audio_routing_;
+};
+
+#endif // RTP_AUDIO_ROUTING_FILTER_H
diff --git a/ui/qt/utils/rtp_audio_silence_generator.cpp b/ui/qt/utils/rtp_audio_silence_generator.cpp
new file mode 100644
index 00000000..15289f3e
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_silence_generator.cpp
@@ -0,0 +1,47 @@
+/* rtp_audio_silence_stream.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "rtp_audio_silence_generator.h"
+#include <ws_attributes.h>
+
+AudioSilenceGenerator::AudioSilenceGenerator(QObject *parent) :
+ QIODevice(parent),
+ pos_(0)
+{
+ QIODevice::open(QIODevice::ReadOnly);
+}
+
+qint64 AudioSilenceGenerator::size() const
+{
+ return std::numeric_limits <qint64>::max();
+}
+
+qint64 AudioSilenceGenerator::pos() const
+{
+ return pos_;
+}
+
+bool AudioSilenceGenerator::seek(qint64 off)
+{
+ pos_ = off;
+ return true;
+}
+
+qint64 AudioSilenceGenerator::readData(char *data, qint64 maxSize)
+{
+ memset(data, 0, maxSize);
+ pos_ += maxSize;
+
+ return maxSize;
+}
+
+qint64 AudioSilenceGenerator::writeData(const char *data _U_, qint64 maxSize)
+{
+ return maxSize;
+}
diff --git a/ui/qt/utils/rtp_audio_silence_generator.h b/ui/qt/utils/rtp_audio_silence_generator.h
new file mode 100644
index 00000000..48a22856
--- /dev/null
+++ b/ui/qt/utils/rtp_audio_silence_generator.h
@@ -0,0 +1,35 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTP_AUDIO_SILENCE_GENERATOR_H
+#define RTP_AUDIO_SILENCE_GENERATOR_H
+
+#include "config.h"
+
+#include <QIODevice>
+
+class AudioSilenceGenerator: public QIODevice
+{
+public:
+ explicit AudioSilenceGenerator(QObject *parent = nullptr);
+ ~AudioSilenceGenerator() { }
+
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64 off) override;
+
+protected:
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 maxSize) override;
+
+private:
+ quint64 pos_;
+};
+
+#endif // RTP_AUDIO_SILENCE_GENERATOR_H
diff --git a/ui/qt/utils/stock_icon.cpp b/ui/qt/utils/stock_icon.cpp
new file mode 100644
index 00000000..dc5fb01a
--- /dev/null
+++ b/ui/qt/utils/stock_icon.cpp
@@ -0,0 +1,253 @@
+/* stock_icon.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/utils/stock_icon.h>
+
+// Stock icons. Based on gtk/stock_icons.h
+
+// Toolbar icon sizes:
+// macOS freestanding: 32x32, 32x32@2x, segmented (inside a button): <= 19x19
+// Windows: 16x16, 24x24, 32x32
+// GNOME: 24x24 (default), 48x48
+
+// References:
+//
+// https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+// https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
+//
+// https://mithatkonar.com/wiki/doku.php/qt/icons
+//
+// https://web.archive.org/web/20140829010224/https://developer.apple.com/library/mac/documentation/userexperience/conceptual/applehiguidelines/IconsImages/IconsImages.html
+// https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
+// https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon/
+// https://docs.microsoft.com/en-us/windows/win32/uxguide/vis-icons
+// https://developer.gnome.org/hig/stable/icons-and-artwork.html.en
+// https://docs.microsoft.com/en-us/visualstudio/designers/the-visual-studio-image-library
+
+// To do:
+// - 32x32, 48x48, 64x64, and unscaled (.svg) icons.
+// - Indent find & go actions when those panes are open.
+// - Replace or remove:
+// WIRESHARK_STOCK_CAPTURE_FILTER x-capture-filter
+// WIRESHARK_STOCK_DISPLAY_FILTER x-display-filter
+// GTK_STOCK_SELECT_COLOR x-coloring-rules
+// GTK_STOCK_PREFERENCES preferences-system
+// GTK_STOCK_HELP help-contents
+
+#include <QApplication>
+#include <QFile>
+#include <QFontMetrics>
+#include <QMap>
+#include <QPainter>
+#include <QPainterPath>
+#include <QStyle>
+#include <QStyleOption>
+
+static const QString path_pfx_ = ":/stock_icons/";
+
+// Map FreeDesktop icon names to Qt standard pixmaps.
+static QMap<QString, QStyle::StandardPixmap> icon_name_to_standard_pixmap_;
+
+StockIcon::StockIcon(const QString icon_name) :
+ QIcon()
+{
+ if (icon_name_to_standard_pixmap_.isEmpty()) {
+ fillIconNameMap();
+ }
+
+ // Does our theme contain this icon?
+ // X11 only as per the QIcon documentation.
+ if (hasThemeIcon(icon_name)) {
+ QIcon theme_icon = fromTheme(icon_name);
+ swap(theme_icon);
+ return;
+ }
+
+ // Is this is an icon we've manually mapped to a standard pixmap below?
+ if (icon_name_to_standard_pixmap_.contains(icon_name)) {
+ QIcon standard_icon = qApp->style()->standardIcon(icon_name_to_standard_pixmap_[icon_name]);
+ swap(standard_icon);
+ return;
+ }
+
+ // Is this one of our locally sourced, cage-free, organic icons?
+ QStringList types = QStringList() << "8x8" << "14x14" << "16x16" << "24x14" << "24x24";
+ QList<QIcon::Mode> icon_modes = QList<QIcon::Mode>()
+ << QIcon::Disabled
+ << QIcon::Active
+ << QIcon::Selected;
+ foreach (QString type, types) {
+ // First, check for a template (mask) icon
+ // Templates should be monochrome as described at
+ // https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/custom-icons/
+ // Transparency is supported.
+ QString icon_path_template = path_pfx_ + QString("%1/%2.template.png").arg(type).arg(icon_name);
+ if (QFile::exists(icon_path_template)) {
+ QIcon mask_icon = QIcon();
+ mask_icon.addFile(icon_path_template);
+
+ foreach(QSize sz, mask_icon.availableSizes()) {
+ QPixmap mask_pm = mask_icon.pixmap(sz);
+ QImage normal_img(sz, QImage::Format_ARGB32);
+ QPainter painter(&normal_img);
+ QBrush br(qApp->palette().color(QPalette::Active, QPalette::WindowText));
+ painter.fillRect(0, 0, sz.width(), sz.height(), br);
+ painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ painter.drawPixmap(0, 0, mask_pm);
+
+ QPixmap normal_pm = QPixmap::fromImage(normal_img);
+ addPixmap(normal_pm, QIcon::Normal, QIcon::On);
+ addPixmap(normal_pm, QIcon::Normal, QIcon::Off);
+
+ QStyleOption opt = {};
+ opt.palette = qApp->palette();
+ foreach (QIcon::Mode icon_mode, icon_modes) {
+ QPixmap mode_pm = qApp->style()->generatedIconPixmap(icon_mode, normal_pm, &opt);
+ addPixmap(mode_pm, icon_mode, QIcon::On);
+ addPixmap(mode_pm, icon_mode, QIcon::Off);
+ }
+ }
+ continue;
+ }
+
+ // Regular full-color icons
+ QString icon_path = path_pfx_ + QString("%1/%2.png").arg(type).arg(icon_name);
+ if (QFile::exists(icon_path)) {
+ addFile(icon_path);
+ }
+
+ // Along with each name check for "<name>.active" and
+ // "<name>.selected" for the Active and Selected modes, and
+ // "<name>.on" to use for the on (checked) state.
+ // XXX Allow more (or all) combinations.
+ QString icon_path_active = path_pfx_ + QString("%1/%2.active.png").arg(type).arg(icon_name);
+ if (QFile::exists(icon_path_active)) {
+ addFile(icon_path_active, QSize(), QIcon::Active, QIcon::On);
+ }
+
+ QString icon_path_selected = path_pfx_ + QString("%1/%2.selected.png").arg(type).arg(icon_name);
+ if (QFile::exists(icon_path_selected)) {
+ addFile(icon_path_selected, QSize(), QIcon::Selected, QIcon::On);
+ }
+
+ QString icon_path_on = path_pfx_ + QString("%1/%2.on.png").arg(type).arg(icon_name);
+ if (QFile::exists(icon_path_on)) {
+ addFile(icon_path_on, QSize(), QIcon::Normal, QIcon::On);
+ }
+ }
+}
+
+// Create a square icon filled with the specified color.
+QIcon StockIcon::colorIcon(const QRgb bg_color, const QRgb fg_color, const QString glyph)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QRect border(0, 0, size - 1, size - 1);
+ painter.setPen(fg_color);
+ painter.setBrush(QColor(bg_color));
+ painter.drawRect(border);
+
+ if (!glyph.isEmpty()) {
+ QFont font(qApp->font());
+ font.setPointSizeF(size / 2.0);
+ painter.setFont(font);
+ QRectF bounding = painter.boundingRect(pm.rect(), glyph, Qt::AlignHCenter | Qt::AlignVCenter);
+ painter.drawText(bounding, glyph);
+ }
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+// Create a triangle icon filled with the specified color.
+QIcon StockIcon::colorIconTriangle(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QPainterPath triangle;
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(fg_color);
+ painter.setBrush(QColor(bg_color));
+ triangle.moveTo(0, size-1);
+ triangle.lineTo(size-1, size-1);
+ triangle.lineTo((size-1)/2, 0);
+ triangle.closeSubpath();
+ painter.fillPath(triangle, QColor(bg_color));
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+// Create a cross icon filled with the specified color.
+QIcon StockIcon::colorIconCross(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QPainterPath cross;
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(QPen(QBrush(bg_color), 3));
+ painter.setBrush(QColor(fg_color));
+ cross.moveTo(0, 0);
+ cross.lineTo(size-1, size-1);
+ cross.moveTo(0, size-1);
+ cross.lineTo(size-1, 0);
+ painter.drawPath(cross);
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+// Create a circle icon filled with the specified color.
+QIcon StockIcon::colorIconCircle(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QRect border(2, 2, size - 3, size - 3);
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(QPen(QBrush(bg_color), 3));
+ painter.setBrush(QColor(fg_color));
+ painter.setBrush(QColor(bg_color));
+ painter.drawEllipse(border);
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+void StockIcon::fillIconNameMap()
+{
+ // Note that some of Qt's standard pixmaps are awful. We shouldn't add an
+ // entry just because a match can be made.
+ icon_name_to_standard_pixmap_["document-open"] = QStyle::SP_DirIcon;
+ icon_name_to_standard_pixmap_["media-playback-pause"] = QStyle::SP_MediaPause;
+ icon_name_to_standard_pixmap_["media-playback-start"] = QStyle::SP_MediaPlay;
+ icon_name_to_standard_pixmap_["media-playback-stop"] = QStyle::SP_MediaStop;
+}
diff --git a/ui/qt/utils/stock_icon.h b/ui/qt/utils/stock_icon.h
new file mode 100644
index 00000000..5b333736
--- /dev/null
+++ b/ui/qt/utils/stock_icon.h
@@ -0,0 +1,40 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef STOCK_ICON_H
+#define STOCK_ICON_H
+
+#include <QIcon>
+
+/** @file
+ * Goal: Beautiful icons appropriate for each of our supported platforms.
+ */
+
+// Supported standard names:
+// document-open
+
+// Supported custom names (see images/toolbar):
+// x-capture-file-close
+// x-capture-file-save
+
+class StockIcon : public QIcon
+{
+public:
+ explicit StockIcon(const QString icon_name);
+
+ static QIcon colorIcon(const QRgb bg_color, const QRgb fg_color, const QString glyph = QString());
+ static QIcon colorIconTriangle(const QRgb bg_color, const QRgb fg_color);
+ static QIcon colorIconCross(const QRgb bg_color, const QRgb fg_color);
+ static QIcon colorIconCircle(const QRgb bg_color, const QRgb fg_color);
+
+private:
+ void fillIconNameMap();
+};
+
+#endif // STOCK_ICON_H
diff --git a/ui/qt/utils/tango_colors.h b/ui/qt/utils/tango_colors.h
new file mode 100644
index 00000000..fb63dd3a
--- /dev/null
+++ b/ui/qt/utils/tango_colors.h
@@ -0,0 +1,75 @@
+/** @file
+ *
+ * Tango theme colors
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TANGO_COLORS_H__
+#define __TANGO_COLORS_H__
+
+// http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines
+// with added hues from http://emilis.info/other/extended_tango/
+// (all colors except aluminium)
+
+const QRgb tango_aluminium_1 = 0xeeeeec;
+const QRgb tango_aluminium_2 = 0xd3d7cf;
+const QRgb tango_aluminium_3 = 0xbabdb6;
+const QRgb tango_aluminium_4 = 0x888a85;
+const QRgb tango_aluminium_5 = 0x555753;
+const QRgb tango_aluminium_6 = 0x2e3436;
+
+const QRgb tango_butter_1 = 0xfeffd0;
+const QRgb tango_butter_2 = 0xfffc9c;
+const QRgb tango_butter_3 = 0xfce94f;
+const QRgb tango_butter_4 = 0xedd400;
+const QRgb tango_butter_5 = 0xc4a000;
+const QRgb tango_butter_6 = 0x725000;
+
+const QRgb tango_chameleon_1 = 0xe4ffc7;
+const QRgb tango_chameleon_2 = 0xb7f774;
+const QRgb tango_chameleon_3 = 0x8ae234;
+const QRgb tango_chameleon_4 = 0x73d216;
+const QRgb tango_chameleon_5 = 0x4e9a06;
+const QRgb tango_chameleon_6 = 0x2a5703;
+
+const QRgb tango_chocolate_1 = 0xfaf0d7;
+const QRgb tango_chocolate_2 = 0xefd0a7;
+const QRgb tango_chocolate_3 = 0xe9b96e;
+const QRgb tango_chocolate_4 = 0xc17d11;
+const QRgb tango_chocolate_5 = 0x8f5902;
+const QRgb tango_chocolate_6 = 0x503000;
+
+const QRgb tango_orange_1 = 0xfff0d7;
+const QRgb tango_orange_2 = 0xffd797;
+const QRgb tango_orange_3 = 0xfcaf3e;
+const QRgb tango_orange_4 = 0xf57900;
+const QRgb tango_orange_5 = 0xce5c00;
+const QRgb tango_orange_6 = 0x8c3700;
+
+const QRgb tango_plum_1 = 0xfce0ff;
+const QRgb tango_plum_2 = 0xe0c0e4;
+const QRgb tango_plum_3 = 0xad7fa8;
+const QRgb tango_plum_4 = 0x75507b;
+const QRgb tango_plum_5 = 0x5c3566;
+const QRgb tango_plum_6 = 0x371740;
+
+const QRgb tango_scarlet_red_1 = 0xffcccc;
+const QRgb tango_scarlet_red_2 = 0xf78787;
+const QRgb tango_scarlet_red_3 = 0xef2929;
+const QRgb tango_scarlet_red_4 = 0xcc0000;
+const QRgb tango_scarlet_red_5 = 0xa40000;
+const QRgb tango_scarlet_red_6 = 0x600000;
+
+const QRgb tango_sky_blue_1 = 0xdaeeff;
+const QRgb tango_sky_blue_2 = 0x97c4f0;
+const QRgb tango_sky_blue_3 = 0x729fcf;
+const QRgb tango_sky_blue_4 = 0x3465a4;
+const QRgb tango_sky_blue_5 = 0x204a87;
+const QRgb tango_sky_blue_6 = 0x0a3050;
+
+#endif // __TANGO_COLORS_H__
diff --git a/ui/qt/utils/variant_pointer.h b/ui/qt/utils/variant_pointer.h
new file mode 100644
index 00000000..7a95a94c
--- /dev/null
+++ b/ui/qt/utils/variant_pointer.h
@@ -0,0 +1,34 @@
+/** @file
+ *
+ * Range routines
+ *
+ * Roland Knall <rknall@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef UI_QT_VARIANT_POINTER_H_
+#define UI_QT_VARIANT_POINTER_H_
+
+#include <QVariant>
+
+template <typename T> class VariantPointer
+{
+
+public:
+ static T* asPtr(QVariant v)
+ {
+ return (T *) v.value<void *>();
+ }
+
+ static QVariant asQVariant(T* ptr)
+ {
+ return QVariant::fromValue((void *) ptr);
+ }
+};
+
+#endif /* UI_QT_VARIANT_POINTER_H_ */
diff --git a/ui/qt/utils/wireshark_mime_data.cpp b/ui/qt/utils/wireshark_mime_data.cpp
new file mode 100644
index 00000000..f1b588d0
--- /dev/null
+++ b/ui/qt/utils/wireshark_mime_data.cpp
@@ -0,0 +1,52 @@
+/* wireshark_mime_data.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <utils/wireshark_mime_data.h>
+
+const QString WiresharkMimeData::ColoringRulesMimeType = "application/vnd.wireshark.coloringrules";
+const QString WiresharkMimeData::ColumnListMimeType = "application/vnd.wireshark.columnlist";
+const QString WiresharkMimeData::FilterListMimeType = "application/vnd.wireshark.filterlist";
+const QString WiresharkMimeData::DisplayFilterMimeType = "application/vnd.wireshark.displayfilter";
+
+void WiresharkMimeData::allowPlainText()
+{
+ setText(labelText());
+}
+
+ToolbarEntryMimeData::ToolbarEntryMimeData(QString element, int pos) :
+ WiresharkMimeData(),
+ element_(element),
+ filter_(QString()),
+ pos_(pos)
+{}
+
+QString ToolbarEntryMimeData::element() const
+{
+ return element_;
+}
+
+QString ToolbarEntryMimeData::labelText() const
+{
+ return QString("%1").arg(element_);
+}
+
+int ToolbarEntryMimeData::position() const
+{
+ return pos_;
+}
+
+void ToolbarEntryMimeData::setFilter(QString text)
+{
+ filter_ = text;
+}
+
+QString ToolbarEntryMimeData::filter() const
+{
+ return filter_;
+}
diff --git a/ui/qt/utils/wireshark_mime_data.h b/ui/qt/utils/wireshark_mime_data.h
new file mode 100644
index 00000000..4732f39a
--- /dev/null
+++ b/ui/qt/utils/wireshark_mime_data.h
@@ -0,0 +1,47 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef UI_QT_UTILS_WIRESHARK_MIME_DATA_H_
+#define UI_QT_UTILS_WIRESHARK_MIME_DATA_H_
+
+#include <QMimeData>
+
+class WiresharkMimeData: public QMimeData {
+public:
+ virtual QString labelText() const = 0;
+ virtual void allowPlainText();
+
+ static const QString ColoringRulesMimeType;
+ static const QString ColumnListMimeType;
+ static const QString FilterListMimeType;
+ static const QString DisplayFilterMimeType;
+};
+
+class ToolbarEntryMimeData: public WiresharkMimeData {
+ Q_OBJECT
+public:
+
+ ToolbarEntryMimeData(QString element, int pos);
+
+ int position() const;
+ QString element() const;
+ QString filter() const;
+ void setFilter(QString);
+
+ QString labelText() const override;
+
+private:
+
+ QString element_;
+ QString filter_;
+ int pos_;
+
+};
+
+#endif /* UI_QT_UTILS_WIRESHARK_MIME_DATA_H_ */
diff --git a/ui/qt/utils/wireshark_zip_helper.cpp b/ui/qt/utils/wireshark_zip_helper.cpp
new file mode 100644
index 00000000..7bfa0101
--- /dev/null
+++ b/ui/qt/utils/wireshark_zip_helper.cpp
@@ -0,0 +1,283 @@
+/* wireshark_zip_helper.cpp
+ *
+ * Definitions for zip / unzip of files
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/utils/wireshark_zip_helper.h>
+
+#ifdef HAVE_MINIZIP
+#include "config.h"
+
+#include "glib.h"
+
+#include <iosfwd>
+#include <iostream>
+#include <zlib.h> // For Z_DEFLATED, etc.
+#include <minizip/unzip.h>
+#include <minizip/zip.h>
+
+#include "epan/prefs.h"
+#include "wsutil/file_util.h"
+
+#include <QDataStream>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QDateTime>
+#include <QMap>
+
+/* Whether we are using minizip-ng and it uses an incompatible 'dos_date'
+ * struct member. */
+#ifdef HAVE_MZCOMPAT_DOS_DATE
+#define _MZDOSDATE dos_date
+#else
+#define _MZDOSDATE dosDate
+#endif
+
+bool WiresharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int), QString (*cleanName)(QString))
+{
+ unzFile uf = Q_NULLPTR;
+ QFileInfo fi(zipFile);
+ QDir di(directory);
+ int files = 0;
+
+ if (! fi.exists() || ! di.exists())
+ return false;
+
+ if ((uf = unzOpen64(zipFile.toUtf8().constData())) == Q_NULLPTR)
+ return false;
+
+ unz_global_info64 gi;
+ int err;
+ unzGetGlobalInfo64(uf,&gi);
+ unsigned int nmbr = static_cast<unsigned int>(gi.number_entry);
+ if (nmbr <= 0)
+ return false;
+
+ QMap<QString, QString> cleanPaths;
+
+ for (unsigned int cnt = 0; cnt < nmbr; cnt++)
+ {
+ char filename_inzip[256];
+ unz_file_info64 file_info;
+ err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip),
+ Q_NULLPTR, 0, Q_NULLPTR, 0);
+ if (err == UNZ_OK)
+ {
+ QString fileInZip(filename_inzip);
+ int fileSize = static_cast<int>(file_info.uncompressed_size);
+
+ /* Sanity check for the file */
+ if (fileInZip.length() == 0 || (fileCheck && ! fileCheck(fileInZip, fileSize)) )
+ {
+ if ((cnt + 1) < nmbr)
+ {
+ err = unzGoToNextFile(uf);
+ if (err != UNZ_OK)
+ {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (di.exists())
+ {
+#ifdef _WIN32
+ /* This is an additional fix for bug 16608, in which exports did contain the full path they
+ * where exported from, leading to imports not possible if the path does not exist on that
+ * machine */
+
+ if (fileInZip.contains(":/") || fileInZip.contains(":\\"))
+ {
+ QFileInfo fileName(fileInZip);
+ QFileInfo path(fileName.dir(), "");
+ QString newFile = path.baseName() + "/" + fileName.baseName();
+ fileInZip = newFile;
+ }
+#endif
+
+ QString fullPath = di.path() + "/" + fileInZip;
+ QFileInfo fi(fullPath);
+ QString dirPath = fi.absolutePath();
+
+ /* clean up name from import. e.g. illegal characters in name */
+ if (cleanName)
+ {
+ if (! cleanPaths.keys().contains(dirPath))
+ {
+ QString tempPath = cleanName(dirPath);
+ int cnt = 1;
+ while (QFile::exists(tempPath))
+ {
+ tempPath = cleanName(dirPath) + QString::number(cnt);
+ cnt++;
+ }
+ cleanPaths.insert(dirPath, tempPath);
+ }
+
+ dirPath = cleanPaths[dirPath];
+ if (dirPath.length() == 0)
+ continue;
+
+ fi = QFileInfo(dirPath + "/" + fi.fileName());
+ fullPath = fi.absoluteFilePath();
+ }
+ if (fullPath.length() == 0)
+ continue;
+
+ QDir tP(fi.absolutePath());
+ if (! tP.exists())
+ di.mkpath(fi.absolutePath());
+
+ if (fileInZip.contains("/"))
+ {
+ QString filePath = fi.absoluteFilePath();
+ QFile file(filePath);
+ if (! file.exists())
+ {
+ err = unzOpenCurrentFile(uf);
+ if (err == UNZ_OK)
+ {
+ if (file.open(QIODevice::WriteOnly))
+ {
+ QByteArray buf;
+ buf.resize(IO_BUF_SIZE);
+ while ((err = unzReadCurrentFile(uf, buf.data(), static_cast<int>(buf.size()))) != UNZ_EOF)
+ file.write(buf.constData(), err);
+
+ file.close();
+ }
+ unzCloseCurrentFile(uf);
+
+ files++;
+ }
+ }
+ }
+ }
+ }
+
+ if ((cnt+1) < nmbr)
+ {
+ err = unzGoToNextFile(uf);
+ if (err!=UNZ_OK)
+ {
+ break;
+ }
+ }
+ }
+
+ unzClose(uf);
+
+ return files > 0 ? true : false;
+}
+
+#ifndef UINT32_MAX
+#define UINT32_MAX (0xffffffff)
+#endif
+
+static uint32_t qDateToDosDate(QDateTime time)
+{
+ QDate ld = time.toLocalTime().date();
+
+ int year = ld.year() - 1900;
+ if (year >= 1980)
+ year -= 1980;
+ else if (year >= 80)
+ year -= 80;
+ else
+ year += 20;
+
+ int month = ld.month() - 1;
+ int day = ld.day();
+
+ if (year < 0 || year > 127 || month < 1 || month > 31)
+ return 0;
+
+ QTime lt = time.toLocalTime().time();
+
+ unsigned int dosDate = static_cast<unsigned int>((day + (32 * (month + 1)) + (512 * year)));
+ unsigned int dosTime = static_cast<unsigned int>((lt.second() / 2) + (32 * lt.minute()) + (2048 * lt.hour()));
+
+ return (uint32_t)(dosDate << 16 | dosTime);
+}
+
+void WiresharkZipHelper::addFileToZip(zipFile zf, QString filepath, QString fileInZip)
+{
+ QFileInfo fi(filepath);
+ zip_fileinfo zi;
+ int err = ZIP_OK;
+
+ memset(&zi, 0, sizeof(zi));
+
+ QDateTime fTime = fi.lastModified();
+ zi._MZDOSDATE = qDateToDosDate(fTime);
+
+ QFile fh(filepath);
+ /* Checks if a large file block has to be written */
+ bool isLarge = (fh.size() > UINT32_MAX);
+
+ err = zipOpenNewFileInZip3_64(zf, fileInZip.toUtf8().constData(), &zi,
+ Q_NULLPTR, 0, Q_NULLPTR, 0, Q_NULLPTR, Z_DEFLATED, 9 , 0,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+ Q_NULLPTR, 0, static_cast<int>(isLarge));
+
+ if (err != ZIP_OK)
+ return;
+
+ if (fh.open(QIODevice::ReadOnly))
+ {
+ QByteArray buf;
+ buf.resize(IO_BUF_SIZE);
+ while (! fh.atEnd() && err == ZIP_OK)
+ {
+ qint64 bytesIn = fh.read(buf.data(), buf.size());
+ if (bytesIn > 0 && bytesIn <= buf.size())
+ {
+ err = zipWriteInFileInZip(zf, buf, (unsigned int) bytesIn);
+ }
+ }
+ fh.close();
+ }
+
+ zipCloseFileInZip(zf);
+}
+
+bool WiresharkZipHelper::zip(QString fileName, QStringList files, QString relativeTo)
+{
+
+ QFileInfo fi(fileName);
+ if (fi.exists())
+ QFile::remove(fileName);
+
+ zipFile zf = zipOpen(fileName.toUtf8().constData(), APPEND_STATUS_CREATE);
+ if (zf == Q_NULLPTR)
+ return false;
+
+ for (int cnt = 0; cnt < files.count(); cnt++)
+ {
+ QFileInfo sf(files.at(cnt));
+ QString fileInZip = sf.absoluteFilePath();
+ QFileInfo relat(relativeTo);
+ fileInZip.replace(relat.absoluteFilePath(), "");
+ /* Windows cannot open zip files, if the filenames starts with a separator */
+ while (fileInZip.length() > 0 && fileInZip.startsWith("/"))
+ fileInZip = fileInZip.right(fileInZip.length() - 1);
+
+ WiresharkZipHelper::addFileToZip(zf, sf.absoluteFilePath(), fileInZip);
+
+ }
+
+ if (zipClose(zf, Q_NULLPTR))
+ return false;
+
+ return true;
+}
+
+#endif
diff --git a/ui/qt/utils/wireshark_zip_helper.h b/ui/qt/utils/wireshark_zip_helper.h
new file mode 100644
index 00000000..2f8e39f8
--- /dev/null
+++ b/ui/qt/utils/wireshark_zip_helper.h
@@ -0,0 +1,36 @@
+/** @file
+ *
+ * Definitions for zip / unzip of files
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef WS_ZIP_HELPER_H
+#define WS_ZIP_HELPER_H
+
+#include "config.h"
+
+#include <QDir>
+
+#ifdef HAVE_MINIZIP
+
+#include "minizip/zip.h"
+
+class WiresharkZipHelper
+{
+public:
+ static bool zip(QString zipFile, QStringList files, QString relativeTo = QString());
+ static bool unzip(QString zipFile, QString directory, bool (*fileCheck)(QString fileName, int fileSize) = Q_NULLPTR, QString (*cleanName)(QString name) = Q_NULLPTR);
+
+protected:
+ static void addFileToZip(zipFile zf, QString filepath, QString fileInZip);
+
+};
+
+#endif
+
+#endif // WS_ZIP_HELPER_H