forked from osmocom/wireshark
360 lines
11 KiB
C++
360 lines
11 KiB
C++
/* byte_view_tab.cpp
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "byte_view_tab.h"
|
|
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
#include <QMimeData>
|
|
#include <QTabBar>
|
|
|
|
#include "cfile.h"
|
|
#include "epan/epan_dissect.h"
|
|
#include "epan/tvbuff-int.h"
|
|
|
|
#include <wireshark_application.h>
|
|
|
|
#include <ui/qt/utils/variant_pointer.h>
|
|
#include <ui/qt/widgets/byte_view_text.h>
|
|
|
|
#define tvb_data_property "tvb_data_property"
|
|
|
|
// To do:
|
|
// - We might want to add a callback to free_data_sources in so that we
|
|
// don't have to blindly call clear().
|
|
|
|
ByteViewTab::ByteViewTab(QWidget *parent, epan_dissect_t *edt_fixed) :
|
|
QTabWidget(parent),
|
|
cap_file_(0),
|
|
is_fixed_packet_(edt_fixed != NULL),
|
|
edt_(edt_fixed)
|
|
{
|
|
setAccessibleName(tr("Packet bytes"));
|
|
setTabPosition(QTabWidget::South);
|
|
setDocumentMode(true);
|
|
|
|
// Shrink down to a small but nonzero size in the main splitter.
|
|
int one_em = fontMetrics().height();
|
|
setMinimumSize(one_em, one_em);
|
|
|
|
if (!edt_fixed) {
|
|
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow()));
|
|
}
|
|
}
|
|
|
|
// Connects the byte view with the main window, acting on changes to the packet
|
|
// list selection. It MUST NOT be used with the packet dialog as that is
|
|
// independent of the selection in the packet list.
|
|
void ByteViewTab::connectToMainWindow()
|
|
{
|
|
connect(this, SIGNAL(fieldSelected(FieldInformation *)),
|
|
wsApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)));
|
|
connect(this, SIGNAL(fieldHighlight(FieldInformation *)),
|
|
wsApp->mainWindow(), SIGNAL(fieldHighlight(FieldInformation *)));
|
|
|
|
/* Connect change of packet selection */
|
|
connect(wsApp->mainWindow(), SIGNAL(frameSelected(int)), this, SLOT(selectedFrameChanged(int)));
|
|
connect(wsApp->mainWindow(), SIGNAL(setCaptureFile(capture_file*)), this, SLOT(setCaptureFile(capture_file*)));
|
|
connect(wsApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)), this, SLOT(selectedFieldChanged(FieldInformation *)));
|
|
|
|
connect(wsApp->mainWindow(), SIGNAL(captureActive(int)), this, SLOT(captureActive(int)));
|
|
}
|
|
|
|
void ByteViewTab::captureActive(int cap)
|
|
{
|
|
if ( cap == 0 )
|
|
{
|
|
QList<ByteViewText *> allBVTs = findChildren<ByteViewText *>();
|
|
if ( allBVTs.count() > 0 )
|
|
{
|
|
ByteViewText * bvt = allBVTs.at(0);
|
|
tvbuff_t * stored = VariantPointer<tvbuff_t>::asPtr(bvt->property(tvb_data_property));
|
|
|
|
if ( ! stored )
|
|
selectedFrameChanged(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ByteViewTab::addTab(const char *name, tvbuff_t *tvb) {
|
|
if (count() == 1) { // Remove empty placeholder.
|
|
ByteViewText *cur_text = qobject_cast<ByteViewText *>(currentWidget());
|
|
if (cur_text && cur_text->isEmpty()) delete currentWidget();
|
|
}
|
|
|
|
packet_char_enc encoding = PACKET_CHAR_ENC_CHAR_ASCII;
|
|
if ( cap_file_ && cap_file_->current_frame )
|
|
encoding = (packet_char_enc)cap_file_->current_frame->encoding;
|
|
|
|
QByteArray data;
|
|
if ( tvb ) {
|
|
int data_len = (int) tvb_captured_length(tvb);
|
|
if (data_len > 0) {
|
|
// Note: this does not copy the data and will be invalidated when
|
|
// the tvb becomes invalid (e.g. when the current packet changes).
|
|
data = QByteArray::fromRawData((const char *) tvb_get_ptr(tvb, 0, data_len), data_len);
|
|
}
|
|
}
|
|
|
|
ByteViewText * byte_view_text = new ByteViewText(data, encoding, this);
|
|
byte_view_text->setAccessibleName(name);
|
|
byte_view_text->setMonospaceFont(wsApp->monospaceFont(true));
|
|
|
|
if ( tvb )
|
|
{
|
|
byte_view_text->setProperty(tvb_data_property, VariantPointer<tvbuff_t>::asQVariant(tvb));
|
|
|
|
connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont)));
|
|
|
|
connect(byte_view_text, SIGNAL(byteHovered(int)), this, SLOT(byteViewTextHovered(int)));
|
|
connect(byte_view_text, SIGNAL(byteSelected(int)), this, SLOT(byteViewTextMarked(int)));
|
|
}
|
|
|
|
int idx = QTabWidget::addTab(byte_view_text, name);
|
|
byte_view_text->setProperty("tab_index", QVariant::fromValue(idx));
|
|
|
|
QTabWidget::setTabToolTip(idx, name);
|
|
}
|
|
|
|
void ByteViewTab::byteViewTextHovered(int idx)
|
|
{
|
|
if ( idx >= 0 && edt_ )
|
|
{
|
|
tvbuff_t * tvb = VariantPointer<tvbuff_t>::asPtr(sender()->property(tvb_data_property));
|
|
proto_tree * tree = edt_->tree;
|
|
|
|
if ( tvb && tree )
|
|
{
|
|
field_info * fi = proto_find_field_from_offset(tree, idx, tvb);
|
|
if ( fi )
|
|
{
|
|
FieldInformation finfo(fi, this);
|
|
highlightedFieldChanged(&finfo);
|
|
emit fieldHighlight(&finfo);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
emit fieldHighlight((FieldInformation *)0);
|
|
}
|
|
|
|
void ByteViewTab::byteViewTextMarked(int idx)
|
|
{
|
|
if ( idx >= 0 && edt_ )
|
|
{
|
|
tvbuff_t * tvb = VariantPointer<tvbuff_t>::asPtr(sender()->property(tvb_data_property));
|
|
proto_tree * tree = edt_->tree;
|
|
|
|
if ( tvb && tree )
|
|
{
|
|
field_info * fi = proto_find_field_from_offset(tree, idx, tvb);
|
|
if ( fi )
|
|
{
|
|
FieldInformation finfo(fi, this);
|
|
emit fieldSelected(&finfo);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
emit fieldSelected((FieldInformation *)0);
|
|
}
|
|
|
|
ByteViewText * ByteViewTab::findByteViewTextForTvb(tvbuff_t * search_tvb, int * idx)
|
|
{
|
|
|
|
ByteViewText * item = 0;
|
|
if ( ! search_tvb )
|
|
return item;
|
|
|
|
bool found = false;
|
|
|
|
QList<ByteViewText *> allBVTs = findChildren<ByteViewText *>();
|
|
unsigned int length = search_tvb->length;
|
|
for (int i = 0; i < allBVTs.size() && ! found; ++i)
|
|
{
|
|
ByteViewText * bvt = allBVTs.at(i);
|
|
tvbuff_t * stored = VariantPointer<tvbuff_t>::asPtr(bvt->property(tvb_data_property));
|
|
if ( stored == search_tvb )
|
|
{
|
|
found = true;
|
|
}
|
|
else if ( stored )
|
|
{
|
|
if ( stored->length >= length && tvb_memeql(search_tvb, 0, tvb_get_ptr(stored, 0, length), length ) == 0 )
|
|
{
|
|
/* In packetDialog we do not match, because we came from different data sources.
|
|
* Assuming the capture files match, this should be a sufficient enough difference */
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if ( found )
|
|
{
|
|
int wdgIdx = bvt->property("tab_index").toInt();
|
|
if ( idx )
|
|
{
|
|
*idx = wdgIdx;
|
|
}
|
|
item = (ByteViewText *)widget(wdgIdx);
|
|
}
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
void ByteViewTab::tabInserted(int tab_index) {
|
|
setTabsVisible();
|
|
QTabWidget::tabInserted(tab_index);
|
|
}
|
|
|
|
void ByteViewTab::tabRemoved(int tab_index) {
|
|
setTabsVisible();
|
|
QTabWidget::tabRemoved(tab_index);
|
|
}
|
|
|
|
void ByteViewTab::setTabsVisible() {
|
|
if (count() > 1)
|
|
tabBar()->show();
|
|
else
|
|
tabBar()->hide();
|
|
}
|
|
|
|
void ByteViewTab::selectedFrameChanged(int frameNum)
|
|
{
|
|
clear();
|
|
qDeleteAll(findChildren<ByteViewText *>());
|
|
|
|
if (!is_fixed_packet_) {
|
|
/* If this is not a fixed packet (not the packet dialog), it must be the
|
|
* byte view associated with the packet list. */
|
|
if (cap_file_ && cap_file_->edt) {
|
|
/* Assumes that this function is called as a result of selecting a
|
|
* packet in the packet list (PacketList::selectionChanged). That
|
|
* invokes "cf_select_packet" which will update "cap_file_->edt". */
|
|
edt_ = cap_file_->edt;
|
|
} else {
|
|
/* capture file is closing or packet is deselected. */
|
|
edt_ = NULL;
|
|
}
|
|
}
|
|
|
|
if ( frameNum >= 0 )
|
|
{
|
|
if ( ! cap_file_ || ! cap_file_->edt )
|
|
return;
|
|
|
|
/* This code relies on a dissection, which had happened somewhere else. It also does not
|
|
* really check, if the dissection happened for the correct frame. In the future we might
|
|
* rewrite this for directly calling the dissection engine here. */
|
|
GSList *src_le;
|
|
for (src_le = edt_->pi.data_src; src_le != NULL; src_le = src_le->next) {
|
|
struct data_source *source;
|
|
char* source_name;
|
|
source = (struct data_source *)src_le->data;
|
|
source_name = get_data_source_name(source);
|
|
addTab(source_name, get_data_source_tvb(source));
|
|
wmem_free(NULL, source_name);
|
|
}
|
|
}
|
|
else
|
|
addTab("PlaceHolder", 0);
|
|
|
|
setCurrentIndex(0);
|
|
}
|
|
|
|
void ByteViewTab::selectedFieldChanged(FieldInformation *selected)
|
|
{
|
|
// We need to handle both selection and deselection.
|
|
ByteViewText * byte_view_text = qobject_cast<ByteViewText *>(currentWidget());
|
|
int f_start = -1, f_length = -1;
|
|
int p_start = -1, p_length = -1;
|
|
int fa_start = -1, fa_length = -1;
|
|
|
|
if (selected) {
|
|
if (selected->parent() == this) {
|
|
// We only want inbound signals.
|
|
return;
|
|
}
|
|
const field_info *fi = selected->fieldInfo();
|
|
|
|
int idx = 0;
|
|
if ( fi )
|
|
byte_view_text = findByteViewTextForTvb(fi->ds_tvb, &idx);
|
|
|
|
if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
|
|
// In the hex view, only highlight the target bytes or string. The entire
|
|
// field can then be displayed by clicking on any of the bytes in the field.
|
|
f_start = cap_file_->search_pos - cap_file_->search_len + 1;
|
|
f_length = (int) cap_file_->search_len;
|
|
} else {
|
|
f_start = selected->position().start;
|
|
f_length = selected->position().length;
|
|
}
|
|
|
|
setCurrentIndex(idx);
|
|
|
|
p_start = selected->parentField()->position().start;
|
|
p_length = selected->parentField()->position().length;
|
|
fa_start = selected->appendix().start;
|
|
fa_length = selected->appendix().length;
|
|
}
|
|
|
|
if (byte_view_text)
|
|
{
|
|
byte_view_text->markField(f_start, f_length);
|
|
byte_view_text->markProtocol(p_start, p_length);
|
|
byte_view_text->markAppendix(fa_start, fa_length);
|
|
}
|
|
}
|
|
void ByteViewTab::highlightedFieldChanged(FieldInformation *highlighted)
|
|
{
|
|
ByteViewText * byte_view_text = qobject_cast<ByteViewText *>(currentWidget());
|
|
if (!highlighted || !byte_view_text) {
|
|
return;
|
|
}
|
|
|
|
int f_start = -1, f_length = -1;
|
|
|
|
if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
|
|
// In the hex view, only highlight the target bytes or string. The entire
|
|
// field can then be displayed by clicking on any of the bytes in the field.
|
|
f_start = cap_file_->search_pos - cap_file_->search_len + 1;
|
|
f_length = (int) cap_file_->search_len;
|
|
} else {
|
|
f_start = highlighted->position().start;
|
|
f_length = highlighted->position().length;
|
|
}
|
|
|
|
byte_view_text->markField(f_start, f_length, false);
|
|
byte_view_text->markProtocol(-1, -1);
|
|
byte_view_text->markAppendix(-1, -1);
|
|
}
|
|
|
|
void ByteViewTab::setCaptureFile(capture_file *cf)
|
|
{
|
|
selectedFrameChanged(-1);
|
|
|
|
cap_file_ = cf;
|
|
}
|
|
|
|
/*
|
|
* Editor modelines
|
|
*
|
|
* Local Variables:
|
|
* c-basic-offset: 4
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* ex: set shiftwidth=4 tabstop=8 expandtab:
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
*/
|