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 "search_frame.h"
11 #include <ui_search_frame.h>
14 #include "ui/recent.h"
16 #include <epan/proto.h>
17 #include <epan/strutil.h>
19 #include <wsutil/filesystem.h>
20 #include <wsutil/utf8_entities.h>
21 #include <wsutil/regex.h>
23 #include "main_application.h"
24 #include "utils/qt_ui_utils.h"
43 narrow_and_wide_chars_
,
48 SearchFrame::SearchFrame(QWidget
*parent
) :
49 AccordionFrame(parent
),
50 sf_ui_(new Ui::SearchFrame
),
54 sf_ui_
->setupUi(this);
57 foreach (QWidget
*w
, findChildren
<QWidget
*>()) {
58 w
->setAttribute(Qt::WA_MacSmallSize
, true);
62 if (!is_packet_configuration_namespace()) {
63 sf_ui_
->searchInComboBox
->setItemText(0, tr("Event List"));
64 sf_ui_
->searchInComboBox
->setItemText(1, tr("Event Details"));
65 sf_ui_
->searchInComboBox
->setItemText(2, tr("Event Bytes"));
66 sf_ui_
->searchInComboBox
->setToolTip(tr("<html><head/><body>"
67 "<p>Search the Info column of the event list (summary pane), "
68 "decoded event display labels (tree view pane) or the "
69 "ASCII-converted event data (hex view pane).</p>"
74 applyRecentSearchSettings();
79 SearchFrame::~SearchFrame()
82 ws_regex_free(regex_
);
87 void SearchFrame::animatedShow()
89 AccordionFrame::animatedShow();
91 sf_ui_
->searchLineEdit
->setFocus();
94 void SearchFrame::findNext()
96 if (!cap_file_
) return;
98 sf_ui_
->dirCheckBox
->setChecked(false);
103 on_findButton_clicked();
106 void SearchFrame::findPrevious()
108 if (!cap_file_
) return;
110 sf_ui_
->dirCheckBox
->setChecked(true);
115 on_findButton_clicked();
118 void SearchFrame::setFocus()
120 sf_ui_
->searchLineEdit
->setFocus();
121 sf_ui_
->searchLineEdit
->selectAll();
124 void SearchFrame::setCaptureFile(capture_file
*cf
)
127 if (!cf
&& isVisible()) {
133 void SearchFrame::findFrameWithFilter(QString
&filter
)
136 sf_ui_
->searchLineEdit
->setText(filter
);
137 sf_ui_
->searchLineEdit
->setCursorPosition(0);
138 sf_ui_
->searchTypeComboBox
->setCurrentIndex(df_search_
);
140 on_findButton_clicked();
143 void SearchFrame::keyPressEvent(QKeyEvent
*event
)
145 if (event
->modifiers() == Qt::NoModifier
) {
146 if (event
->key() == Qt::Key_Escape
) {
147 on_cancelButton_clicked();
148 } else if (event
->key() == Qt::Key_Enter
|| event
->key() == Qt::Key_Return
) {
149 on_findButton_clicked();
153 AccordionFrame::keyPressEvent(event
);
156 bool SearchFrame::regexCompile()
159 if (!sf_ui_
->caseCheckBox
->isChecked()) {
160 flags
|= WS_REGEX_CASELESS
;
162 if (sf_ui_
->dirCheckBox
->isChecked()) {
163 flags
|= WS_REGEX_ANCHORED
;
167 ws_regex_free(regex_
);
170 if (sf_ui_
->searchLineEdit
->text().isEmpty()) {
175 char *errmsg
= nullptr;
176 regex_
= ws_regex_compile_ex(sf_ui_
->searchLineEdit
->text().toUtf8().constData(), -1,
179 if (errmsg
!= nullptr) {
180 regex_error_
= errmsg
;
183 return regex_
? true : false;
186 void SearchFrame::applyRecentSearchSettings()
188 int search_in_idx
= in_packet_list_
;
189 int char_encoding_idx
= narrow_and_wide_chars_
;
190 int search_type_idx
= df_search_
;
192 switch (recent
.gui_search_in
) {
193 case SEARCH_IN_PACKET_LIST
:
194 search_in_idx
= in_packet_list_
;
196 case SEARCH_IN_PACKET_DETAILS
:
197 search_in_idx
= in_proto_tree_
;
199 case SEARCH_IN_PACKET_BYTES
:
200 search_in_idx
= in_bytes_
;
206 switch (recent
.gui_search_char_set
) {
207 case SEARCH_CHAR_SET_NARROW_AND_WIDE
:
208 char_encoding_idx
= narrow_and_wide_chars_
;
210 case SEARCH_CHAR_SET_NARROW
:
211 char_encoding_idx
= narrow_chars_
;
213 case SEARCH_CHAR_SET_WIDE
:
214 char_encoding_idx
= wide_chars_
;
220 switch (recent
.gui_search_type
) {
221 case SEARCH_TYPE_DISPLAY_FILTER
:
222 search_type_idx
= df_search_
;
224 case SEARCH_TYPE_HEX_VALUE
:
225 search_type_idx
= hex_search_
;
227 case SEARCH_TYPE_STRING
:
228 search_type_idx
= string_search_
;
230 case SEARCH_TYPE_REGEX
:
231 search_type_idx
= regex_search_
;
237 sf_ui_
->searchInComboBox
->setCurrentIndex(search_in_idx
);
238 sf_ui_
->charEncodingComboBox
->setCurrentIndex(char_encoding_idx
);
239 sf_ui_
->caseCheckBox
->setChecked(recent
.gui_search_case_sensitive
);
240 sf_ui_
->searchTypeComboBox
->setCurrentIndex(search_type_idx
);
241 sf_ui_
->dirCheckBox
->setChecked(recent
.gui_search_reverse_dir
);
242 sf_ui_
->multipleCheckBox
->setChecked(recent
.gui_search_multiple_occurs
);
245 void SearchFrame::updateWidgets()
254 int search_type
= sf_ui_
->searchTypeComboBox
->currentIndex();
255 sf_ui_
->searchInComboBox
->setEnabled(search_type
== string_search_
|| search_type
== regex_search_
);
256 sf_ui_
->caseCheckBox
->setEnabled(search_type
== string_search_
|| search_type
== regex_search_
);
257 // The encoding only is used when searching the raw Packet Bytes
258 // (otherwise all strings have already been converted to UTF-8)
259 sf_ui_
->charEncodingComboBox
->setEnabled(search_type
== string_search_
&& sf_ui_
->searchInComboBox
->currentIndex() == in_bytes_
);
261 // We can search for multiple matches in the same frame if we're doing
262 // a Proto Tree search or a Frame Bytes search, but not a string/regex
263 // search in the Packet List, or a display filter search (since those
264 // don't highlight what fields / offsets caused the match.)
265 sf_ui_
->multipleCheckBox
->setEnabled((sf_ui_
->searchInComboBox
->isEnabled() && sf_ui_
->searchInComboBox
->currentIndex() != in_packet_list_
) || search_type
== hex_search_
);
267 switch (search_type
) {
269 sf_ui_
->searchLineEdit
->checkDisplayFilter(sf_ui_
->searchLineEdit
->text());
272 if (sf_ui_
->searchLineEdit
->text().isEmpty()) {
273 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Invalid
);
277 bytes
= convert_string_to_hex(sf_ui_
->searchLineEdit
->text().toUtf8().constData(), &nbytes
);
278 if (bytes
== nullptr)
279 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Invalid
);
282 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Valid
);
287 if (sf_ui_
->searchLineEdit
->text().isEmpty()) {
288 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Invalid
);
290 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Valid
);
294 if (regexCompile()) {
295 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Valid
);
297 sf_ui_
->searchLineEdit
->setSyntaxState(SyntaxLineEdit::Invalid
);
301 // currentIndex is probably -1. Nothing is selected or list is empty.
305 if (sf_ui_
->searchLineEdit
->text().isEmpty() || sf_ui_
->searchLineEdit
->syntaxState() == SyntaxLineEdit::Invalid
) {
306 sf_ui_
->findButton
->setEnabled(false);
308 sf_ui_
->findButton
->setEnabled(true);
312 void SearchFrame::on_searchInComboBox_currentIndexChanged(int idx
)
315 case in_packet_list_
:
316 recent
.gui_search_in
= SEARCH_IN_PACKET_LIST
;
319 recent
.gui_search_in
= SEARCH_IN_PACKET_DETAILS
;
322 recent
.gui_search_in
= SEARCH_IN_PACKET_BYTES
;
328 // We only search for multiple occurrences in packet list and bytes
332 void SearchFrame::on_charEncodingComboBox_currentIndexChanged(int idx
)
335 case narrow_and_wide_chars_
:
336 recent
.gui_search_char_set
= SEARCH_CHAR_SET_NARROW_AND_WIDE
;
339 recent
.gui_search_char_set
= SEARCH_CHAR_SET_NARROW
;
342 recent
.gui_search_char_set
= SEARCH_CHAR_SET_WIDE
;
349 void SearchFrame::on_caseCheckBox_toggled(bool checked
)
351 recent
.gui_search_case_sensitive
= checked
;
355 void SearchFrame::on_searchTypeComboBox_currentIndexChanged(int idx
)
359 recent
.gui_search_type
= SEARCH_TYPE_DISPLAY_FILTER
;
362 recent
.gui_search_type
= SEARCH_TYPE_HEX_VALUE
;
365 recent
.gui_search_type
= SEARCH_TYPE_STRING
;
368 recent
.gui_search_type
= SEARCH_TYPE_REGEX
;
374 // Enable completion only for display filter search.
375 sf_ui_
->searchLineEdit
->allowCompletion(idx
== df_search_
);
377 if (idx
== df_search_
) {
378 sf_ui_
->searchLineEdit
->setPlaceholderText(DisplayFilterEdit::tr("Enter a display filter %1").arg(UTF8_HORIZONTAL_ELLIPSIS
));
379 sf_ui_
->searchLineEdit
->checkFilter();
381 sf_ui_
->searchLineEdit
->setPlaceholderText(QString());
382 sf_ui_
->searchLineEdit
->setToolTip(QString());
383 mainApp
->popStatus(MainApplication::FilterSyntax
);
389 void SearchFrame::on_searchLineEdit_textChanged(const QString
&)
394 void SearchFrame::on_dirCheckBox_toggled(bool checked
)
396 recent
.gui_search_reverse_dir
= checked
;
399 void SearchFrame::on_multipleCheckBox_toggled(bool checked
)
401 recent
.gui_search_multiple_occurs
= checked
;
404 void SearchFrame::on_findButton_clicked()
406 uint8_t *bytes
= nullptr;
408 char *string
= nullptr;
409 dfilter_t
*dfp
= nullptr;
410 bool found_packet
= false;
417 cap_file_
->hex
= false;
418 cap_file_
->string
= false;
419 cap_file_
->case_type
= false;
420 cap_file_
->regex
= nullptr;
421 cap_file_
->packet_data
= false;
422 cap_file_
->decode_data
= false;
423 cap_file_
->summary_data
= false;
424 cap_file_
->scs_type
= SCS_NARROW_AND_WIDE
;
425 cap_file_
->dir
= sf_ui_
->dirCheckBox
->isChecked() ? SD_BACKWARD
: SD_FORWARD
;
426 bool multiple_occurrences
= sf_ui_
->multipleCheckBox
->isChecked();
428 int search_type
= sf_ui_
->searchTypeComboBox
->currentIndex();
429 switch (search_type
) {
431 if (!dfilter_compile(sf_ui_
->searchLineEdit
->text().toUtf8().constData(), &dfp
, nullptr)) {
432 err_string
= tr("Invalid filter.");
436 if (dfp
== nullptr) {
437 err_string
= tr("That filter doesn't test anything.");
442 bytes
= convert_string_to_hex(sf_ui_
->searchLineEdit
->text().toUtf8().constData(), &nbytes
);
443 if (bytes
== nullptr) {
444 err_string
= tr("That's not a valid hex string.");
447 cap_file_
->hex
= true;
451 if (sf_ui_
->searchLineEdit
->text().isEmpty()) {
452 err_string
= tr("You didn't specify any text for which to search.");
455 cap_file_
->string
= true;
456 cap_file_
->case_type
= sf_ui_
->caseCheckBox
->isChecked() ? false : true;
457 cap_file_
->regex
= (search_type
== regex_search_
? regex_
: nullptr);
458 switch (sf_ui_
->charEncodingComboBox
->currentIndex()) {
459 case narrow_and_wide_chars_
:
460 cap_file_
->scs_type
= SCS_NARROW_AND_WIDE
;
463 cap_file_
->scs_type
= SCS_NARROW
;
466 cap_file_
->scs_type
= SCS_WIDE
;
469 err_string
= tr("No valid character set selected. Please report this to the development team.");
472 string
= convert_string_case(sf_ui_
->searchLineEdit
->text().toUtf8().constData(), cap_file_
->case_type
);
475 err_string
= tr("No valid search type selected. Please report this to the development team.");
479 switch (sf_ui_
->searchInComboBox
->currentIndex()) {
480 case in_packet_list_
:
481 cap_file_
->summary_data
= true;
484 cap_file_
->decode_data
= true;
487 cap_file_
->packet_data
= true;
490 err_string
= tr("No valid search area selected. Please report this to the development team.");
494 g_free(cap_file_
->sfilter
);
495 cap_file_
->sfilter
= qstring_strdup(sf_ui_
->searchLineEdit
->text());
496 mainApp
->popStatus(MainApplication::FileStatus
);
497 mainApp
->pushStatus(MainApplication::FileStatus
, tr("Searching for %1…").arg(sf_ui_
->searchLineEdit
->text()));
499 if (cap_file_
->hex
) {
500 /* Hex value in packet data */
501 found_packet
= cf_find_packet_data(cap_file_
, bytes
, nbytes
, cap_file_
->dir
, multiple_occurrences
);
504 /* We didn't find a packet */
505 err_string
= tr("No packet contained those bytes.");
508 } else if (cap_file_
->string
) {
509 if (search_type
== regex_search_
&& !cap_file_
->regex
) {
510 err_string
= regex_error_
;
513 if (cap_file_
->summary_data
) {
514 /* String in the Info column of the summary line */
515 found_packet
= cf_find_packet_summary_line(cap_file_
, string
, cap_file_
->dir
);
518 err_string
= tr("No packet contained that string in its Info column.");
521 } else if (cap_file_
->decode_data
) {
522 /* String in the protocol tree headings */
523 found_packet
= cf_find_packet_protocol_tree(cap_file_
, string
, cap_file_
->dir
, multiple_occurrences
);
526 err_string
= tr("No packet contained that string in its dissected display.");
529 } else if (cap_file_
->packet_data
&& string
) {
530 /* String in the ASCII-converted packet data */
531 found_packet
= cf_find_packet_data(cap_file_
, (uint8_t *) string
, strlen(string
), cap_file_
->dir
, multiple_occurrences
);
534 err_string
= tr("No packet contained that string in its converted data.");
539 /* Search via display filter */
540 found_packet
= cf_find_packet_dfilter(cap_file_
, dfp
, cap_file_
->dir
);
543 err_string
= tr("No packet matched that filter.");
550 mainApp
->popStatus(MainApplication::FileStatus
);
551 if (!err_string
.isEmpty()) {
552 mainApp
->pushStatus(MainApplication::FilterSyntax
, err_string
);
556 void SearchFrame::on_cancelButton_clicked()
558 mainApp
->popStatus(MainApplication::FilterSyntax
);
562 void SearchFrame::changeEvent(QEvent
* event
)
566 switch (event
->type())
568 case QEvent::LanguageChange
:
569 sf_ui_
->retranslateUi(this);
575 AccordionFrame::changeEvent(event
);