3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "byte_view_tab.h"
12 #include <QApplication>
18 #include "epan/epan_dissect.h"
19 #include "epan/tvbuff-int.h"
21 #include <main_application.h>
23 #include <ui/qt/utils/variant_pointer.h>
24 #include <ui/qt/widgets/byte_view_text.h>
26 #define tvb_data_property "tvb_data_property"
29 // - We might want to add a callback to free_data_sources in so that we
30 // don't have to blindly call clear().
32 ByteViewTab::ByteViewTab(QWidget
*parent
, epan_dissect_t
*edt_fixed
) :
35 is_fixed_packet_(edt_fixed
!= NULL
),
39 setAccessibleName(tr("Packet bytes"));
40 setTabPosition(QTabWidget::South
);
41 setDocumentMode(true);
43 // Shrink down to a small but nonzero size in the main splitter.
44 int one_em
= fontMetrics().height();
45 setMinimumSize(one_em
, one_em
);
48 connect(mainApp
, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow()));
52 // Connects the byte view with the main window, acting on changes to the packet
53 // list selection. It MUST NOT be used with the packet dialog as that is
54 // independent of the selection in the packet list.
55 void ByteViewTab::connectToMainWindow()
57 connect(this, SIGNAL(fieldSelected(FieldInformation
*)),
58 mainApp
->mainWindow(), SIGNAL(fieldSelected(FieldInformation
*)));
59 connect(this, SIGNAL(fieldHighlight(FieldInformation
*)),
60 mainApp
->mainWindow(), SIGNAL(fieldHighlight(FieldInformation
*)));
62 /* Connect change of packet selection */
63 connect(mainApp
->mainWindow(), SIGNAL(framesSelected(QList
<int>)), this, SLOT(selectedFrameChanged(QList
<int>)));
64 connect(mainApp
->mainWindow(), SIGNAL(setCaptureFile(capture_file
*)), this, SLOT(setCaptureFile(capture_file
*)));
65 connect(mainApp
->mainWindow(), SIGNAL(fieldSelected(FieldInformation
*)), this, SLOT(selectedFieldChanged(FieldInformation
*)));
67 connect(mainApp
->mainWindow(), SIGNAL(captureActive(int)), this, SLOT(captureActive(int)));
70 void ByteViewTab::captureActive(int cap
)
74 QList
<ByteViewText
*> allBVTs
= findChildren
<ByteViewText
*>();
75 if (allBVTs
.count() > 0)
77 ByteViewText
* bvt
= allBVTs
.at(0);
78 tvbuff_t
* stored
= VariantPointer
<tvbuff_t
>::asPtr(bvt
->property(tvb_data_property
));
81 selectedFrameChanged(QList
<int>());
86 void ByteViewTab::addTab(const char *name
, tvbuff_t
*tvb
) {
87 if (count() == 1) { // Remove empty placeholder.
88 ByteViewText
*cur_text
= qobject_cast
<ByteViewText
*>(currentWidget());
89 if (cur_text
&& cur_text
->isEmpty()) delete currentWidget();
92 packet_char_enc encoding
= PACKET_CHAR_ENC_CHAR_ASCII
;
93 if (cap_file_
&& cap_file_
->current_frame
)
94 encoding
= (packet_char_enc
)cap_file_
->current_frame
->encoding
;
98 int data_len
= (int) tvb_captured_length(tvb
);
100 // Note: this does not copy the data and will be invalidated
101 // when the tvbuff's real data becomes invalid (which is not
102 // necessarily when the tvb itself becomes invalid.)
103 data
= QByteArray::fromRawData((const char *) tvb_get_ptr(tvb
, 0, data_len
), data_len
);
107 ByteViewText
* byte_view_text
= new ByteViewText(data
, encoding
, this);
108 byte_view_text
->setAccessibleName(name
);
109 byte_view_text
->setMonospaceFont(mainApp
->monospaceFont(true));
113 // There are some secondary data source tvbuffs whose datais not freed
114 // when the epan_dissect_t is freed, but at some other point expected
115 // to outlive the packet, generally when the capture file is closed.
116 // If this is a PacketDialog, it can break that assumption.
117 // To get around this, we deep copy their data when the file is closed.
119 // XXX: We could add a function to the tvbuff API and only do this if
120 // there is no free_cb (a free_cb implies the data is freed at the
121 // same time as the tvb, i.e. when leaving the packet.)
122 if (is_fixed_packet_
&& count() > 0) {
123 connect(this, &ByteViewTab::detachData
, byte_view_text
, &ByteViewText::detachData
);
125 // See above - this tvb is (expected to be) scoped to the packet, but
126 // the real data is not necessarily so. If this is a PacketDialog
127 // and such a secondary data source, then we MUST NOT use any tvb
128 // function that accesses the real data after the capture file closes.
129 // That includes via the ds_tvb item of a field_info in the tree.
130 // proto_find_field_from_offset() is OK. See #14363.
132 // XXX: It sounds appealing to clone the secondary data source tvbs
133 // and set them to be freed when the byte_view_text is freed, perhaps
134 // even doing so only when the capture file is closing. However, while
135 // relatively simple for the few number of secondary data sources, it
136 // would be a pain to change the pointers for every field_info.
137 byte_view_text
->setProperty(tvb_data_property
, VariantPointer
<tvbuff_t
>::asQVariant(tvb
));
139 connect(mainApp
, SIGNAL(zoomMonospaceFont(QFont
)), byte_view_text
, SLOT(setMonospaceFont(QFont
)));
141 connect(byte_view_text
, SIGNAL(byteHovered(int)), this, SLOT(byteViewTextHovered(int)));
142 connect(byte_view_text
, SIGNAL(byteSelected(int)), this, SLOT(byteViewTextMarked(int)));
143 connect(byte_view_text
, SIGNAL(byteViewSettingsChanged()), this, SIGNAL(byteViewSettingsChanged()));
144 connect(this, SIGNAL(byteViewSettingsChanged()), byte_view_text
, SLOT(updateByteViewSettings()));
145 connect(this, SIGNAL(byteViewUnmarkField()), byte_view_text
, SLOT(unmarkField()));
148 int idx
= QTabWidget::addTab(byte_view_text
, name
);
149 byte_view_text
->setProperty("tab_index", QVariant::fromValue(idx
));
151 QTabWidget::setTabToolTip(idx
, name
);
154 void ByteViewTab::byteViewTextHovered(int idx
)
156 if (idx
>= 0 && edt_
)
158 tvbuff_t
* tvb
= VariantPointer
<tvbuff_t
>::asPtr(sender()->property(tvb_data_property
));
159 proto_tree
* tree
= edt_
->tree
;
163 field_info
* fi
= proto_find_field_from_offset(tree
, idx
, tvb
);
166 FieldInformation
finfo(fi
, this);
167 highlightedFieldChanged(&finfo
);
168 emit
fieldHighlight(&finfo
);
174 emit
fieldHighlight((FieldInformation
*)0);
177 void ByteViewTab::byteViewTextMarked(int idx
)
179 if (idx
>= 0 && edt_
)
181 tvbuff_t
* tvb
= VariantPointer
<tvbuff_t
>::asPtr(sender()->property(tvb_data_property
));
182 proto_tree
* tree
= edt_
->tree
;
186 field_info
* fi
= proto_find_field_from_offset(tree
, idx
, tvb
);
189 FieldInformation
finfo(fi
, this);
190 emit
fieldSelected(&finfo
);
196 emit
fieldSelected((FieldInformation
*)0);
199 ByteViewText
* ByteViewTab::findByteViewTextForTvb(tvbuff_t
* search_tvb
, int * idx
)
202 ByteViewText
* item
= 0;
208 QList
<ByteViewText
*> allBVTs
= findChildren
<ByteViewText
*>();
209 for (int i
= 0; i
< allBVTs
.size() && ! found
; ++i
)
211 ByteViewText
* bvt
= allBVTs
.at(i
);
212 tvbuff_t
* stored
= VariantPointer
<tvbuff_t
>::asPtr(bvt
->property(tvb_data_property
));
213 if (stored
== search_tvb
)
216 int wdgIdx
= bvt
->property("tab_index").toInt();
221 item
= (ByteViewText
*)widget(wdgIdx
);
228 void ByteViewTab::tabInserted(int tab_index
) {
230 QTabWidget::tabInserted(tab_index
);
233 void ByteViewTab::tabRemoved(int tab_index
) {
235 QTabWidget::tabRemoved(tab_index
);
238 void ByteViewTab::setTabsVisible() {
245 void ByteViewTab::selectedFrameChanged(QList
<int> frames
)
248 qDeleteAll(findChildren
<ByteViewText
*>());
250 if (!is_fixed_packet_
) {
251 /* If this is not a fixed packet (not the packet dialog), it must be the
252 * byte view associated with the packet list. */
253 if (cap_file_
&& cap_file_
->edt
) {
254 /* Assumes that this function is called as a result of selecting a
255 * packet in the packet list (PacketList::selectionChanged). That
256 * invokes "cf_select_packet" which will update "cap_file_->edt". */
257 edt_
= cap_file_
->edt
;
259 /* capture file is closing or packet is deselected. */
264 /* only show the bytes for single selections */
265 if (frames
.count() == 1)
267 if (! cap_file_
|| ! cap_file_
->edt
)
270 /* This code relies on a dissection, which had happened somewhere else. It also does not
271 * really check, if the dissection happened for the correct frame. In the future we might
272 * rewrite this for directly calling the dissection engine here. */
274 for (src_le
= edt_
->pi
.data_src
; src_le
!= NULL
; src_le
= src_le
->next
) {
275 struct data_source
*source
;
277 source
= (struct data_source
*)src_le
->data
;
278 source_name
= get_data_source_name(source
);
279 addTab(source_name
, get_data_source_tvb(source
));
280 wmem_free(NULL
, source_name
);
284 addTab("PlaceHolder", 0);
289 void ByteViewTab::selectedFieldChanged(FieldInformation
*selected
)
291 // We need to handle both selection and deselection.
292 ByteViewText
* byte_view_text
= qobject_cast
<ByteViewText
*>(currentWidget());
293 int f_start
= -1, f_length
= -1;
294 int p_start
= -1, p_length
= -1;
295 int fa_start
= -1, fa_length
= -1;
298 if (selected
->parent() == this) {
299 // We only want inbound signals.
302 const field_info
*fi
= selected
->fieldInfo();
306 byte_view_text
= findByteViewTextForTvb(fi
->ds_tvb
, &idx
);
308 if (cap_file_
->search_in_progress
&& (cap_file_
->hex
|| (cap_file_
->string
&& cap_file_
->packet_data
))) {
309 // In the hex view, only highlight the target bytes or string. The entire
310 // field can then be displayed by clicking on any of the bytes in the field.
311 f_start
= (int)cap_file_
->search_pos
;
312 f_length
= (int) cap_file_
->search_len
;
314 f_start
= selected
->position().start
;
315 f_length
= selected
->position().length
;
318 setCurrentIndex(idx
);
320 FieldInformation
*parentField
= selected
->parentField();
322 p_start
= parentField
->position().start
;
323 p_length
= parentField
->position().length
;
324 fa_start
= selected
->appendix().start
;
325 fa_length
= selected
->appendix().length
;
332 byte_view_text
->markField(f_start
, f_length
);
333 byte_view_text
->markProtocol(p_start
, p_length
);
334 byte_view_text
->markAppendix(fa_start
, fa_length
);
336 emit
byteViewUnmarkField();
339 void ByteViewTab::highlightedFieldChanged(FieldInformation
*highlighted
)
341 ByteViewText
* byte_view_text
= qobject_cast
<ByteViewText
*>(currentWidget());
342 if (!highlighted
|| !byte_view_text
) {
346 int f_start
= -1, f_length
= -1;
348 if (cap_file_
->search_in_progress
&& (cap_file_
->hex
|| (cap_file_
->string
&& cap_file_
->packet_data
))) {
349 // In the hex view, only highlight the target bytes or string. The entire
350 // field can then be displayed by clicking on any of the bytes in the field.
351 f_start
= cap_file_
->search_pos
- cap_file_
->search_len
+ 1;
352 f_length
= (int) cap_file_
->search_len
;
354 f_start
= highlighted
->position().start
;
355 f_length
= highlighted
->position().length
;
358 byte_view_text
->markField(f_start
, f_length
, false);
359 byte_view_text
->markProtocol(-1, -1);
360 byte_view_text
->markAppendix(-1, -1);
363 void ByteViewTab::setCaptureFile(capture_file
*cf
)
365 selectedFrameChanged(QList
<int>());
370 void ByteViewTab::captureFileClosing()