/* byte_view_tab.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "byte_view_tab.h" #include #include #include #include #include "cfile.h" #include "epan/epan_dissect.h" #include "epan/tvbuff-int.h" #include #include #include #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 allBVTs = findChildren(); if ( allBVTs.count() > 0 ) { ByteViewText * bvt = allBVTs.at(0); tvbuff_t * stored = VariantPointer::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(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::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::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::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 allBVTs = findChildren(); unsigned int length = search_tvb->length; for (int i = 0; i < allBVTs.size() && ! found; ++i) { ByteViewText * bvt = allBVTs.at(i); tvbuff_t * stored = VariantPointer::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()); 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(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(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: */