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