1 /* byte_view_tab.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "byte_view_tab.h"
11
12 #include <QApplication>
13 #include <QClipboard>
14 #include <QMimeData>
15 #include <QTabBar>
16
17 #include "cfile.h"
18 #include "epan/epan_dissect.h"
19 #include "epan/tvbuff-int.h"
20
21 #include <wireshark_application.h>
22
23 #include <ui/qt/utils/variant_pointer.h>
24 #include <ui/qt/widgets/byte_view_text.h>
25
26 #define tvb_data_property "tvb_data_property"
27
28 // To do:
29 // - We might want to add a callback to free_data_sources in so that we
30 // don't have to blindly call clear().
31
ByteViewTab(QWidget * parent,epan_dissect_t * edt_fixed)32 ByteViewTab::ByteViewTab(QWidget *parent, epan_dissect_t *edt_fixed) :
33 QTabWidget(parent),
34 cap_file_(0),
35 is_fixed_packet_(edt_fixed != NULL),
36 edt_(edt_fixed),
37 disable_hover_(false)
38 {
39 setAccessibleName(tr("Packet bytes"));
40 setTabPosition(QTabWidget::South);
41 setDocumentMode(true);
42
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);
46
47 if (!edt_fixed) {
48 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow()));
49 }
50 }
51
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.
connectToMainWindow()55 void ByteViewTab::connectToMainWindow()
56 {
57 connect(this, SIGNAL(fieldSelected(FieldInformation *)),
58 wsApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)));
59 connect(this, SIGNAL(fieldHighlight(FieldInformation *)),
60 wsApp->mainWindow(), SIGNAL(fieldHighlight(FieldInformation *)));
61
62 /* Connect change of packet selection */
63 connect(wsApp->mainWindow(), SIGNAL(framesSelected(QList<int>)), this, SLOT(selectedFrameChanged(QList<int>)));
64 connect(wsApp->mainWindow(), SIGNAL(setCaptureFile(capture_file*)), this, SLOT(setCaptureFile(capture_file*)));
65 connect(wsApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)), this, SLOT(selectedFieldChanged(FieldInformation *)));
66
67 connect(wsApp->mainWindow(), SIGNAL(captureActive(int)), this, SLOT(captureActive(int)));
68 }
69
captureActive(int cap)70 void ByteViewTab::captureActive(int cap)
71 {
72 if (cap == 0)
73 {
74 QList<ByteViewText *> allBVTs = findChildren<ByteViewText *>();
75 if (allBVTs.count() > 0)
76 {
77 ByteViewText * bvt = allBVTs.at(0);
78 tvbuff_t * stored = VariantPointer<tvbuff_t>::asPtr(bvt->property(tvb_data_property));
79
80 if (! stored)
81 selectedFrameChanged(QList<int>());
82 }
83 }
84 }
85
addTab(const char * name,tvbuff_t * tvb)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();
90 }
91
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;
95
96 QByteArray data;
97 if (tvb) {
98 int data_len = (int) tvb_captured_length(tvb);
99 if (data_len > 0) {
100 // Note: this does not copy the data and will be invalidated when
101 // the tvb becomes invalid (e.g. when the current packet changes).
102 data = QByteArray::fromRawData((const char *) tvb_get_ptr(tvb, 0, data_len), data_len);
103 }
104 }
105
106 ByteViewText * byte_view_text = new ByteViewText(data, encoding, this);
107 byte_view_text->setAccessibleName(name);
108 byte_view_text->setMonospaceFont(wsApp->monospaceFont(true));
109
110 if (tvb)
111 {
112 byte_view_text->setProperty(tvb_data_property, VariantPointer<tvbuff_t>::asQVariant(tvb));
113
114 connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont)));
115
116 connect(byte_view_text, SIGNAL(byteHovered(int)), this, SLOT(byteViewTextHovered(int)));
117 connect(byte_view_text, SIGNAL(byteSelected(int)), this, SLOT(byteViewTextMarked(int)));
118 connect(byte_view_text, SIGNAL(byteViewSettingsChanged()), this, SIGNAL(byteViewSettingsChanged()));
119 connect(this, SIGNAL(byteViewSettingsChanged()), byte_view_text, SLOT(updateByteViewSettings()));
120 }
121
122 int idx = QTabWidget::addTab(byte_view_text, name);
123 byte_view_text->setProperty("tab_index", QVariant::fromValue(idx));
124
125 QTabWidget::setTabToolTip(idx, name);
126 }
127
byteViewTextHovered(int idx)128 void ByteViewTab::byteViewTextHovered(int idx)
129 {
130 if (idx >= 0 && edt_)
131 {
132 tvbuff_t * tvb = VariantPointer<tvbuff_t>::asPtr(sender()->property(tvb_data_property));
133 proto_tree * tree = edt_->tree;
134
135 if (tvb && tree)
136 {
137 field_info * fi = proto_find_field_from_offset(tree, idx, tvb);
138 if (fi)
139 {
140 FieldInformation finfo(fi, this);
141 highlightedFieldChanged(&finfo);
142 emit fieldHighlight(&finfo);
143 return;
144 }
145 }
146 }
147
148 emit fieldHighlight((FieldInformation *)0);
149 }
150
byteViewTextMarked(int idx)151 void ByteViewTab::byteViewTextMarked(int idx)
152 {
153 if (idx >= 0 && edt_)
154 {
155 tvbuff_t * tvb = VariantPointer<tvbuff_t>::asPtr(sender()->property(tvb_data_property));
156 proto_tree * tree = edt_->tree;
157
158 if (tvb && tree)
159 {
160 field_info * fi = proto_find_field_from_offset(tree, idx, tvb);
161 if (fi)
162 {
163 FieldInformation finfo(fi, this);
164 emit fieldSelected(&finfo);
165 return;
166 }
167 }
168 }
169
170 emit fieldSelected((FieldInformation *)0);
171 }
172
findByteViewTextForTvb(tvbuff_t * search_tvb,int * idx)173 ByteViewText * ByteViewTab::findByteViewTextForTvb(tvbuff_t * search_tvb, int * idx)
174 {
175
176 ByteViewText * item = 0;
177 if (! search_tvb)
178 return item;
179
180 bool found = false;
181
182 QList<ByteViewText *> allBVTs = findChildren<ByteViewText *>();
183 unsigned int length = search_tvb->length;
184 for (int i = 0; i < allBVTs.size() && ! found; ++i)
185 {
186 ByteViewText * bvt = allBVTs.at(i);
187 tvbuff_t * stored = VariantPointer<tvbuff_t>::asPtr(bvt->property(tvb_data_property));
188 if (stored == search_tvb)
189 {
190 found = true;
191 }
192 else if (stored)
193 {
194 if (stored->length >= length && tvb_memeql(search_tvb, 0, tvb_get_ptr(stored, 0, length), length) == 0)
195 {
196 /* In packetDialog we do not match, because we came from different data sources.
197 * Assuming the capture files match, this should be a sufficient enough difference */
198 found = true;
199 }
200 }
201
202 if (found)
203 {
204 int wdgIdx = bvt->property("tab_index").toInt();
205 if (idx)
206 {
207 *idx = wdgIdx;
208 }
209 item = (ByteViewText *)widget(wdgIdx);
210 }
211 }
212
213 return item;
214 }
215
tabInserted(int tab_index)216 void ByteViewTab::tabInserted(int tab_index) {
217 setTabsVisible();
218 QTabWidget::tabInserted(tab_index);
219 }
220
tabRemoved(int tab_index)221 void ByteViewTab::tabRemoved(int tab_index) {
222 setTabsVisible();
223 QTabWidget::tabRemoved(tab_index);
224 }
225
setTabsVisible()226 void ByteViewTab::setTabsVisible() {
227 if (count() > 1)
228 tabBar()->show();
229 else
230 tabBar()->hide();
231 }
232
selectedFrameChanged(QList<int> frames)233 void ByteViewTab::selectedFrameChanged(QList<int> frames)
234 {
235 clear();
236 qDeleteAll(findChildren<ByteViewText *>());
237
238 if (!is_fixed_packet_) {
239 /* If this is not a fixed packet (not the packet dialog), it must be the
240 * byte view associated with the packet list. */
241 if (cap_file_ && cap_file_->edt) {
242 /* Assumes that this function is called as a result of selecting a
243 * packet in the packet list (PacketList::selectionChanged). That
244 * invokes "cf_select_packet" which will update "cap_file_->edt". */
245 edt_ = cap_file_->edt;
246 } else {
247 /* capture file is closing or packet is deselected. */
248 edt_ = NULL;
249 }
250 }
251
252 /* only show the bytes for single selections */
253 if (frames.count() == 1)
254 {
255 if (! cap_file_ || ! cap_file_->edt)
256 return;
257
258 /* This code relies on a dissection, which had happened somewhere else. It also does not
259 * really check, if the dissection happened for the correct frame. In the future we might
260 * rewrite this for directly calling the dissection engine here. */
261 GSList *src_le;
262 for (src_le = edt_->pi.data_src; src_le != NULL; src_le = src_le->next) {
263 struct data_source *source;
264 char* source_name;
265 source = (struct data_source *)src_le->data;
266 source_name = get_data_source_name(source);
267 addTab(source_name, get_data_source_tvb(source));
268 wmem_free(NULL, source_name);
269 }
270 }
271 else
272 addTab("PlaceHolder", 0);
273
274 setCurrentIndex(0);
275 }
276
selectedFieldChanged(FieldInformation * selected)277 void ByteViewTab::selectedFieldChanged(FieldInformation *selected)
278 {
279 // We need to handle both selection and deselection.
280 ByteViewText * byte_view_text = qobject_cast<ByteViewText *>(currentWidget());
281 int f_start = -1, f_length = -1;
282 int p_start = -1, p_length = -1;
283 int fa_start = -1, fa_length = -1;
284
285 if (selected) {
286 if (selected->parent() == this) {
287 // We only want inbound signals.
288 return;
289 }
290 const field_info *fi = selected->fieldInfo();
291
292 int idx = 0;
293 if (fi)
294 byte_view_text = findByteViewTextForTvb(fi->ds_tvb, &idx);
295
296 if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
297 // In the hex view, only highlight the target bytes or string. The entire
298 // field can then be displayed by clicking on any of the bytes in the field.
299 f_start = cap_file_->search_pos - cap_file_->search_len + 1;
300 f_length = (int) cap_file_->search_len;
301 } else {
302 f_start = selected->position().start;
303 f_length = selected->position().length;
304 }
305
306 setCurrentIndex(idx);
307
308 FieldInformation *parentField = selected->parentField();
309
310 p_start = parentField->position().start;
311 p_length = parentField->position().length;
312 fa_start = selected->appendix().start;
313 fa_length = selected->appendix().length;
314
315 delete parentField;
316 }
317
318 if (byte_view_text)
319 {
320 byte_view_text->markField(f_start, f_length);
321 byte_view_text->markProtocol(p_start, p_length);
322 byte_view_text->markAppendix(fa_start, fa_length);
323 }
324 }
highlightedFieldChanged(FieldInformation * highlighted)325 void ByteViewTab::highlightedFieldChanged(FieldInformation *highlighted)
326 {
327 ByteViewText * byte_view_text = qobject_cast<ByteViewText *>(currentWidget());
328 if (!highlighted || !byte_view_text) {
329 return;
330 }
331
332 int f_start = -1, f_length = -1;
333
334 if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
335 // In the hex view, only highlight the target bytes or string. The entire
336 // field can then be displayed by clicking on any of the bytes in the field.
337 f_start = cap_file_->search_pos - cap_file_->search_len + 1;
338 f_length = (int) cap_file_->search_len;
339 } else {
340 f_start = highlighted->position().start;
341 f_length = highlighted->position().length;
342 }
343
344 byte_view_text->markField(f_start, f_length, false);
345 byte_view_text->markProtocol(-1, -1);
346 byte_view_text->markAppendix(-1, -1);
347 }
348
setCaptureFile(capture_file * cf)349 void ByteViewTab::setCaptureFile(capture_file *cf)
350 {
351 selectedFrameChanged(QList<int>());
352
353 cap_file_ = cf;
354 }
355