diff options
Diffstat (limited to 'ui/qt/utils')
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 |