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 "main_application.h"
11 #include "wireshark_main_window.h"
14 * The generated Ui_WiresharkMainWindow::setupUi() can grow larger than our configured limit,
15 * so turn off -Wframe-larger-than= for ui_wireshark_main_window.h.
17 DIAG_OFF(frame
-larger
-than
=)
18 #include <ui_wireshark_main_window.h>
19 DIAG_ON(frame
-larger
-than
=)
21 #include <epan/addr_resolv.h>
22 #include "epan/conversation_filter.h"
23 #include <epan/epan_dissect.h>
24 #include <wsutil/filesystem.h>
25 #include <wsutil/wslog.h>
26 #include <wsutil/ws_assert.h>
27 #include <wsutil/version_info.h>
28 #include <epan/prefs.h>
29 #include <epan/stats_tree_priv.h>
30 #include <epan/plugin_if.h>
31 #include <epan/export_object.h>
32 #include <frame_tvbuff.h>
34 #include "ui/iface_toolbar.h"
35 #include "ui/commandline.h"
38 #include "ui/capture.h"
39 #include <capture/capture_session.h>
42 #include "ui/alert_box.h"
44 #include "ui/capture_ui_utils.h"
46 #include "ui/capture_globals.h"
47 #include "ui/main_statusbar.h"
48 #include "ui/recent.h"
49 #include "ui/recent_utils.h"
51 #include "ui/preference_utils.h"
53 #include "byte_view_tab.h"
55 #include "capture_options_dialog.h"
57 #include "conversation_colorize_action.h"
58 #include "export_dissection_dialog.h"
59 #include "export_object_action.h"
60 #include "file_set_dialog.h"
61 #include "filter_dialog.h"
62 #include "follow_stream_action.h"
63 #include "funnel_statistics.h"
64 #include "import_text_dialog.h"
65 #include "interface_toolbar.h"
66 #include "packet_diagram.h"
67 #include "packet_list.h"
68 #include "proto_tree.h"
69 #include "simple_dialog.h"
70 #include "tap_parameter_dialog.h"
71 #include "wireless_frame.h"
72 #include <ui/qt/widgets/wireless_timeline.h>
74 #include <ui/qt/widgets/additional_toolbar.h>
75 #include <ui/qt/widgets/display_filter_edit.h>
76 #include <ui/qt/widgets/filter_expression_toolbar.h>
78 #include <ui/qt/utils/color_utils.h>
79 #include <ui/qt/utils/profile_switcher.h>
80 #include <ui/qt/utils/qt_ui_utils.h>
81 #include <ui/qt/utils/stock_icon.h>
82 #include <ui/qt/utils/variant_pointer.h>
85 #include <QActionGroup>
86 #include <QIntValidator>
89 #include <QMessageBox>
90 #include <QMetaObject>
94 #include <QToolButton>
95 #include <QTreeWidget>
98 //menu_recent_file_write_all
100 // If we ever add support for multiple windows this will need to be replaced.
101 static WiresharkMainWindow
*gbl_cur_main_window_
;
103 static void plugin_if_mainwindow_apply_filter(GHashTable
* data_set
)
105 if (!gbl_cur_main_window_
|| !data_set
)
108 if (g_hash_table_lookup_extended(data_set
, "filter_string", NULL
, NULL
)) {
109 QString
filter((const char *)g_hash_table_lookup(data_set
, "filter_string"));
110 gbl_cur_main_window_
->filterPackets(filter
);
114 static void plugin_if_mainwindow_preference(GHashTable
* data_set
)
116 if (!gbl_cur_main_window_
|| !data_set
)
119 const char * module_name
;
120 const char * pref_name
;
121 const char * pref_value
;
123 DIAG_OFF_CAST_AWAY_CONST
124 if (g_hash_table_lookup_extended(data_set
, "pref_module", NULL
, (void * *)&module_name
) &&
125 g_hash_table_lookup_extended(data_set
, "pref_key", NULL
, (void * *)&pref_name
) &&
126 g_hash_table_lookup_extended(data_set
, "pref_value", NULL
, (void * *)&pref_value
))
128 unsigned int changed_flags
= prefs_store_ext(module_name
, pref_name
, pref_value
);
130 mainApp
->emitAppSignal(WiresharkApplication::PacketDissectionChanged
);
131 mainApp
->emitAppSignal(WiresharkApplication::PreferencesChanged
);
134 DIAG_ON_CAST_AWAY_CONST
137 static void plugin_if_mainwindow_gotoframe(GHashTable
* data_set
)
139 if (!gbl_cur_main_window_
|| !data_set
)
144 if (g_hash_table_lookup_extended(data_set
, "frame_nr", NULL
, &framenr
)) {
145 if (GPOINTER_TO_UINT(framenr
) != 0)
146 gbl_cur_main_window_
->gotoFrame(GPOINTER_TO_UINT(framenr
));
152 static void plugin_if_mainwindow_get_ws_info(GHashTable
* data_set
)
154 if (!gbl_cur_main_window_
|| !data_set
)
157 ws_info_t
*ws_info
= NULL
;
159 if (!g_hash_table_lookup_extended(data_set
, "ws_info", NULL
, (void**)&ws_info
))
162 CaptureFile
*cfWrap
= gbl_cur_main_window_
->captureFile();
163 capture_file
*cf
= cfWrap
->capFile();
165 ws_info
->ws_info_supported
= true;
167 /* If we have a filename attached to ws_info clear it */
168 if (ws_info
->cf_filename
!= NULL
)
170 g_free(ws_info
->cf_filename
);
171 ws_info
->cf_filename
= NULL
;
174 /* Determine the true state of the capture file. We return the true state in
175 the ws_info structure and DON'T CHANGE the cf->state as we don't want to cause problems
176 with code that follows this. */
181 /* As we have a cf->filename we'll use the name and the state */
182 ws_info
->cf_filename
= g_strdup(cf
->filename
);
183 ws_info
->cf_state
= cf
->state
;
187 /* When we come through here the cf->state can show FILE_READ_DONE even though the
188 file is actually closed (no filename). A better fix would be to have a
189 FILE_CLOSE_PENDING state but that involves a lot of code change elsewhere. */
190 ws_info
->cf_state
= FILE_CLOSED
;
194 if (!ws_info
->cf_filename
)
196 /* We may have a filename associated with the main window so let's use it */
197 QString fileNameString
= gbl_cur_main_window_
->getMwFileName();
198 if (fileNameString
.length())
200 QByteArray ba
= fileNameString
.toLatin1();
201 const char *c_file_name
= ba
.data();
202 ws_info
->cf_filename
= g_strdup(c_file_name
);
207 ws_info
->cf_count
= cf
->count
;
209 QList
<int> rows
= gbl_cur_main_window_
->selectedRows();
210 frame_data
* fdata
= NULL
;
211 if (rows
.count() > 0)
212 fdata
= gbl_cur_main_window_
->frameDataForRow(rows
.at(0));
214 if (cf
->state
== FILE_READ_DONE
&& fdata
) {
215 ws_info
->cf_framenr
= fdata
->num
;
216 ws_info
->frame_passed_dfilter
= (fdata
->passed_dfilter
== 1);
219 ws_info
->cf_framenr
= 0;
220 ws_info
->frame_passed_dfilter
= false;
225 /* Initialise the other ws_info structure values */
226 ws_info
->cf_count
= 0;
227 ws_info
->cf_framenr
= 0;
228 ws_info
->frame_passed_dfilter
= false;
232 #endif /* HAVE_LIBPCAP */
234 static void plugin_if_mainwindow_get_frame_data(GHashTable
* data_set
)
236 if (!gbl_cur_main_window_
|| !data_set
)
239 plugin_if_frame_data_cb extract_cb
;
241 void** ret_value_ptr
;
243 if (g_hash_table_lookup_extended(data_set
, "extract_cb", NULL
, (void**)&extract_cb
) &&
244 g_hash_table_lookup_extended(data_set
, "user_data", NULL
, (void**)&user_data
) &&
245 g_hash_table_lookup_extended(data_set
, "ret_value_ptr", NULL
, (void**)&ret_value_ptr
))
247 QList
<int> rows
= gbl_cur_main_window_
->selectedRows();
248 if (rows
.count() > 0) {
249 frame_data
* fdata
= gbl_cur_main_window_
->frameDataForRow(rows
.at(0));
251 *ret_value_ptr
= extract_cb(fdata
, user_data
);
257 static void plugin_if_mainwindow_get_capture_file(GHashTable
* data_set
)
259 if (!gbl_cur_main_window_
|| !data_set
)
262 plugin_if_capture_file_cb extract_cb
;
264 void** ret_value_ptr
;
266 if (g_hash_table_lookup_extended(data_set
, "extract_cb", NULL
, (void**)&extract_cb
) &&
267 g_hash_table_lookup_extended(data_set
, "user_data", NULL
, (void**)&user_data
) &&
268 g_hash_table_lookup_extended(data_set
, "ret_value_ptr", NULL
, (void**)&ret_value_ptr
))
270 CaptureFile
* cfWrap
= gbl_cur_main_window_
->captureFile();
271 capture_file
* cf
= cfWrap
->capFile();
273 *ret_value_ptr
= extract_cb(cf
, user_data
);
278 static void plugin_if_mainwindow_update_toolbars(GHashTable
* data_set
)
280 if (!gbl_cur_main_window_
|| !data_set
)
283 if (g_hash_table_lookup_extended(data_set
, "toolbar_name", NULL
, NULL
)) {
284 QString
toolbarName((const char *)g_hash_table_lookup(data_set
, "toolbar_name"));
285 gbl_cur_main_window_
->removeAdditionalToolbar(toolbarName
);
290 static void mainwindow_add_toolbar(const iface_toolbar
*toolbar_entry
)
292 if (gbl_cur_main_window_
&& toolbar_entry
)
294 gbl_cur_main_window_
->addInterfaceToolbar(toolbar_entry
);
298 static void mainwindow_remove_toolbar(const char *menu_title
)
300 if (gbl_cur_main_window_
&& menu_title
)
302 gbl_cur_main_window_
->removeInterfaceToolbar(menu_title
);
306 QMenu
* WiresharkMainWindow::findOrAddMenu(QMenu
*parent_menu
, const QStringList
& menu_parts
) {
307 for (auto const & menu_text
: menu_parts
) {
309 for (auto const & action
: parent_menu
->actions()) {
310 if (action
->text() == menu_text
.trimmed()) {
311 parent_menu
= action
->menu();
317 // If we get here the menu entry was not found, add a sub menu
318 parent_menu
= parent_menu
->addMenu(menu_text
.trimmed());
324 WiresharkMainWindow::WiresharkMainWindow(QWidget
*parent
) :
326 main_ui_(new Ui::WiresharkMainWindow
),
327 previous_focus_(NULL
),
328 file_set_dialog_(NULL
),
329 show_hide_actions_(NULL
),
330 time_display_actions_(NULL
),
331 time_precision_actions_(NULL
),
332 funnel_statistics_(NULL
),
334 was_maximized_(false),
335 capture_stopping_(false),
336 capture_filter_valid_(false),
337 use_capturing_title_(false)
339 , capture_options_dialog_(NULL
)
342 #if defined(Q_OS_MAC)
346 if (!gbl_cur_main_window_
) {
347 connect(mainApp
, &MainApplication::openStatCommandDialog
, this, &WiresharkMainWindow::openStatCommandDialog
);
348 connect(mainApp
, &MainApplication::openTapParameterDialog
,
349 this, [=](const QString cfg_str
, const QString arg
, void *userdata
) {openTapParameterDialog(cfg_str
, arg
, userdata
);});
351 gbl_cur_main_window_
= this;
353 capture_input_init(&cap_session_
, CaptureFile::globalCapFile());
357 // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
358 // iterates over *all* of our children, looking for matching "on_" slots.
359 // The fewer children we have at this point the better.
360 main_ui_
->setupUi(this);
361 #ifdef HAVE_SOFTWARE_UPDATE
362 update_action_
= new QAction(tr("Check for Updates…"), main_ui_
->menuHelp
);
364 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
365 wireless_frame_
= new WirelessFrame(this);
366 main_ui_
->wirelessToolBar
->addWidget(wireless_frame_
);
368 removeToolBar(main_ui_
->wirelessToolBar
);
369 main_ui_
->menuView
->removeAction(main_ui_
->actionViewWirelessToolbar
);
372 menu_groups_
= QList
<register_stat_group_t
>()
373 << REGISTER_PACKET_ANALYZE_GROUP_UNSORTED
374 << REGISTER_PACKET_STAT_GROUP_UNSORTED
375 << REGISTER_STAT_GROUP_GENERIC
376 << REGISTER_STAT_GROUP_RESPONSE_TIME
377 << REGISTER_STAT_GROUP_RSERPOOL
378 << REGISTER_TELEPHONY_GROUP_UNSORTED
379 << REGISTER_TELEPHONY_GROUP_ANSI
380 << REGISTER_TELEPHONY_GROUP_GSM
381 << REGISTER_TELEPHONY_GROUP_3GPP_UU
382 << REGISTER_TELEPHONY_GROUP_MTP3
383 << REGISTER_TELEPHONY_GROUP_SCTP
384 << REGISTER_TOOLS_GROUP_UNSORTED
;
386 setWindowIcon(mainApp
->normalIcon());
388 setMenusForCaptureFile();
389 setForCapturedPackets(false);
390 setMenusForFileSet(false);
391 interfaceSelectionChanged();
392 loadWindowGeometry();
395 main_ui_
->actionAnalyzeReloadLuaPlugins
->setVisible(false);
398 qRegisterMetaType
<FilterAction::Action
>("FilterAction::Action");
399 qRegisterMetaType
<FilterAction::ActionType
>("FilterAction::ActionType");
400 connect(this, &WiresharkMainWindow::filterAction
, this, &WiresharkMainWindow::queuedFilterAction
, Qt::QueuedConnection
);
402 //To prevent users use features before initialization complete
403 //Otherwise unexpected problems may occur
404 setFeaturesEnabled(false);
405 connect(mainApp
, &MainApplication::appInitialized
, this, [this]() { setFeaturesEnabled(); });
406 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::applyGlobalCommandLineOptions
);
407 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::zoomText
);
408 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::initViewColorizeMenu
);
409 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::addStatsPluginsToMenu
);
410 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::addDynamicMenus
);
411 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::addPluginIFStructures
);
412 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::initConversationMenus
);
413 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::initExportObjectsMenus
);
414 connect(mainApp
, &MainApplication::appInitialized
, this, &WiresharkMainWindow::initFollowStreamMenus
);
415 connect(mainApp
, &MainApplication::appInitialized
, this,
416 [=]() { addDisplayFilterTranslationActions(main_ui_
->menuEditCopy
); });
418 connect(mainApp
, &MainApplication::profileChanging
, this, &WiresharkMainWindow::saveWindowGeometry
);
419 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::layoutPanes
);
420 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::layoutToolbars
);
421 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::updatePreferenceActions
);
422 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::zoomText
);
423 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::updateTitlebar
);
425 connect(mainApp
, &MainApplication::updateRecentCaptureStatus
, this, &WiresharkMainWindow::updateRecentCaptures
);
426 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WiresharkMainWindow::updateRecentCaptures
);
427 updateRecentCaptures();
429 #if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
430 connect(mainApp
, &MainApplication::softwareUpdateRequested
, this, &WiresharkMainWindow::softwareUpdateRequested
,
431 Qt::BlockingQueuedConnection
);
434 df_combo_box_
= new DisplayFilterCombo(this);
436 funnel_statistics_
= new FunnelStatistics(this, capture_file_
);
437 connect(df_combo_box_
, &QComboBox::editTextChanged
, funnel_statistics_
, &FunnelStatistics::displayFilterTextChanged
);
438 connect(funnel_statistics_
, &FunnelStatistics::setDisplayFilter
, this, &WiresharkMainWindow::setDisplayFilter
);
439 connect(funnel_statistics_
, &FunnelStatistics::openCaptureFile
, this,
440 [=](QString cf_path
, QString filter
) { openCaptureFile(cf_path
, filter
); });
442 connect(df_combo_box_
, &QComboBox::editTextChanged
, this, &WiresharkMainWindow::updateDisplayFilterTranslationActions
);
444 file_set_dialog_
= new FileSetDialog(this);
445 connect(file_set_dialog_
, &FileSetDialog::fileSetOpenCaptureFile
, this, [=](QString cf_path
) { openCaptureFile(cf_path
); });
447 initMainToolbarIcons();
449 main_ui_
->displayFilterToolBar
->insertWidget(main_ui_
->actionNewDisplayFilterExpression
, df_combo_box_
);
451 // Make sure filter expressions overflow into a menu instead of a
452 // larger toolbar. We do this by adding them to a child toolbar.
453 // https://bugreports.qt.io/browse/QTBUG-2472
454 FilterExpressionToolBar
*filter_expression_toolbar_
= new FilterExpressionToolBar(this);
455 connect(filter_expression_toolbar_
, &FilterExpressionToolBar::filterPreferences
, this, &WiresharkMainWindow::onFilterPreferences
);
456 connect(filter_expression_toolbar_
, &FilterExpressionToolBar::filterSelected
, this, &WiresharkMainWindow::onFilterSelected
);
457 connect(filter_expression_toolbar_
, &FilterExpressionToolBar::filterEdit
, this, &WiresharkMainWindow::onFilterEdit
);
459 main_ui_
->displayFilterToolBar
->addWidget(filter_expression_toolbar_
);
461 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
462 connect(wireless_frame_
, &WirelessFrame::showWirelessPreferences
, this, &WiresharkMainWindow::showPreferencesDialog
);
465 main_ui_
->goToFrame
->hide();
466 connect(main_ui_
->goToFrame
, &AccordionFrame::visibilityChanged
, main_ui_
->actionGoGoToPacket
, &QAction::setChecked
);
468 // XXX For some reason the cursor is drawn funny with an input mask set
469 // https://bugreports.qt-project.org/browse/QTBUG-7174
471 main_ui_
->searchFrame
->hide();
472 connect(main_ui_
->searchFrame
, &SearchFrame::visibilityChanged
, main_ui_
->actionEditFindPacket
, &QAction::setChecked
);
474 main_ui_
->addressEditorFrame
->hide();
475 main_ui_
->columnEditorFrame
->hide();
476 main_ui_
->preferenceEditorFrame
->hide();
477 main_ui_
->filterExpressionFrame
->hide();
480 main_ui_
->menuCapture
->setEnabled(false);
481 main_ui_
->actionCaptureStart
->setEnabled(false);
482 main_ui_
->actionCaptureStop
->setEnabled(false);
483 main_ui_
->actionCaptureRestart
->setEnabled(false);
484 main_ui_
->actionCaptureOptions
->setEnabled(false);
485 main_ui_
->actionCaptureRefreshInterfaces
->setEnabled(false);
488 // Set OS specific shortcuts for fullscreen mode
489 #if defined(Q_OS_MAC)
490 main_ui_
->actionViewFullScreen
->setShortcut(QKeySequence::FullScreen
);
492 main_ui_
->actionViewFullScreen
->setShortcut(QKeySequence(Qt::Key_F11
));
495 #if defined(Q_OS_MAC)
497 main_ui_
->goToPacketLabel
->setAttribute(Qt::WA_MacSmallSize
, true);
498 main_ui_
->goToLineEdit
->setAttribute(Qt::WA_MacSmallSize
, true);
499 main_ui_
->goToGo
->setAttribute(Qt::WA_MacSmallSize
, true);
500 main_ui_
->goToCancel
->setAttribute(Qt::WA_MacSmallSize
, true);
502 main_ui_
->actionEditPreferences
->setMenuRole(QAction::PreferencesRole
);
506 connect(main_ui_
->goToGo
, &QPushButton::pressed
, this, &WiresharkMainWindow::goToGoClicked
);
507 connect(main_ui_
->goToCancel
, &QPushButton::pressed
, this, &WiresharkMainWindow::goToCancelClicked
);
509 // A billion-1 is equivalent to the inputMask 900000000 previously used
510 // Avoid QValidator::Intermediate values by using a top value of all 9's
511 #define MAX_GOTO_LINE 999999999
513 QIntValidator
*goToLineQiv
= new QIntValidator(0,MAX_GOTO_LINE
,this);
514 main_ui_
->goToLineEdit
->setValidator(goToLineQiv
);
516 #ifdef HAVE_SOFTWARE_UPDATE
517 QAction
*update_sep
= main_ui_
->menuHelp
->insertSeparator(main_ui_
->actionHelpAbout
);
518 main_ui_
->menuHelp
->insertAction(update_sep
, update_action_
);
519 connect(update_action_
, &QAction::triggered
, this, &WiresharkMainWindow::checkForUpdates
);
521 master_split_
.setObjectName("splitterMaster");
522 extra_split_
.setObjectName("splitterExtra");
523 master_split_
.setChildrenCollapsible(false);
524 extra_split_
.setChildrenCollapsible(false);
525 main_ui_
->mainStack
->addWidget(&master_split_
);
527 empty_pane_
.setObjectName("emptyPane");
528 empty_pane_
.setVisible(false);
530 packet_list_
= new PacketList(&master_split_
);
531 main_ui_
->wirelessTimelineWidget
->setPacketList(packet_list_
);
532 connect(packet_list_
, &PacketList::framesSelected
, this, &WiresharkMainWindow::setMenusForSelectedPacket
);
533 connect(packet_list_
, &PacketList::framesSelected
, this, &WiresharkMainWindow::framesSelected
);
535 QAction
*action
= main_ui_
->menuPacketComment
->addAction(tr("Add New Comment…"));
536 connect(action
, &QAction::triggered
, this, &WiresharkMainWindow::addPacketComment
);
537 action
->setShortcut(QKeySequence(Qt::CTRL
| Qt::ALT
| Qt::Key_C
));
538 connect(main_ui_
->menuPacketComment
, &QMenu::aboutToShow
, this, &WiresharkMainWindow::setEditCommentsMenu
);
540 proto_tree_
= new ProtoTree(&master_split_
);
541 proto_tree_
->installEventFilter(this);
543 packet_list_
->setProtoTree(proto_tree_
);
544 packet_list_
->setProfileSwitcher(profile_switcher_
);
545 packet_list_
->installEventFilter(this);
547 packet_diagram_
= new PacketDiagram(&master_split_
);
549 main_stack_
= main_ui_
->mainStack
;
550 welcome_page_
= main_ui_
->welcomePage
;
551 main_status_bar_
= main_ui_
->statusBar
;
553 connect(proto_tree_
, &ProtoTree::fieldSelected
,
554 this, &WiresharkMainWindow::fieldSelected
);
555 connect(packet_list_
, &PacketList::fieldSelected
,
556 this, &WiresharkMainWindow::fieldSelected
);
557 connect(this, &WiresharkMainWindow::fieldSelected
,
558 this, &WiresharkMainWindow::setMenusForSelectedTreeRow
);
559 connect(this, &WiresharkMainWindow::fieldSelected
,
560 main_ui_
->statusBar
, &MainStatusBar::selectedFieldChanged
);
562 connect(this, &WiresharkMainWindow::fieldHighlight
,
563 main_ui_
->statusBar
, &MainStatusBar::highlightedFieldChanged
);
564 connect(mainApp
, &WiresharkApplication::captureActive
,
565 this, &WiresharkMainWindow::captureActive
);
567 byte_view_tab_
= new ByteViewTab(&master_split_
);
569 // Packet list and proto tree must exist before these are called.
570 setMenusForSelectedPacket();
571 setMenusForSelectedTreeRow();
573 initShowHideMainWidgets();
574 initTimeDisplayFormatMenu();
575 initTimePrecisionFormatMenu();
577 updatePreferenceActions();
578 updateRecentActions();
579 setForCaptureInProgress(false);
581 setTabOrder(df_combo_box_
->lineEdit(), packet_list_
);
582 setTabOrder(packet_list_
, proto_tree_
);
584 connect(&capture_file_
, &CaptureFile::captureEvent
, this, &WiresharkMainWindow::captureEventHandler
);
585 connect(&capture_file_
, &CaptureFile::captureEvent
, mainApp
, &WiresharkApplication::captureEventHandler
);
586 connect(&capture_file_
, &CaptureFile::captureEvent
, main_ui_
->statusBar
, &MainStatusBar::captureEventHandler
);
587 connect(&capture_file_
, &CaptureFile::captureEvent
, profile_switcher_
, &ProfileSwitcher::captureEventHandler
);
589 connect(mainApp
, &MainApplication::freezePacketList
, packet_list_
, &PacketList::freezePacketList
);
590 connect(mainApp
, &MainApplication::columnsChanged
, packet_list_
, &PacketList::columnsChanged
);
591 connect(mainApp
, &MainApplication::colorsChanged
, packet_list_
, &PacketList::colorsChanged
);
592 connect(mainApp
, &MainApplication::preferencesChanged
, packet_list_
, &PacketList::preferencesChanged
);
593 connect(mainApp
, &MainApplication::recentPreferencesRead
, this, &WiresharkMainWindow::applyRecentPaneGeometry
);
594 connect(mainApp
, &MainApplication::recentPreferencesRead
, this, &WiresharkMainWindow::updateRecentActions
);
595 connect(mainApp
, &MainApplication::packetDissectionChanged
, this, &WiresharkMainWindow::redissectPackets
, Qt::QueuedConnection
);
597 connect(mainApp
, &MainApplication::checkDisplayFilter
, this, &WiresharkMainWindow::checkDisplayFilter
);
598 connect(mainApp
, &MainApplication::fieldsChanged
, this, &WiresharkMainWindow::fieldsChanged
);
599 connect(mainApp
, &MainApplication::reloadLuaPlugins
, this, &WiresharkMainWindow::reloadLuaPlugins
);
601 connect(main_ui_
->mainStack
, &QStackedWidget::currentChanged
, this, &WiresharkMainWindow::mainStackChanged
);
603 connect(welcome_page_
, &WelcomePage::startCapture
, this, [this](QStringList interfaces
) { startCapture(interfaces
); });
604 connect(welcome_page_
, &WelcomePage::recentFileActivated
, this, [this](QString cfile
) { openCaptureFile(cfile
); });
606 connect(main_ui_
->addressEditorFrame
, &AddressEditorFrame::redissectPackets
,
607 this, &WiresharkMainWindow::redissectPackets
);
608 connect(main_ui_
->addressEditorFrame
, &AddressEditorFrame::showNameResolutionPreferences
,
609 this, &WiresharkMainWindow::showPreferencesDialog
);
610 connect(main_ui_
->preferenceEditorFrame
, &PreferenceEditorFrame::showProtocolPreferences
,
611 this, &WiresharkMainWindow::showPreferencesDialog
);
612 connect(main_ui_
->filterExpressionFrame
, &FilterExpressionFrame::showPreferencesDialog
,
613 this, &WiresharkMainWindow::showPreferencesDialog
);
614 connect(main_ui_
->filterExpressionFrame
, &FilterExpressionFrame::filterExpressionsChanged
,
615 filter_expression_toolbar_
, &FilterExpressionToolBar::filterExpressionsChanged
);
617 /* Connect change of capture file */
618 connect(this, &WiresharkMainWindow::setCaptureFile
,
619 main_ui_
->searchFrame
, &SearchFrame::setCaptureFile
);
620 connect(this, &WiresharkMainWindow::setCaptureFile
,
621 main_ui_
->statusBar
, &MainStatusBar::setCaptureFile
);
622 connect(this, &WiresharkMainWindow::setCaptureFile
,
623 packet_list_
, &PacketList::setCaptureFile
);
624 connect(this, &WiresharkMainWindow::setCaptureFile
,
625 proto_tree_
, &ProtoTree::setCaptureFile
);
627 connect(mainApp
, &MainApplication::zoomMonospaceFont
, packet_list_
, &PacketList::setMonospaceFont
);
628 connect(mainApp
, &MainApplication::zoomRegularFont
, packet_list_
, &PacketList::setRegularFont
);
629 connect(mainApp
, &MainApplication::zoomMonospaceFont
, proto_tree_
, &ProtoTree::setMonospaceFont
);
631 connectFileMenuActions();
632 connectEditMenuActions();
633 connectViewMenuActions();
634 connectGoMenuActions();
635 connectCaptureMenuActions();
636 connectAnalyzeMenuActions();
637 connectStatisticsMenuActions();
638 connectTelephonyMenuActions();
639 connectWirelessMenuActions();
640 connectToolsMenuActions();
641 connectHelpMenuActions();
643 connect(packet_list_
, &PacketList::packetDissectionChanged
, this, &WiresharkMainWindow::redissectPackets
);
644 connect(packet_list_
, &PacketList::showColumnPreferences
, this, &WiresharkMainWindow::showPreferencesDialog
);
645 connect(packet_list_
, &PacketList::showProtocolPreferences
, this, &WiresharkMainWindow::showPreferencesDialog
);
646 connect(packet_list_
, SIGNAL(editProtocolPreference(preference
*, pref_module
*)),
647 main_ui_
->preferenceEditorFrame
, SLOT(editPreference(preference
*, pref_module
*)));
648 connect(packet_list_
, &PacketList::editColumn
, this, &WiresharkMainWindow::showColumnEditor
);
649 connect(main_ui_
->columnEditorFrame
, &ColumnEditorFrame::columnEdited
, packet_list_
, &PacketList::columnsChanged
);
650 connect(packet_list_
, &QAbstractItemView::doubleClicked
, this, [=](const QModelIndex
&){ openPacketDialog(); });
651 connect(packet_list_
, &PacketList::packetListScrolled
, main_ui_
->actionGoAutoScroll
, &QAction::setChecked
);
653 connect(proto_tree_
, &ProtoTree::openPacketInNewWindow
, this, &WiresharkMainWindow::openPacketDialog
);
654 connect(proto_tree_
, &ProtoTree::showProtocolPreferences
, this, &WiresharkMainWindow::showPreferencesDialog
);
655 connect(proto_tree_
, SIGNAL(editProtocolPreference(preference
*, pref_module
*)),
656 main_ui_
->preferenceEditorFrame
, SLOT(editPreference(preference
*, pref_module
*)));
658 connect(main_ui_
->statusBar
, &MainStatusBar::showExpertInfo
, this, [=]() {
659 statCommandExpertInfo(NULL
, NULL
);
662 connect(main_ui_
->statusBar
, &MainStatusBar::stopLoading
,
663 &capture_file_
, &CaptureFile::stopLoading
);
665 connect(main_ui_
->statusBar
, &MainStatusBar::editCaptureComment
,
666 main_ui_
->actionStatisticsCaptureFileProperties
, &QAction::trigger
);
668 connect(main_ui_
->menuApplyAsFilter
, &QMenu::aboutToShow
,
669 this, &WiresharkMainWindow::filterMenuAboutToShow
);
670 connect(main_ui_
->menuPrepareAFilter
, &QMenu::aboutToShow
,
671 this, &WiresharkMainWindow::filterMenuAboutToShow
);
674 QTreeWidget
*iface_tree
= findChild
<QTreeWidget
*>("interfaceTree");
676 connect(iface_tree
, &QTreeWidget::itemSelectionChanged
, this, &WiresharkMainWindow::interfaceSelectionChanged
);
678 connect(main_ui_
->welcomePage
, &WelcomePage::captureFilterSyntaxChanged
,
679 this, &WiresharkMainWindow::captureFilterSyntaxChanged
);
681 connect(this, &WiresharkMainWindow::showExtcapOptions
, this, &WiresharkMainWindow::showExtcapOptionsDialog
);
682 connect(this->welcome_page_
, &WelcomePage::showExtcapOptions
, this, &WiresharkMainWindow::showExtcapOptionsDialog
);
684 #endif // HAVE_LIBPCAP
686 /* Create plugin_if hooks */
687 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY
, plugin_if_mainwindow_apply_filter
);
688 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE
, plugin_if_mainwindow_apply_filter
);
689 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE
, plugin_if_mainwindow_preference
);
690 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME
, plugin_if_mainwindow_gotoframe
);
692 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO
, plugin_if_mainwindow_get_ws_info
);
694 plugin_if_register_gui_cb(PLUGIN_IF_GET_FRAME_DATA
, plugin_if_mainwindow_get_frame_data
);
695 plugin_if_register_gui_cb(PLUGIN_IF_GET_CAPTURE_FILE
, plugin_if_mainwindow_get_capture_file
);
696 plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR
, plugin_if_mainwindow_update_toolbars
);
698 /* Register Interface Toolbar callbacks */
699 iface_toolbar_register_cb(mainwindow_add_toolbar
, mainwindow_remove_toolbar
);
701 /* Show tooltips on menu items that go to websites */
702 main_ui_
->actionHelpMPWireshark
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_WIRESHARK
)));
703 main_ui_
->actionHelpMPWireshark_Filter
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_WIRESHARK_FILTER
)));
704 main_ui_
->actionHelpMPWireshark_FilterReference
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_DFILTER_REF
)));
705 main_ui_
->actionHelpMPCapinfos
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_CAPINFOS
)));
706 main_ui_
->actionHelpMPDumpcap
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_DUMPCAP
)));
707 main_ui_
->actionHelpMPEditcap
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_EDITCAP
)));
708 main_ui_
->actionHelpMPMergecap
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_MERGECAP
)));
709 main_ui_
->actionHelpMPRawshark
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_RAWSHARK
)));
710 main_ui_
->actionHelpMPReordercap
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_REORDERCAP
)));
711 main_ui_
->actionHelpMPText2pcap
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_TEXT2PCAP
)));
712 main_ui_
->actionHelpMPTShark
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_TSHARK
)));
714 main_ui_
->actionHelpContents
->setToolTip(gchar_free_to_qstring(topic_action_url(HELP_CONTENT
)));
715 main_ui_
->actionHelpWebsite
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_HOME
)));
716 main_ui_
->actionHelpFAQ
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_FAQ
)));
717 main_ui_
->actionHelpAsk
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_ASK
)));
718 main_ui_
->actionHelpDownloads
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_DOWNLOAD
)));
719 main_ui_
->actionHelpWiki
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_WIKI
)));
720 main_ui_
->actionHelpSampleCaptures
->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_SAMPLE_CAPTURES
)));
721 main_ui_
->actionHelpReleaseNotes
->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_RELEASE_NOTES
)));
726 WiresharkMainWindow::~WiresharkMainWindow()
728 disconnect(main_ui_
->mainStack
, 0, 0, 0);
729 if (previous_focus_
!= nullptr) {
730 disconnect(previous_focus_
, &QWidget::destroyed
, this, &WiresharkMainWindow::resetPreviousFocus
);
734 // Below dialogs inherit GeometryStateDialog
735 // For reasons described in geometry_state_dialog.h no parent is set when
736 // instantiating the dialogs and as a result objects are not automatically
737 // freed by its parent. Free then here explicitly to avoid leak and numerous
738 // Valgrind complaints.
739 delete file_set_dialog_
;
741 delete capture_options_dialog_
;
748 QMenu
*WiresharkMainWindow::createPopupMenu()
750 QMenu
*menu
= new QMenu();
751 menu
->addAction(main_ui_
->actionViewMainToolbar
);
752 menu
->addAction(main_ui_
->actionViewFilterToolbar
);
753 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
754 menu
->addAction(main_ui_
->actionViewWirelessToolbar
);
757 if (!main_ui_
->menuInterfaceToolbars
->actions().isEmpty()) {
758 QMenu
*submenu
= menu
->addMenu(main_ui_
->menuInterfaceToolbars
->title());
759 foreach(QAction
*action
, main_ui_
->menuInterfaceToolbars
->actions()) {
760 submenu
->addAction(action
);
764 if (!main_ui_
->menuAdditionalToolbars
->actions().isEmpty()) {
765 QMenu
*subMenu
= menu
->addMenu(main_ui_
->menuAdditionalToolbars
->title());
766 foreach(QAction
*action
, main_ui_
->menuAdditionalToolbars
->actions()) {
767 subMenu
->addAction(action
);
771 menu
->addAction(main_ui_
->actionViewStatusBar
);
773 menu
->addSeparator();
774 menu
->addAction(main_ui_
->actionViewPacketList
);
775 menu
->addAction(main_ui_
->actionViewPacketDetails
);
776 menu
->addAction(main_ui_
->actionViewPacketBytes
);
777 menu
->addAction(main_ui_
->actionViewPacketDiagram
);
781 void WiresharkMainWindow::addInterfaceToolbar(const iface_toolbar
*toolbar_entry
)
783 QMenu
*menu
= main_ui_
->menuInterfaceToolbars
;
784 bool visible
= g_list_find_custom(recent
.interface_toolbars
, toolbar_entry
->menu_title
, (GCompareFunc
)strcmp
) ? true : false;
786 QString title
= QString().fromUtf8(toolbar_entry
->menu_title
);
787 QAction
*action
= new QAction(title
, menu
);
788 action
->setEnabled(true);
789 action
->setCheckable(true);
790 action
->setChecked(visible
);
791 action
->setToolTip(tr("Show or hide the toolbar"));
793 QAction
*before
= NULL
;
794 foreach(QAction
*action
, menu
->actions()) {
795 // Ensure we add the menu entries in sorted order
796 if (action
->text().compare(title
, Qt::CaseInsensitive
) > 0) {
801 menu
->insertAction(before
, action
);
803 InterfaceToolbar
*interface_toolbar
= new InterfaceToolbar(this, toolbar_entry
);
804 connect(mainApp
, &MainApplication::appInitialized
, interface_toolbar
, &InterfaceToolbar::interfaceListChanged
);
805 connect(mainApp
, &MainApplication::localInterfaceListChanged
, interface_toolbar
, &InterfaceToolbar::interfaceListChanged
);
807 QToolBar
*toolbar
= new QToolBar(this);
808 toolbar
->addWidget(interface_toolbar
);
809 toolbar
->setMovable(false);
810 toolbar
->setVisible(visible
);
812 action
->setData(QVariant::fromValue(toolbar
));
814 addToolBar(Qt::TopToolBarArea
, toolbar
);
815 insertToolBarBreak(toolbar
);
817 if (show_hide_actions_
) {
818 show_hide_actions_
->addAction(action
);
821 menu
->menuAction()->setVisible(true);
824 void WiresharkMainWindow::removeInterfaceToolbar(const char *menu_title
)
826 QMenu
*menu
= main_ui_
->menuInterfaceToolbars
;
827 QAction
*action
= NULL
;
828 QMap
<QAction
*, QWidget
*>::iterator i
;
830 QString title
= QString().fromUtf8(menu_title
);
831 foreach(action
, menu
->actions()) {
832 if (title
.compare(action
->text()) == 0) {
838 if (show_hide_actions_
) {
839 show_hide_actions_
->removeAction(action
);
841 menu
->removeAction(action
);
843 QToolBar
*toolbar
= action
->data().value
<QToolBar
*>();
844 removeToolBar(toolbar
);
850 menu
->menuAction()->setVisible(!menu
->actions().isEmpty());
853 void WiresharkMainWindow::updateStyleSheet()
856 // TODO: The event type QEvent::ApplicationPaletteChange is not sent to all child widgets.
857 // Workaround this by doing it manually for all AccordionFrame.
858 main_ui_
->addressEditorFrame
->updateStyleSheet();
859 main_ui_
->columnEditorFrame
->updateStyleSheet();
860 main_ui_
->filterExpressionFrame
->updateStyleSheet();
861 main_ui_
->goToFrame
->updateStyleSheet();
862 main_ui_
->preferenceEditorFrame
->updateStyleSheet();
863 main_ui_
->searchFrame
->updateStyleSheet();
865 df_combo_box_
->updateStyleSheet();
866 welcome_page_
->updateStyleSheets();
870 bool WiresharkMainWindow::eventFilter(QObject
*obj
, QEvent
*event
) {
872 // The user typed some text. Start filling in a filter.
873 // We may need to be more choosy here. We just need to catch events for the packet list,
874 // proto tree, and main welcome widgets.
875 if (event
->type() == QEvent::KeyPress
) {
876 QKeyEvent
*kevt
= static_cast<QKeyEvent
*>(event
);
877 if (kevt
->text().length() > 0 && kevt
->text()[0].isPrint() &&
878 !(kevt
->modifiers() & Qt::ControlModifier
)) {
879 df_combo_box_
->lineEdit()->insert(kevt
->text());
880 df_combo_box_
->lineEdit()->setFocus();
885 return QMainWindow::eventFilter(obj
, event
);
888 bool WiresharkMainWindow::event(QEvent
*event
)
890 switch (event
->type()) {
891 case QEvent::ApplicationPaletteChange
:
892 initMainToolbarIcons();
899 return QMainWindow::event(event
);
902 void WiresharkMainWindow::keyPressEvent(QKeyEvent
*event
) {
904 // Explicitly focus on the display filter combo.
905 if (event
->modifiers() & Qt::ControlModifier
&& event
->key() == Qt::Key_Slash
) {
906 df_combo_box_
->setFocus(Qt::ShortcutFocusReason
);
910 if (mainApp
->focusWidget() == main_ui_
->goToLineEdit
) {
911 if (event
->modifiers() == Qt::NoModifier
|| event
->modifiers() == Qt::KeypadModifier
) {
912 if (event
->key() == Qt::Key_Escape
) {
914 } else if (event
->key() == Qt::Key_Enter
|| event
->key() == Qt::Key_Return
) {
918 return; // goToLineEdit didn't want it and we don't either.
921 // Move up & down the packet list.
922 if (event
->key() == Qt::Key_F7
) {
923 packet_list_
->goPreviousPacket();
924 } else if (event
->key() == Qt::Key_F8
) {
925 packet_list_
->goNextPacket();
928 // Move along, citizen.
929 QMainWindow::keyPressEvent(event
);
932 void WiresharkMainWindow::closeEvent(QCloseEvent
*event
) {
933 saveWindowGeometry();
935 /* If we're in the middle of stopping a capture, don't do anything;
936 the user can try deleting the window after the capture stops. */
937 /* Note the ordinary File->Quit menu item is disabled but it's always
938 * possible to close via other means (e.g. the window title bar.)
940 if (capture_stopping_
) {
941 /* What if Wireshark is far behind the capture file and stopping is
942 taking a long time? (#19831) We could set the capture file state
943 to FILE_READ_ABORTED to make it stop quicker, but we might need to
944 warn about unsaved packets. We don't know if we're waiting to save
945 in testCaptureFileClose() having already warned about that, or if
946 the capture was stopped via other means. Call testCaptureFileClose
947 with special handling if capture_stopping_ is true? */
952 QString
before_what(tr(" before quitting"));
953 if (!testCaptureFileClose(before_what
, Quit
)) {
959 if (capture_options_dialog_
) capture_options_dialog_
->close();
961 // Make sure we kill any open dumpcap processes.
962 delete welcome_page_
;
964 // One of the many places we assume one main window.
965 if (!mainApp
->isInitialized()) {
966 // If we're still initializing, QCoreApplication::quit() won't
967 // exit properly because we are not in the event loop. This
968 // means that the application won't clean up after itself. We
969 // might want to call mainApp->processEvents() during startup
970 // instead so that we can do a normal exit here.
974 // When the main loop is not yet running (i.e. when openCaptureFile is
975 // executing in main.cpp), the above quit action has no effect.
976 // Schedule a quit action for the next execution of the main loop.
977 QMetaObject::invokeMethod(mainApp
, "quit", Qt::QueuedConnection
);
980 // XXX On windows the drag description is "Copy". It should be "Open" or
981 // "Merge" as appropriate. It looks like we need access to IDataObject in
982 // order to set DROPDESCRIPTION.
983 void WiresharkMainWindow::dragEnterEvent(QDragEnterEvent
*event
)
985 if (!event
->mimeData()->hasUrls())
991 if (!main_ui_
->actionFileOpen
->isEnabled()) {
992 // We could alternatively call setAcceptDrops(!capture_in_progress)
993 // in setMenusForCaptureInProgress but that wouldn't provide feedback.
995 mainApp
->pushStatus(WiresharkApplication::TemporaryStatus
, tr("Unable to drop files during capture."));
996 event
->setDropAction(Qt::IgnoreAction
);
1001 bool have_files
= false;
1002 foreach(QUrl drag_url
, event
->mimeData()->urls()) {
1003 if (!drag_url
.toLocalFile().isEmpty()) {
1010 event
->acceptProposedAction();
1014 void WiresharkMainWindow::dropEvent(QDropEvent
*event
)
1016 if (!event
->mimeData()->hasUrls())
1022 QList
<QByteArray
> local_files
;
1023 int max_dropped_files
= 100; // Arbitrary
1025 foreach(QUrl drop_url
, event
->mimeData()->urls()) {
1026 QString drop_file
= drop_url
.toLocalFile();
1027 if (!drop_file
.isEmpty()) {
1028 local_files
<< drop_file
.toUtf8();
1029 if (local_files
.size() >= max_dropped_files
) {
1035 event
->acceptProposedAction();
1037 if (local_files
.size() < 1) {
1044 if (local_files
.size() == 1) {
1045 openCaptureFile(local_files
.at(0));
1049 const char **in_filenames
= g_new(const char *, local_files
.size());
1050 char *tmpname
= NULL
;
1052 for (int i
= 0; i
< local_files
.size(); i
++) {
1053 in_filenames
[i
] = local_files
.at(i
).constData();
1056 /* merge the files in chronological order */
1057 if (cf_merge_files_to_tempfile(this, global_capture_opts
.temp_dir
, &tmpname
, static_cast<int>(local_files
.size()),
1059 wtap_pcapng_file_type_subtype(),
1061 /* Merge succeeded; close the currently-open file and try
1062 to open the merged capture file. */
1063 openCaptureFile(tmpname
, QString(), WTAP_TYPE_AUTO
, true);
1067 g_free(in_filenames
);
1070 // Apply recent settings to the main window geometry.
1071 // We haven't loaded the preferences at this point so we assume that the
1072 // position and size preference are enabled.
1073 // Note we might end up with unexpected screen geometries if the user
1074 // unplugs or plugs in a monitor:
1075 // https://bugreports.qt.io/browse/QTBUG-44213
1076 void WiresharkMainWindow::loadWindowGeometry()
1078 int min_sensible_dimension
= 200;
1081 if (recent
.gui_geometry_main_maximized
) {
1082 // [save|restore]Geometry does a better job (on Linux and Windows)
1083 // of restoring to the original monitor because it saves
1084 // QGuiApplication::screens().indexOf(screen())
1085 // (it also saves Qt::WindowFullScreen, restores the non-maximized
1086 // size even when starting out maximized, etc.)
1087 // Monitors of different DPI might still be tricky:
1088 // https://bugreports.qt.io/browse/QTBUG-70721
1089 // https://bugreports.qt.io/browse/QTBUG-77385
1091 // We might eventually want to always use restoreGeometry, but
1092 // for now at least use it just for maximized because it's better
1093 // then what we've been doing.
1094 if (recent
.gui_geometry_main
== nullptr ||
1095 !restoreGeometry(QByteArray::fromHex(recent
.gui_geometry_main
))) {
1097 setWindowState(Qt::WindowMaximized
);
1102 QRect
recent_geom(recent
.gui_geometry_main_x
, recent
.gui_geometry_main_y
,
1103 recent
.gui_geometry_main_width
, recent
.gui_geometry_main_height
);
1104 if (!rect_on_screen(recent_geom
)) {
1105 // We're not visible on any screens. See if we can move onscreen
1106 // without resizing.
1107 recent_geom
.moveTo(50, 50); // recent.c defaults to 20.
1110 if (!rect_on_screen(recent_geom
)) {
1111 // Give up and use the default geometry.
1115 // if (prefs.gui_geometry_save_position) {
1116 move(recent_geom
.topLeft());
1119 if (// prefs.gui_geometry_save_size &&
1120 recent_geom
.width() > min_sensible_dimension
&&
1121 recent_geom
.height() > min_sensible_dimension
) {
1122 resize(recent_geom
.size());
1127 void WiresharkMainWindow::saveWindowGeometry()
1129 if (prefs
.gui_geometry_save_position
||
1130 prefs
.gui_geometry_save_size
||
1131 prefs
.gui_geometry_save_maximized
) {
1132 g_free(recent
.gui_geometry_main
);
1133 recent
.gui_geometry_main
= g_strdup(saveGeometry().toHex().constData());
1136 if (prefs
.gui_geometry_save_position
) {
1137 recent
.gui_geometry_main_x
= pos().x();
1138 recent
.gui_geometry_main_y
= pos().y();
1141 if (prefs
.gui_geometry_save_size
) {
1142 recent
.gui_geometry_main_width
= size().width();
1143 recent
.gui_geometry_main_height
= size().height();
1146 if (prefs
.gui_geometry_save_maximized
) {
1147 // On macOS this is false when it shouldn't be
1148 // XXX: Does save/restoreGeometry work any better on macOS
1149 // for maximized windows? Apparently not:
1150 // https://bugreports.qt.io/browse/QTBUG-100272
1151 recent
.gui_geometry_main_maximized
= isMaximized();
1154 if (master_split_
.sizes().length() > 0) {
1155 recent
.gui_geometry_main_upper_pane
= master_split_
.sizes()[0];
1158 g_free(recent
.gui_geometry_main_master_split
);
1159 g_free(recent
.gui_geometry_main_extra_split
);
1160 recent
.gui_geometry_main_master_split
= g_strdup(master_split_
.saveState().toHex().constData());
1161 recent
.gui_geometry_main_extra_split
= g_strdup(extra_split_
.saveState().toHex().constData());
1163 // Saving the QSplitter state is more accurate (#19361), but save
1164 // the old GTK-style pane information for backwards compatibility
1165 // for switching back and forth with older versions.
1166 if (master_split_
.sizes().length() > 2) {
1167 recent
.gui_geometry_main_lower_pane
= master_split_
.sizes()[1];
1168 } else if (extra_split_
.sizes().length() > 0) {
1169 recent
.gui_geometry_main_lower_pane
= extra_split_
.sizes()[0];
1173 // Our event loop becomes nested whenever we call update_progress_dlg, which
1174 // includes several places in file.c. The GTK+ UI stays out of trouble by
1175 // showing a modal progress dialog. We attempt to do the equivalent below by
1176 // disabling parts of the main window. At a minimum the ProgressFrame in the
1177 // main status bar must remain accessible.
1179 // We might want to do this any time the main status bar progress frame is
1180 // shown and hidden.
1181 void WiresharkMainWindow::freeze()
1183 freeze_focus_
= mainApp
->focusWidget();
1185 // XXX Alternatively we could just disable and enable the main menu.
1186 for (int i
= 0; i
< freeze_actions_
.size(); i
++) {
1187 QAction
*action
= freeze_actions_
[i
].first
;
1188 freeze_actions_
[i
].second
= action
->isEnabled();
1189 action
->setEnabled(false);
1191 main_ui_
->centralWidget
->setEnabled(false);
1194 void WiresharkMainWindow::thaw()
1196 main_ui_
->centralWidget
->setEnabled(true);
1197 for (int i
= 0; i
< freeze_actions_
.size(); i
++) {
1198 freeze_actions_
[i
].first
->setEnabled(freeze_actions_
[i
].second
);
1201 if (freeze_focus_
) freeze_focus_
->setFocus();
1202 freeze_focus_
= NULL
;
1205 void WiresharkMainWindow::mergeCaptureFile()
1207 QString file_name
= "";
1208 QString read_filter
= "";
1209 dfilter_t
*rfcode
= NULL
;
1212 if (!capture_file_
.capFile())
1215 if (prefs
.gui_ask_unsaved
) {
1216 if (cf_has_unsaved_data(capture_file_
.capFile())) {
1217 QMessageBox msg_dialog
;
1218 char *display_basename
;
1221 msg_dialog
.setIcon(QMessageBox::Question
);
1222 /* This file has unsaved data; ask the user whether to save
1224 if (capture_file_
.capFile()->is_tempfile
) {
1225 msg_dialog
.setText(tr("Save packets before merging?"));
1226 msg_dialog
.setInformativeText(tr("A temporary capture file can't be merged."));
1229 * Format the message.
1231 display_basename
= g_filename_display_basename(capture_file_
.capFile()->filename
);
1232 msg_dialog
.setText(tr("Save changes in \"%1\" before merging?").arg(display_basename
));
1233 g_free(display_basename
);
1234 msg_dialog
.setInformativeText(tr("Changes must be saved before the files can be merged."));
1237 msg_dialog
.setStandardButtons(QMessageBox::Save
| QMessageBox::Cancel
);
1238 msg_dialog
.setDefaultButton(QMessageBox::Save
);
1240 response
= msg_dialog
.exec();
1244 case QMessageBox::Save
:
1245 /* Save the file but don't close it */
1246 saveCaptureFile(capture_file_
.capFile(), false);
1249 case QMessageBox::Cancel
:
1251 /* Don't do the merge. */
1258 CaptureFileDialog
merge_dlg(this, capture_file_
.capFile());
1260 cf_status_t merge_status
;
1261 char *in_filenames
[2];
1264 if (merge_dlg
.merge(file_name
, read_filter
)) {
1265 df_error_t
*df_err
= NULL
;
1267 if (!dfilter_compile(qUtf8Printable(read_filter
), &rfcode
, &df_err
)) {
1268 /* Not valid. Tell the user, and go back and run the file
1269 selection box again once they dismiss the alert. */
1270 // Similar to commandline_info.jfilter section in main().
1271 QMessageBox::warning(this, tr("Invalid Read Filter"),
1272 tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter
, df_err
->msg
),
1274 df_error_free(&df_err
);
1281 file_type
= capture_file_
.capFile()->cd_t
;
1283 /* Try to merge or append the two files */
1284 if (merge_dlg
.mergeType() == 0) {
1285 /* chronological order */
1286 in_filenames
[0] = g_strdup(capture_file_
.capFile()->filename
);
1287 in_filenames
[1] = qstring_strdup(file_name
);
1288 merge_status
= cf_merge_files_to_tempfile(this, global_capture_opts
.temp_dir
, &tmpname
, 2, in_filenames
, file_type
, false);
1289 } else if (merge_dlg
.mergeType() <= 0) {
1291 in_filenames
[0] = qstring_strdup(file_name
);
1292 in_filenames
[1] = g_strdup(capture_file_
.capFile()->filename
);
1293 merge_status
= cf_merge_files_to_tempfile(this, global_capture_opts
.temp_dir
, &tmpname
, 2, in_filenames
, file_type
, true);
1296 in_filenames
[0] = g_strdup(capture_file_
.capFile()->filename
);
1297 in_filenames
[1] = qstring_strdup(file_name
);
1298 merge_status
= cf_merge_files_to_tempfile(this, global_capture_opts
.temp_dir
, &tmpname
, 2, in_filenames
, file_type
, true);
1301 g_free(in_filenames
[0]);
1302 g_free(in_filenames
[1]);
1304 if (merge_status
!= CF_OK
) {
1305 dfilter_free(rfcode
);
1310 cf_close(capture_file_
.capFile());
1312 /* Try to open the merged capture file. */
1313 // XXX - Just free rfcode and call
1314 // openCaptureFile(tmpname, read_filter, WTAP_TYPE_AUTO, true);
1315 CaptureFile::globalCapFile()->window
= this;
1316 if (cf_open(CaptureFile::globalCapFile(), tmpname
, WTAP_TYPE_AUTO
, true /* temporary file */, &err
) != CF_OK
) {
1317 /* We couldn't open it; fail. */
1318 CaptureFile::globalCapFile()->window
= NULL
;
1319 dfilter_free(rfcode
);
1324 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1325 it closed the previous capture file, and thus destroyed any
1326 previous read filter attached to "cf"). */
1327 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode
);
1329 switch (cf_read(CaptureFile::globalCapFile(), /*reloading=*/false)) {
1333 /* Just because we got an error, that doesn't mean we were unable
1334 to read any of the file; we handle what we could get from the
1338 case CF_READ_ABORTED
:
1339 /* The user bailed out of re-reading the capture file; the
1340 capture file has been closed - just free the capture file name
1341 string and return (without changing the last containing
1347 /* This is a tempfile; don't change the last open directory. */
1349 main_ui_
->statusBar
->showExpert();
1355 void WiresharkMainWindow::importCaptureFile() {
1356 ImportTextDialog import_dlg
;
1358 QString
before_what(tr(" before importing a capture"));
1359 if (!testCaptureFileClose(before_what
))
1364 if (import_dlg
.result() != QDialog::Accepted
) {
1369 openCaptureFile(import_dlg
.capfileName(), QString(), WTAP_TYPE_AUTO
, true);
1372 bool WiresharkMainWindow::saveCaptureFile(capture_file
*cf
, bool dont_reopen
) {
1374 bool discard_comments
;
1376 if (cf
->is_tempfile
) {
1377 /* This is a temporary capture file, so saving it means saving
1378 it to a permanent file. Prompt the user for a location
1379 to which to save it. Don't require that the file format
1380 support comments - if it's a temporary capture file, it's
1381 probably pcapng, which supports comments and, if it's
1382 not pcapng, let the user decide what they want to do
1383 if they've added comments. */
1384 return saveAsCaptureFile(cf
, false, dont_reopen
);
1386 if (cf
->unsaved_changes
) {
1387 cf_write_status_t status
;
1389 /* This is not a temporary capture file, but it has unsaved
1390 changes, so saving it means doing a "safe save" on top
1391 of the existing file, in the same format - no UI needed
1392 unless the file has comments and the file's format doesn't
1395 If the file has comments, does the file's format support them?
1396 If not, ask the user whether they want to discard the comments
1397 or choose a different format. */
1398 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf
, cf
->cd_t
)) {
1401 /* The file can be saved in the specified format as is;
1402 just drive on and save in the format they selected. */
1403 discard_comments
= false;
1406 case SAVE_WITHOUT_COMMENTS
:
1407 /* The file can't be saved in the specified format as is,
1408 but it can be saved without the comments, and the user
1409 said "OK, discard the comments", so save it in the
1410 format they specified without the comments. */
1411 discard_comments
= true;
1414 case SAVE_IN_ANOTHER_FORMAT
:
1415 /* There are file formats in which we can save this that
1416 support comments, and the user said not to delete the
1417 comments. Do a "Save As" so the user can select
1418 one of those formats and choose a file name. */
1419 return saveAsCaptureFile(cf
, true, dont_reopen
);
1422 /* The user said "forget it". Just return. */
1426 /* Squelch warnings that discard_comments is being used
1428 ws_assert_not_reached();
1432 /* XXX - cf->filename might get freed out from under us, because
1433 the code path through which cf_save_records() goes currently
1434 closes the current file and then opens and reloads the saved file,
1435 so make a copy and free it later. */
1436 file_name
= cf
->filename
;
1437 status
= cf_save_records(cf
, qUtf8Printable(file_name
), cf
->cd_t
, cf
->compression_type
,
1438 discard_comments
, dont_reopen
);
1442 /* The save succeeded; we're done.
1443 If we discarded comments, redraw the packet list to reflect
1444 any packets that no longer have comments. If we had unsaved
1445 changes, redraw the packet list, because saving a time
1446 shift zeroes out the frame.offset_shift field.
1447 If we had a color filter based on frame data, recolor. */
1448 /* XXX: If there is a filter based on those, we want to force
1449 a rescan with the current filter (we don't actually
1452 if (discard_comments
|| cf
->unsaved_changes
) {
1453 if (color_filters_use_proto(proto_get_id_by_filter_name("frame"))) {
1454 packet_list_
->recolorPackets();
1456 packet_list_
->redrawVisiblePackets();
1460 cf
->unsaved_changes
= false; //we just saved so we signal that we have no unsaved changes
1461 updateForUnsavedChanges(); // we update the title bar to remove the *
1464 case CF_WRITE_ERROR
:
1465 /* The write failed.
1466 XXX - OK, what do we do now? Let them try a
1467 "Save As", in case they want to try to save to a
1468 different directory or file system? */
1471 case CF_WRITE_ABORTED
:
1472 /* The write was aborted; just drive on. */
1476 /* Otherwise just do nothing. */
1482 bool WiresharkMainWindow::saveAsCaptureFile(capture_file
*cf
, bool must_support_comments
, bool dont_reopen
) {
1483 QString file_name
= "";
1485 wtap_compression_type compression_type
;
1486 cf_write_status_t status
;
1488 bool discard_comments
= false;
1495 CaptureFileDialog
save_as_dlg(this, cf
);
1497 /* If the file has comments, does the format the user selected
1498 support them? If not, ask the user whether they want to
1499 discard the comments or choose a different format. */
1500 switch (save_as_dlg
.saveAs(file_name
, must_support_comments
)) {
1503 /* The file can be saved in the specified format as is;
1504 just drive on and save in the format they selected. */
1505 discard_comments
= false;
1508 case SAVE_WITHOUT_COMMENTS
:
1509 /* The file can't be saved in the specified format as is,
1510 but it can be saved without the comments, and the user
1511 said "OK, discard the comments", so save it in the
1512 format they specified without the comments. */
1513 discard_comments
= true;
1516 case SAVE_IN_ANOTHER_FORMAT
:
1517 /* There are file formats in which we can save this that
1518 support comments, and the user said not to delete the
1519 comments. The combo box of file formats has had the
1520 formats that don't support comments trimmed from it,
1521 so run the dialog again, to let the user decide
1522 whether to save in one of those formats or give up. */
1523 must_support_comments
= true;
1527 /* The user said "forget it". Just get rid of the dialog box
1531 file_type
= save_as_dlg
.selectedFileType();
1532 if (file_type
== WTAP_FILE_TYPE_SUBTYPE_UNKNOWN
) {
1533 /* This "should not happen". */
1534 QMessageBox msg_dialog
;
1536 msg_dialog
.setIcon(QMessageBox::Critical
);
1537 msg_dialog
.setText(tr("Unknown file type returned by merge dialog."));
1538 msg_dialog
.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues."));
1542 compression_type
= save_as_dlg
.compressionType();
1545 // /* If the file exists and it's user-immutable or not writable,
1546 // ask the user whether they want to override that. */
1547 // if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) {
1548 // /* They don't. Let them try another file name or cancel. */
1553 /* Attempt to save the file */
1554 status
= cf_save_records(cf
, qUtf8Printable(file_name
), file_type
, compression_type
,
1555 discard_comments
, dont_reopen
);
1559 /* The save succeeded; we're done. */
1560 /* Save the directory name for future file dialogs. */
1561 dirname
= qstring_strdup(file_name
); /* Overwrites cf_name */
1562 set_last_open_dir(get_dirname(dirname
));
1564 /* The save succeeded; we're done.
1565 If we discarded comments, redraw the packet list to reflect
1566 any packets that no longer have comments. If we had unsaved
1567 changes, redraw the packet list, because saving a time
1568 shift zeroes out the frame.offset_shift field.
1569 If we had a color filter based on frame data, recolor. */
1570 /* XXX: If there is a filter based on those, we want to force
1571 a rescan with the current filter (we don't actually
1574 if (discard_comments
|| cf
->unsaved_changes
) {
1575 if (color_filters_use_proto(proto_get_id_by_filter_name("frame"))) {
1576 packet_list_
->recolorPackets();
1578 packet_list_
->redrawVisiblePackets();
1582 cf
->unsaved_changes
= false; //we just saved so we signal that we have no unsaved changes
1583 updateForUnsavedChanges(); // we update the title bar to remove the *
1584 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1585 add_menu_recent_capture_file(qUtf8Printable(file_name
), false);
1588 case CF_WRITE_ERROR
:
1589 /* The save failed; let the user try again. */
1592 case CF_WRITE_ABORTED
:
1593 /* The user aborted the save; just return. */
1600 void WiresharkMainWindow::exportSelectedPackets() {
1601 QString file_name
= "";
1603 wtap_compression_type compression_type
;
1604 packet_range_t range
;
1605 cf_write_status_t status
;
1607 bool discard_comments
= false;
1609 if (!capture_file_
.capFile())
1612 /* Init the packet range */
1613 packet_range_init(&range
, capture_file_
.capFile());
1614 range
.process_filtered
= true;
1615 range
.include_dependents
= true;
1617 QList
<int> rows
= packet_list_
->selectedRows(true);
1619 QStringList entries
;
1620 foreach (int row
, rows
)
1621 entries
<< QString::number(row
);
1622 QString selRange
= entries
.join(",");
1625 CaptureFileDialog
esp_dlg(this, capture_file_
.capFile());
1627 /* If the file has comments, does the format the user selected
1628 support them? If not, ask the user whether they want to
1629 discard the comments or choose a different format. */
1630 switch (esp_dlg
.exportSelectedPackets(file_name
, &range
, selRange
)) {
1633 /* The file can be saved in the specified format as is;
1634 just drive on and save in the format they selected. */
1635 discard_comments
= false;
1638 case SAVE_WITHOUT_COMMENTS
:
1639 /* The file can't be saved in the specified format as is,
1640 but it can be saved without the comments, and the user
1641 said "OK, discard the comments", so save it in the
1642 format they specified without the comments. */
1643 discard_comments
= true;
1646 case SAVE_IN_ANOTHER_FORMAT
:
1647 /* There are file formats in which we can save this that
1648 support comments, and the user said not to delete the
1649 comments. The combo box of file formats has had the
1650 formats that don't support comments trimmed from it,
1651 so run the dialog again, to let the user decide
1652 whether to save in one of those formats or give up. */
1656 /* The user said "forget it". Just get rid of the dialog box
1662 * Check that we're not going to save on top of the current
1664 * We do it here so we catch all cases ...
1665 * Unfortunately, the file requester gives us an absolute file
1666 * name and the read file name may be relative (if supplied on
1667 * the command line). From Joerg Mayer.
1669 if (files_identical(capture_file_
.capFile()->filename
, qUtf8Printable(file_name
))) {
1670 QMessageBox msg_box
;
1671 char *display_basename
= g_filename_display_basename(qUtf8Printable(file_name
));
1673 msg_box
.setIcon(QMessageBox::Critical
);
1674 msg_box
.setText(tr("Unable to export to \"%1\".").arg(display_basename
));
1675 msg_box
.setInformativeText(tr("You cannot export packets to the current capture file."));
1676 msg_box
.setStandardButtons(QMessageBox::Ok
);
1677 msg_box
.setDefaultButton(QMessageBox::Ok
);
1679 g_free(display_basename
);
1683 file_type
= esp_dlg
.selectedFileType();
1684 if (file_type
== WTAP_FILE_TYPE_SUBTYPE_UNKNOWN
) {
1685 /* This "should not happen". */
1686 QMessageBox msg_box
;
1688 msg_box
.setIcon(QMessageBox::Critical
);
1689 msg_box
.setText(tr("Unknown file type returned by export dialog."));
1690 msg_box
.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues."));
1694 compression_type
= esp_dlg
.compressionType();
1697 // /* If the file exists and it's user-immutable or not writable,
1698 // ask the user whether they want to override that. */
1699 // if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) {
1700 // /* They don't. Let them try another file name or cancel. */
1705 /* Attempt to save the file */
1706 status
= cf_export_specified_packets(capture_file_
.capFile(), qUtf8Printable(file_name
), &range
, file_type
, compression_type
);
1710 /* The save succeeded; we're done. */
1711 /* Save the directory name for future file dialogs. */
1712 dirname
= qstring_strdup(file_name
); /* Overwrites cf_name */
1713 set_last_open_dir(get_dirname(dirname
));
1715 /* If we discarded comments, redraw the packet list to reflect
1716 any packets that no longer have comments. */
1717 /* XXX: Why? We're exporting some packets to a new file but not
1718 changing our current capture file, that shouldn't change the
1719 current packet list. */
1720 if (discard_comments
)
1721 packet_list_
->redrawVisiblePackets();
1722 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1723 add_menu_recent_capture_file(qUtf8Printable(file_name
), false);
1726 case CF_WRITE_ERROR
:
1727 /* The save failed; let the user try again. */
1730 case CF_WRITE_ABORTED
:
1731 /* The user aborted the save; just return. */
1737 packet_range_cleanup(&range
);
1740 void WiresharkMainWindow::exportDissections(export_type_e export_type
) {
1741 capture_file
*cf
= capture_file_
.capFile();
1742 g_return_if_fail(cf
);
1744 QList
<int> rows
= packet_list_
->selectedRows(true);
1746 QStringList entries
;
1747 foreach (int row
, rows
)
1748 entries
<< QString::number(row
);
1749 QString selRange
= entries
.join(",");
1751 ExportDissectionDialog
*ed_dlg
= new ExportDissectionDialog(this, cf
, export_type
, selRange
);
1752 ed_dlg
->setWindowModality(Qt::ApplicationModal
);
1753 ed_dlg
->setAttribute(Qt::WA_DeleteOnClose
);
1757 bool WiresharkMainWindow::testCaptureFileClose(QString before_what
, FileCloseContext context
) {
1758 bool capture_in_progress
= false;
1759 bool do_close_file
= false;
1761 if (!capture_file_
.capFile() || capture_file_
.capFile()->state
== FILE_CLOSED
)
1762 return true; /* Already closed, nothing to do */
1763 // XXX - capture_file_.capFile() returns NULL for a pending (not yet opened)
1764 // file in non real time mode. That's what we want for most of Wireshark
1765 // (because the capture_file* struct isn't fully set up crashes and other
1766 // oddities can occur, see #4035), but we need to examine it here.
1767 // We could use CaptureFile::globalCapFile(), or add a new member that
1768 // returns the capture_file* when it's pending but not when closed.
1770 if (capture_file_
.capFile()->read_lock
) {
1772 * If the file is being redissected, we cannot stop the capture since
1773 * that would crash and burn "cf_read", so stop early. Ideally all
1774 * callers should be modified to check this condition and act
1775 * accordingly (ignore action or queue it up), so print a warning.
1777 * setMenusForCaptureFile() disables most of the ways to reach
1778 * here (like Reload, Reload as File Format) when reading an file
1779 * (offline or live capture). Reload Lua Plugins is disabled and
1780 * enabled in a different place (because reloading Lua plugins
1781 * makes sense when there's no capture file, unlike those options),
1782 * although it's disabled while reading an offline file, not a live
1783 * capture (reloading Lua plugins only requires closing the file
1784 * if the current file is opened with a Lua FileHandler registered
1785 * to wiretap, which isn't the case with live captures as they only
1786 * use pcap and pcapng.)
1788 ws_warning("Refusing to close \"%s\" which is being read.", capture_file_
.capFile()->filename
);
1793 if (capture_file_
.capFile()->state
== FILE_READ_IN_PROGRESS
||
1794 capture_file_
.capFile()->state
== FILE_READ_PENDING
) {
1796 * FILE_READ_IN_PROGRESS is true if we're reading a capture file
1797 * *or* if we're doing a live capture. From the capture file itself we
1798 * cannot differentiate the cases, so check the current capture session.
1799 * FILE_READ_PENDING is only used for a live capture, but it doesn't
1800 * hurt to check it here.
1802 capture_in_progress
= captureSession()->state
!= CAPTURE_STOPPED
;
1806 if (prefs
.gui_ask_unsaved
) {
1807 if (cf_has_unsaved_data(capture_file_
.capFile())) {
1808 // XXX - What if this a capture_in_progress in non real time mode?
1809 // Then the capture file isn't open and so it doesn't report
1810 // unsaved data. We need to check the captureSession count_pending.
1811 if (context
== Update
) {
1812 // We're being called from the software update window;
1813 // don't spawn yet another dialog. Just try again later.
1814 // XXX: The WinSparkle dialogs *aren't* modal, and a user
1815 // can bring Wireshark to the foreground, close/save the
1816 // file, and then click "Install Update" again, but it
1817 // seems like many users don't expect that (and also don't
1818 // know that Help->Check for Updates... exist, only knowing
1819 // about the automatic check.) See #17658 and duplicates.
1820 // Maybe we *should* spawn the dialog?
1824 QMessageBox
msg_dialog(this);
1827 QPushButton
*save_button
;
1828 QPushButton
*discard_button
;
1830 msg_dialog
.setIcon(QMessageBox::Question
);
1831 msg_dialog
.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS
);
1833 /* This file has unsaved data or there's a capture in
1834 progress; ask the user whether to save the data. */
1835 if (capture_in_progress
&& context
!= Restart
) {
1836 question
= tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what
);
1837 infotext
= tr("Your captured packets will be lost if you don't save them.");
1838 } else if (capture_file_
.capFile()->is_tempfile
) {
1839 if (context
== Reload
) {
1840 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1841 question
= tr("Do you want to save the changes you've made%1?").arg(before_what
);
1842 infotext
= tr("Your changes will be lost if you don't save them.");
1844 question
= tr("Do you want to save the captured packets%1?").arg(before_what
);
1845 infotext
= tr("Your captured packets will be lost if you don't save them.");
1848 // No capture in progress and not a tempfile, so this is not unsaved packets
1849 char *display_basename
= g_filename_display_basename(capture_file_
.capFile()->filename
);
1850 question
= tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename
, before_what
);
1851 infotext
= tr("Your changes will be lost if you don't save them.");
1852 g_free(display_basename
);
1855 msg_dialog
.setText(question
);
1856 msg_dialog
.setInformativeText(infotext
);
1858 // XXX Text comes from ui/gtk/stock_icons.[ch]
1859 // Note that the button roles differ from the GTK+ version.
1860 // Cancel = RejectRole
1861 // Save = AcceptRole
1862 // Don't Save = DestructiveRole
1863 msg_dialog
.addButton(QMessageBox::Cancel
);
1865 if (capture_in_progress
) {
1866 QString save_button_text
;
1867 if (context
== Restart
) {
1868 save_button_text
= tr("Save before Continue");
1870 save_button_text
= tr("Stop and Save");
1872 save_button
= msg_dialog
.addButton(save_button_text
, QMessageBox::AcceptRole
);
1874 save_button
= msg_dialog
.addButton(QMessageBox::Save
);
1876 msg_dialog
.setDefaultButton(save_button
);
1878 QString discard_button_text
;
1879 if (capture_in_progress
) {
1882 discard_button_text
= tr("Stop and Quit &without Saving");
1885 discard_button_text
= tr("Continue &without Saving");
1888 discard_button_text
= tr("Stop and Continue &without Saving");
1894 discard_button_text
= tr("Quit &without Saving");
1898 discard_button_text
= tr("Continue &without Saving");
1902 discard_button
= msg_dialog
.addButton(discard_button_text
, QMessageBox::DestructiveRole
);
1904 #if defined(Q_OS_MAC)
1906 * In macOS, the "default button" is not necessarily the
1907 * button that has the input focus; Enter/Return activates
1908 * the default button, and the spacebar activates the button
1909 * that has the input focus, and they might be different
1912 * In a "do you want to save" dialog, for example, the
1913 * "save" button is the default button, and the "don't
1914 * save" button has the input focus, so you can press
1915 * Enter/Return to save or space not to save (or Escape
1916 * to dismiss the dialog).
1918 * In Qt terms, this means "no auto-default", as auto-default
1919 * makes the button with the input focus the default button,
1920 * so that Enter/Return will activate it.
1922 QList
<QAbstractButton
*> buttons
= msg_dialog
.buttons();
1923 for (int i
= 0; i
< buttons
.size(); ++i
) {
1924 QPushButton
*button
= static_cast<QPushButton
*>(buttons
.at(i
));
1925 button
->setAutoDefault(false);
1929 * It also means that the "don't save" button should be the one
1930 * initially given the focus.
1932 discard_button
->setFocus();
1935 * On Windows, if multiple Wireshark processes are open, another
1936 * application has focus, and "Close all [Wireshark] windows" is
1937 * chosen from the taskbar, we need to activate the window to
1938 * at least flash the taskbar (#16309).
1942 /* According to the Qt doc:
1943 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1945 * Therefore we should use clickedButton() to determine which button was clicked. */
1947 if (msg_dialog
.clickedButton() == save_button
) {
1949 /* If there's a capture in progress, we have to stop the capture
1950 and then do the save. Wait until we dissect all the packets. */
1951 if (capture_in_progress
)
1954 /* Save the file and close it */
1955 // XXX if no packets were captured, any unsaved comments set by
1956 // the user are silently discarded because capFile() is null.
1957 if (capture_file_
.capFile() && saveCaptureFile(capture_file_
.capFile(), true) == false)
1959 do_close_file
= true;
1960 } else if (msg_dialog
.clickedButton() == discard_button
) {
1961 /* Just close the file, discarding changes */
1962 do_close_file
= true;
1964 // cancelButton or some other unspecified button
1968 /* Unchanged file or capturing with no packets */
1969 do_close_file
= true;
1972 /* User asked not to be bothered by those prompts, just close it.
1973 XXX - should that apply only to saving temporary files? */
1974 do_close_file
= true;
1978 * Are we done with this file and should we close the file?
1980 if (do_close_file
) {
1982 /* If there's a capture in progress, we have to stop the capture
1983 and then do the close. We don't care about unsaved packets,
1984 so we can abort the read. */
1985 if (capture_in_progress
)
1987 else if (capture_file_
.capFile() && capture_file_
.capFile()->state
== FILE_READ_IN_PROGRESS
) {
1989 * When an offline capture is being read, mark it as aborted.
1990 * cf_read will be responsible for actually closing the capture.
1992 * We cannot just invoke cf_close here since cf_read is up in the
1993 * call chain. (update_progress_dlg can end up processing the Quit
1994 * event from the user which then ends up here.)
1995 * See also the above "read_lock" check.
1997 * XXX - How do we get here with FILE_READ_IN_PROGRESS state but
1998 * read_lock false? Some time between cf_open and cf_read?
2000 capture_file_
.capFile()->state
= FILE_READ_ABORTED
;
2004 /* Clear MainWindow file name details */
2005 gbl_cur_main_window_
->setMwFileName("");
2007 /* captureStop() will close the file if not having any packets */
2008 if (capture_file_
.capFile() && context
!= Restart
&& context
!= Reload
)
2009 // Don't really close if Restart or Reload
2010 cf_close(capture_file_
.capFile());
2013 return true; /* File closed */
2016 void WiresharkMainWindow::captureStop(bool discard
) {
2017 if (discard
&& capture_file_
.capFile()) {
2018 capture_file_
.capFile()->state
= FILE_READ_ABORTED
;
2022 while (capture_file_
.capFile() && (capture_file_
.capFile()->state
!= FILE_READ_DONE
&&
2023 capture_file_
.capFile()->state
!= FILE_CLOSED
)) {
2024 WiresharkApplication::processEvents();
2028 void WiresharkMainWindow::findTextCodecs() {
2029 const QList
<int> mibs
= QTextCodec::availableMibs();
2030 QRegularExpression
ibmRegExp("^IBM([0-9]+).*$");
2031 QRegularExpression
iso8859RegExp("^ISO-8859-([0-9]+).*$");
2032 QRegularExpression
windowsRegExp("^WINDOWS-([0-9]+).*$");
2033 QRegularExpressionMatch match
;
2034 for (int mib
: mibs
) {
2035 QTextCodec
*codec
= QTextCodec::codecForMib(mib
);
2036 // QTextCodec::availableMibs() returns a list of hard-coded MIB
2037 // numbers, it doesn't check if they are really available. ICU data may
2038 // not have been compiled with support for all encodings.
2043 QString key
= codec
->name().toUpper();
2046 if (key
.localeAwareCompare("IBM") < 0) {
2048 } else if ((match
= ibmRegExp
.match(key
)).hasMatch()) {
2049 rank
= match
.captured(1).size(); // Up to 5
2050 } else if (key
.localeAwareCompare("ISO-8859-") < 0) {
2052 } else if ((match
= iso8859RegExp
.match(key
)).hasMatch()) {
2053 rank
= 6 + match
.captured(1).size(); // Up to 6 + 2
2054 } else if (key
.localeAwareCompare("WINDOWS-") < 0) {
2056 } else if ((match
= windowsRegExp
.match(key
)).hasMatch()) {
2057 rank
= 9 + match
.captured(1).size(); // Up to 9 + 4
2061 // This doesn't perfectly well order the IBM codecs because it's
2062 // annoying to properly place IBM00858 and IBM00924 in the middle of
2063 // code page numbers not zero padded to 5 digits.
2064 // We could manipulate the key further to have more commonly used
2065 // charsets earlier. IANA MIB ordering would be unexpected:
2066 // https://www.iana.org/assignments/character-sets/character-sets.xml
2067 // For data about use in HTTP (other protocols can be quite different):
2068 // https://w3techs.com/technologies/overview/character_encoding
2070 key
.prepend(char('0' + rank
));
2071 // We use a map here because, due to backwards compatibility,
2072 // the same QTextCodec may be returned for multiple MIBs, which
2073 // happens for GBK/GB2312, EUC-KR/windows-949/UHC, and others.
2074 text_codec_map_
.insert(key
, codec
);
2078 void WiresharkMainWindow::initMainToolbarIcons()
2080 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
2081 int icon_size
= style()->pixelMetric(QStyle::PM_SmallIconSize
);
2082 #if !defined(Q_OS_WIN)
2083 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
2084 // The macOS HIG specifies 32-pixel icons but they're a little too
2086 icon_size
= icon_size
* 3 / 2;
2088 main_ui_
->mainToolBar
->setIconSize(QSize(icon_size
, icon_size
));
2090 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
2091 // toolbar item but that clutters up our menu. Set menu icons sparingly.
2093 main_ui_
->actionCaptureStart
->setIcon(StockIcon("x-capture-start"));
2094 main_ui_
->actionCaptureStop
->setIcon(StockIcon("x-capture-stop"));
2095 main_ui_
->actionCaptureRestart
->setIcon(StockIcon("x-capture-restart"));
2096 main_ui_
->actionCaptureOptions
->setIcon(StockIcon("x-capture-options"));
2098 // Menu icons are disabled in wireshark_main_window.ui for these File-> items.
2099 main_ui_
->actionFileOpen
->setIcon(StockIcon("document-open"));
2100 main_ui_
->actionFileSave
->setIcon(StockIcon("x-capture-file-save"));
2101 main_ui_
->actionFileClose
->setIcon(StockIcon("x-capture-file-close"));
2103 main_ui_
->actionEditFindPacket
->setIcon(StockIcon("edit-find"));
2104 main_ui_
->actionGoPreviousPacket
->setIcon(StockIcon("go-previous"));
2105 main_ui_
->actionGoNextPacket
->setIcon(StockIcon("go-next"));
2106 main_ui_
->actionGoGoToPacket
->setIcon(StockIcon("go-jump"));
2107 main_ui_
->actionGoFirstPacket
->setIcon(StockIcon("go-first"));
2108 main_ui_
->actionGoLastPacket
->setIcon(StockIcon("go-last"));
2109 main_ui_
->actionGoPreviousConversationPacket
->setIcon(StockIcon("go-previous"));
2110 main_ui_
->actionGoNextConversationPacket
->setIcon(StockIcon("go-next"));
2111 #if defined(Q_OS_MAC)
2112 main_ui_
->actionGoPreviousConversationPacket
->setShortcut(QKeySequence(Qt::META
| Qt::Key_Comma
));
2113 main_ui_
->actionGoNextConversationPacket
->setShortcut(QKeySequence(Qt::META
| Qt::Key_Period
));
2115 main_ui_
->actionGoPreviousHistoryPacket
->setIcon(StockIcon("go-previous"));
2116 main_ui_
->actionGoNextHistoryPacket
->setIcon(StockIcon("go-next"));
2117 main_ui_
->actionGoAutoScroll
->setIcon(StockIcon("x-stay-last"));
2119 main_ui_
->actionViewColorizePacketList
->setIcon(StockIcon("x-colorize-packets"));
2121 QList
<QKeySequence
> zi_seq
= main_ui_
->actionViewZoomIn
->shortcuts();
2122 zi_seq
<< QKeySequence(Qt::CTRL
| Qt::Key_Equal
);
2123 main_ui_
->actionViewZoomIn
->setIcon(StockIcon("zoom-in"));
2124 main_ui_
->actionViewZoomIn
->setShortcuts(zi_seq
);
2125 main_ui_
->actionViewZoomOut
->setIcon(StockIcon("zoom-out"));
2126 main_ui_
->actionViewNormalSize
->setIcon(StockIcon("zoom-original"));
2127 main_ui_
->actionViewResizeColumns
->setIcon(StockIcon("x-resize-columns"));
2128 main_ui_
->actionViewResetLayout
->setIcon(StockIcon("x-reset-layout_2"));
2129 main_ui_
->actionViewReload
->setIcon(StockIcon("x-capture-file-reload"));
2131 main_ui_
->actionNewDisplayFilterExpression
->setIcon(StockIcon("list-add"));
2134 void WiresharkMainWindow::initShowHideMainWidgets()
2136 if (show_hide_actions_
) {
2140 show_hide_actions_
= new QActionGroup(this);
2141 QMap
<QAction
*, QWidget
*> shmw_actions
;
2143 show_hide_actions_
->setExclusive(false);
2144 shmw_actions
[main_ui_
->actionViewMainToolbar
] = main_ui_
->mainToolBar
;
2145 shmw_actions
[main_ui_
->actionViewFilterToolbar
] = main_ui_
->displayFilterToolBar
;
2146 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
2147 shmw_actions
[main_ui_
->actionViewWirelessToolbar
] = main_ui_
->wirelessToolBar
;
2149 shmw_actions
[main_ui_
->actionViewStatusBar
] = main_ui_
->statusBar
;
2150 shmw_actions
[main_ui_
->actionViewPacketList
] = packet_list_
;
2151 shmw_actions
[main_ui_
->actionViewPacketDetails
] = proto_tree_
;
2152 shmw_actions
[main_ui_
->actionViewPacketBytes
] = byte_view_tab_
;
2153 shmw_actions
[main_ui_
->actionViewPacketDiagram
] = packet_diagram_
;
2155 foreach(QAction
*shmwa
, shmw_actions
.keys()) {
2156 shmwa
->setData(QVariant::fromValue(shmw_actions
[shmwa
]));
2157 show_hide_actions_
->addAction(shmwa
);
2160 // Initial hide the Interface Toolbar submenu
2161 main_ui_
->menuInterfaceToolbars
->menuAction()->setVisible(false);
2163 /* Initially hide the additional toolbars menus */
2164 main_ui_
->menuAdditionalToolbars
->menuAction()->setVisible(false);
2166 connect(show_hide_actions_
, &QActionGroup::triggered
, this, &WiresharkMainWindow::showHideMainWidgets
);
2169 void WiresharkMainWindow::initTimeDisplayFormatMenu()
2171 if (time_display_actions_
) {
2175 time_display_actions_
= new QActionGroup(this);
2177 td_actions
[main_ui_
->actionViewTimeDisplayFormatDateYMDandTimeOfDay
] = TS_ABSOLUTE_WITH_YMD
;
2178 td_actions
[main_ui_
->actionViewTimeDisplayFormatDateYDOYandTimeOfDay
] = TS_ABSOLUTE_WITH_YDOY
;
2179 td_actions
[main_ui_
->actionViewTimeDisplayFormatTimeOfDay
] = TS_ABSOLUTE
;
2180 td_actions
[main_ui_
->actionViewTimeDisplayFormatSecondsSinceEpoch
] = TS_EPOCH
;
2181 td_actions
[main_ui_
->actionViewTimeDisplayFormatSecondsSinceFirstCapturedPacket
] = TS_RELATIVE
;
2182 td_actions
[main_ui_
->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket
] = TS_DELTA
;
2183 td_actions
[main_ui_
->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket
] = TS_DELTA_DIS
;
2184 td_actions
[main_ui_
->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay
] = TS_UTC_WITH_YMD
;
2185 td_actions
[main_ui_
->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay
] = TS_UTC_WITH_YDOY
;
2186 td_actions
[main_ui_
->actionViewTimeDisplayFormatUTCTimeOfDay
] = TS_UTC
;
2188 foreach(QAction
* tda
, td_actions
.keys()) {
2189 tda
->setData(QVariant::fromValue(td_actions
[tda
]));
2190 time_display_actions_
->addAction(tda
);
2193 connect(time_display_actions_
, &QActionGroup::triggered
, this, &WiresharkMainWindow::setTimestampFormat
);
2196 void WiresharkMainWindow::initTimePrecisionFormatMenu()
2198 if (time_precision_actions_
) {
2202 time_precision_actions_
= new QActionGroup(this);
2204 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecisionAutomatic
] = TS_PREC_AUTO
;
2205 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecisionSeconds
] = TS_PREC_FIXED_SEC
;
2206 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision100Milliseconds
] = TS_PREC_FIXED_100_MSEC
;
2207 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision10Milliseconds
] = TS_PREC_FIXED_10_MSEC
;
2208 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecisionMilliseconds
] = TS_PREC_FIXED_MSEC
;
2209 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision100Microseconds
] = TS_PREC_FIXED_100_USEC
;
2210 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision10Microseconds
] = TS_PREC_FIXED_10_USEC
;
2211 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecisionMicroseconds
] = TS_PREC_FIXED_USEC
;
2212 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision100Nanoseconds
] = TS_PREC_FIXED_100_NSEC
;
2213 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecision10Nanoseconds
] = TS_PREC_FIXED_10_NSEC
;
2214 tp_actions
[main_ui_
->actionViewTimeDisplayFormatPrecisionNanoseconds
] = TS_PREC_FIXED_NSEC
;
2216 foreach(QAction
* tpa
, tp_actions
.keys()) {
2217 tpa
->setData(QVariant::fromValue(tp_actions
[tpa
]));
2218 time_precision_actions_
->addAction(tpa
);
2221 connect(time_precision_actions_
, &QActionGroup::triggered
, this, &WiresharkMainWindow::setTimestampPrecision
);
2224 // Menu items which will be disabled when we freeze() and whose state will
2225 // be restored when we thaw(). Add to the list as needed.
2226 void WiresharkMainWindow::initFreezeActions()
2228 QList
<QAction
*> freeze_actions
= QList
<QAction
*>()
2229 << main_ui_
->actionFileClose
2230 << main_ui_
->actionViewReload
2231 << main_ui_
->actionEditMarkSelected
2232 << main_ui_
->actionEditMarkAllDisplayed
2233 << main_ui_
->actionEditUnmarkAllDisplayed
2234 << main_ui_
->actionEditIgnoreSelected
2235 << main_ui_
->actionEditIgnoreAllDisplayed
2236 << main_ui_
->actionEditUnignoreAllDisplayed
2237 << main_ui_
->actionEditSetTimeReference
2238 << main_ui_
->actionEditUnsetAllTimeReferences
;
2240 foreach(QAction
*action
, freeze_actions
) {
2241 freeze_actions_
<< QPair
<QAction
*, bool>(action
, false);
2245 void WiresharkMainWindow::initConversationMenus()
2249 QList
<QAction
*> cc_actions
= QList
<QAction
*>()
2250 << main_ui_
->actionViewColorizeConversation1
<< main_ui_
->actionViewColorizeConversation2
2251 << main_ui_
->actionViewColorizeConversation3
<< main_ui_
->actionViewColorizeConversation4
2252 << main_ui_
->actionViewColorizeConversation5
<< main_ui_
->actionViewColorizeConversation6
2253 << main_ui_
->actionViewColorizeConversation7
<< main_ui_
->actionViewColorizeConversation8
2254 << main_ui_
->actionViewColorizeConversation9
<< main_ui_
->actionViewColorizeConversation10
;
2256 packet_list_
->conversationMenu()->clear();
2257 packet_list_
->colorizeMenu()->clear();
2258 proto_tree_
->colorizeMenu()->clear();
2259 main_ui_
->menuConversationFilter
->clear();
2261 for (GList
*conv_filter_list_entry
= packet_conv_filter_list
; conv_filter_list_entry
; conv_filter_list_entry
= gxx_list_next(conv_filter_list_entry
)) {
2263 conversation_filter_t
* conv_filter
= gxx_list_data(conversation_filter_t
*, conv_filter_list_entry
);
2264 ConversationAction
*conv_action
= new ConversationAction(main_ui_
->menuConversationFilter
, conv_filter
);
2265 main_ui_
->menuConversationFilter
->addAction(conv_action
);
2267 connect(this, &WiresharkMainWindow::packetInfoChanged
, conv_action
, &ConversationAction::setPacketInfo
);
2268 connect(conv_action
, &ConversationAction::triggered
, this, &WiresharkMainWindow::applyConversationFilter
, Qt::QueuedConnection
);
2270 // Packet list context menu items
2271 packet_list_
->conversationMenu()->addAction(conv_action
);
2273 QMenu
*submenu
= packet_list_
->colorizeMenu()->addMenu(conv_action
->text());
2276 foreach(QAction
*cc_action
, cc_actions
) {
2277 conv_action
= new ConversationAction(submenu
, conv_filter
);
2278 conv_action
->setText(cc_action
->text());
2279 conv_action
->setIcon(cc_action
->icon());
2280 conv_action
->setColorNumber(i
++);
2281 submenu
->addAction(conv_action
);
2282 connect(this, &WiresharkMainWindow::packetInfoChanged
, conv_action
, &ConversationAction::setPacketInfo
);
2283 connect(conv_action
, &ConversationAction::triggered
, this, &WiresharkMainWindow::colorizeActionTriggered
);
2286 conv_action
= new ConversationAction(submenu
, conv_filter
);
2287 conv_action
->setText(main_ui_
->actionViewColorizeNewColoringRule
->text());
2288 submenu
->addAction(conv_action
);
2289 connect(this, &WiresharkMainWindow::packetInfoChanged
, conv_action
, &ConversationAction::setPacketInfo
);
2290 connect(conv_action
, &ConversationAction::triggered
, this, &WiresharkMainWindow::colorizeActionTriggered
);
2292 // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
2293 // We should probably do that here.
2294 // XXX - Or we should create all the menus in the contextMenuEvents.
2295 // Note that the packet list and proto tree menu items created here are
2296 // not updated automatically on language change. (The main menu items,
2297 // as members of main_ui_ are, in WiresharkMainWindow::changeEvent,)
2301 // Proto tree colorization items
2303 ColorizeAction
*colorize_action
;
2304 foreach(QAction
*cc_action
, cc_actions
) {
2305 colorize_action
= new ColorizeAction(proto_tree_
->colorizeMenu());
2306 colorize_action
->setText(cc_action
->text());
2307 colorize_action
->setIcon(cc_action
->icon());
2308 colorize_action
->setColorNumber(i
++);
2309 proto_tree_
->colorizeMenu()->addAction(colorize_action
);
2310 connect(this, &WiresharkMainWindow::fieldFilterChanged
, colorize_action
, &ColorizeAction::setFieldFilter
);
2311 connect(colorize_action
, &ColorizeAction::triggered
, this, &WiresharkMainWindow::colorizeActionTriggered
);
2314 colorize_action
= new ColorizeAction(proto_tree_
->colorizeMenu());
2315 colorize_action
->setText(main_ui_
->actionViewColorizeNewColoringRule
->text());
2316 proto_tree_
->colorizeMenu()->addAction(colorize_action
);
2317 connect(this, &WiresharkMainWindow::fieldFilterChanged
, colorize_action
, &ColorizeAction::setFieldFilter
);
2318 connect(colorize_action
, &ColorizeAction::triggered
, this, &WiresharkMainWindow::colorizeActionTriggered
);
2321 bool WiresharkMainWindow::addExportObjectsMenuItem(const void *, void *value
, void *userdata
)
2323 register_eo_t
*eo
= (register_eo_t
*)value
;
2324 WiresharkMainWindow
*window
= (WiresharkMainWindow
*)userdata
;
2326 ExportObjectAction
*export_action
= new ExportObjectAction(window
->main_ui_
->menuFileExportObjects
, eo
);
2327 window
->main_ui_
->menuFileExportObjects
->addAction(export_action
);
2329 //initially disable until a file is loaded (then file signals will take over)
2330 export_action
->setEnabled(false);
2332 connect(&window
->capture_file_
, &CaptureFile::captureEvent
, export_action
, &ExportObjectAction::captureFileEvent
);
2333 connect(export_action
, &ExportObjectAction::triggered
, window
, &WiresharkMainWindow::applyExportObject
);
2337 void WiresharkMainWindow::initExportObjectsMenus()
2339 eo_iterate_tables(addExportObjectsMenuItem
, this);
2342 bool WiresharkMainWindow::addFollowStreamMenuItem(const void *key
, void *value
, void *userdata
)
2344 const char *short_name
= (const char*)key
;
2345 register_follow_t
*follow
= (register_follow_t
*)value
;
2346 WiresharkMainWindow
*window
= (WiresharkMainWindow
*)userdata
;
2348 FollowStreamAction
*follow_action
= new FollowStreamAction(window
->main_ui_
->menuFollow
, follow
);
2349 window
->main_ui_
->menuFollow
->addAction(follow_action
);
2351 follow_action
->setEnabled(false);
2353 /* Special features for some of the built in follow types, like
2354 * shortcuts and overriding the name. XXX: Should these go in
2355 * FollowStreamAction, or should some of these (e.g. TCP and UDP)
2356 * be registered in initFollowStreamMenus so that they can be
2357 * on the top of the menu list too?
2359 if (g_strcmp0(short_name
, "TCP") == 0) {
2360 follow_action
->setShortcut(Qt::CTRL
| Qt::ALT
| Qt::SHIFT
| Qt::Key_T
);
2361 } else if (g_strcmp0(short_name
, "UDP") == 0) {
2362 follow_action
->setShortcut(Qt::CTRL
| Qt::ALT
| Qt::SHIFT
| Qt::Key_U
);
2363 } else if (g_strcmp0(short_name
, "DCCP") == 0) {
2364 /* XXX: Not sure this one is widely enough used to need a shortcut. */
2365 follow_action
->setShortcut(Qt::CTRL
| Qt::ALT
| Qt::SHIFT
| Qt::Key_E
);
2366 } else if (g_strcmp0(short_name
, "TLS") == 0) {
2367 follow_action
->setShortcut(Qt::CTRL
| Qt::ALT
| Qt::SHIFT
| Qt::Key_S
);
2368 } else if (g_strcmp0(short_name
, "HTTP") == 0) {
2369 follow_action
->setShortcut(Qt::CTRL
| Qt::ALT
| Qt::SHIFT
| Qt::Key_H
);
2370 } else if (g_strcmp0(short_name
, "HTTP2") == 0) {
2371 follow_action
->setText(tr("HTTP/2 Stream"));
2372 } else if (g_strcmp0(short_name
, "SIP") == 0) {
2373 follow_action
->setText(tr("SIP Call"));
2374 } else if (g_strcmp0(short_name
, "USBCOM") == 0) {
2375 follow_action
->setText(tr("USB CDC Data"));
2378 connect(follow_action
, &QAction::triggered
, window
,
2379 [window
, follow
]() { window
->openFollowStreamDialog(get_follow_proto_id(follow
)); },
2380 Qt::QueuedConnection
);
2384 void WiresharkMainWindow::initFollowStreamMenus()
2386 /* This puts them all in the menus in alphabetical order. */
2387 follow_iterate_followers(addFollowStreamMenuItem
, this);
2391 void WiresharkMainWindow::setTitlebarForCaptureFile()
2393 use_capturing_title_
= false;
2397 QString
WiresharkMainWindow::replaceWindowTitleVariables(QString title
)
2399 title
.replace("%P", get_profile_name());
2400 title
.replace("%V", get_ws_vcs_version_info());
2403 if (global_commandline_info
.capture_comments
) {
2404 // Use the first capture comment from command line.
2405 title
.replace("%C", (char *)g_ptr_array_index(global_commandline_info
.capture_comments
, 0));
2407 // No capture comment.
2414 if (title
.contains("%F")) {
2415 // %F is file path of the capture file.
2416 if (capture_file_
.capFile()) {
2417 // get_dirname() will overwrite the argument so make a copy first
2418 char *filename
= g_strdup(capture_file_
.capFile()->filename
);
2419 QString
file(get_dirname(filename
));
2422 // Substitute HOME with ~
2423 QString
homedir(g_getenv("HOME"));
2424 if (!homedir
.isEmpty()) {
2425 homedir
.remove(QRegularExpression("[/]+$"));
2426 file
.replace(homedir
, "~");
2429 title
.replace("%F", file
);
2431 // No file loaded, no folder name
2436 if (title
.contains("%S")) {
2437 // %S is a conditional separator (" - ") that only shows when surrounded by variables
2438 // with values or static text. Remove repeating, leading and trailing separators.
2439 title
.replace(QRegularExpression("(%S)+"), "%S");
2440 title
.remove(QRegularExpression("^%S|%S$"));
2442 // On macOS we separate with a unicode em dash
2443 title
.replace("%S", " " UTF8_EM_DASH
" ");
2445 title
.replace("%S", " - ");
2452 void WiresharkMainWindow::setWSWindowTitle(QString title
)
2454 if (title
.isEmpty()) {
2455 title
= tr("The Wireshark Network Analyzer");
2458 if (prefs
.gui_prepend_window_title
&& prefs
.gui_prepend_window_title
[0]) {
2459 QString custom_title
= replaceWindowTitleVariables(prefs
.gui_prepend_window_title
);
2460 if (custom_title
.length() > 0) {
2461 title
.prepend(QStringLiteral("[%1] ").arg(custom_title
));
2465 if (prefs
.gui_window_title
&& prefs
.gui_window_title
[0]) {
2466 QString custom_title
= replaceWindowTitleVariables(prefs
.gui_window_title
);
2467 if (custom_title
.length() > 0) {
2469 // On macOS we separate the titles with a unicode em dash
2470 title
.append(QStringLiteral(" %1 %2").arg(UTF8_EM_DASH
).arg(custom_title
));
2472 title
.append(QStringLiteral(" [%1]").arg(custom_title
));
2477 setWindowTitle(title
);
2478 setWindowFilePath(NULL
);
2481 void WiresharkMainWindow::setTitlebarForCaptureInProgress()
2483 use_capturing_title_
= true;
2487 void WiresharkMainWindow::updateTitlebar()
2489 if (use_capturing_title_
&& capture_file_
.capFile()) {
2490 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_
.capFile())));
2491 } else if (capture_file_
.capFile() && capture_file_
.capFile()->filename
) {
2492 setWSWindowTitle(QStringLiteral("[*]%1").arg(capture_file_
.fileDisplayName()));
2494 // XXX - on non-Mac platforms, put in the application
2495 // name? Or do so only for temporary files?
2497 if (!capture_file_
.capFile()->is_tempfile
) {
2499 // Set the file path; that way, for macOS, it'll set the
2502 setWindowFilePath(capture_file_
.filePath());
2504 setWindowModified(cf_has_unsaved_data(capture_file_
.capFile()));
2506 /* We have no capture file. */
2513 /* Enable or disable menu items based on whether you have a capture file
2514 you've finished reading and, if you have one, whether it's been saved
2515 and whether it could be saved except by copying the raw packet data. */
2516 void WiresharkMainWindow::setMenusForCaptureFile(bool force_disable
)
2519 bool can_write
= false;
2520 bool can_save
= false;
2521 bool can_save_as
= false;
2523 if (force_disable
|| capture_file_
.capFile() == NULL
|| capture_file_
.capFile()->state
== FILE_READ_IN_PROGRESS
|| capture_file_
.capFile()->state
== FILE_READ_PENDING
) {
2524 /* We have no capture file or we're currently reading a file */
2527 /* We have a capture file. Can we write or save? */
2528 can_write
= cf_can_write_with_wiretap(capture_file_
.capFile());
2529 can_save
= cf_can_save(capture_file_
.capFile());
2530 can_save_as
= cf_can_save_as(capture_file_
.capFile());
2533 main_ui_
->actionViewReload_as_File_Format_or_Capture
->setEnabled(enable
);
2534 main_ui_
->actionFileMerge
->setEnabled(can_write
);
2535 main_ui_
->actionFileClose
->setEnabled(enable
);
2536 main_ui_
->actionFileSave
->setEnabled(can_save
);
2537 main_ui_
->actionFileSaveAs
->setEnabled(can_save_as
);
2538 main_ui_
->actionStatisticsCaptureFileProperties
->setEnabled(enable
);
2539 /* The Protocol Hierarchy statistics run on all the packets that
2540 * pass the current filter, don't enable if a read or rescan is
2541 * still in progress.
2543 main_ui_
->actionStatisticsProtocolHierarchy
->setEnabled(enable
);
2545 * "Export Specified Packets..." should be available only if
2546 * we can write the file out in at least one format.
2548 main_ui_
->actionFileExportPackets
->setEnabled(can_write
);
2550 main_ui_
->actionFileExportAsCArrays
->setEnabled(enable
);
2551 main_ui_
->actionFileExportAsCSV
->setEnabled(enable
);
2552 main_ui_
->actionFileExportAsPDML
->setEnabled(enable
);
2553 main_ui_
->actionFileExportAsPlainText
->setEnabled(enable
);
2554 main_ui_
->actionFileExportAsPSML
->setEnabled(enable
);
2555 main_ui_
->actionFileExportAsJSON
->setEnabled(enable
);
2557 main_ui_
->actionFileExportPDU
->setEnabled(enable
);
2558 main_ui_
->actionFileStripHeaders
->setEnabled(enable
);
2559 /* XXX: "Export TLS Session Keys..." should be enabled only if
2560 * ssl_session_key_count() > 0.
2562 main_ui_
->actionFileExportTLSSessionKeys
->setEnabled(enable
);
2564 foreach(QAction
*eo_action
, main_ui_
->menuFileExportObjects
->actions()) {
2565 eo_action
->setEnabled(enable
);
2568 main_ui_
->actionViewReload
->setEnabled(enable
);
2570 #ifdef HAVE_SOFTWARE_UPDATE
2571 // We might want to enable or disable automatic checks here as well.
2572 update_action_
->setEnabled(!can_save
);
2576 void WiresharkMainWindow::setMenusForCaptureInProgress(bool capture_in_progress
) {
2577 /* Either a capture was started or stopped; in either case, it's not
2578 in the process of stopping, so allow quitting. */
2580 main_ui_
->actionFileOpen
->setEnabled(!capture_in_progress
);
2581 main_ui_
->menuOpenRecentCaptureFile
->setEnabled(!capture_in_progress
);
2583 main_ui_
->actionFileExportAsCArrays
->setEnabled(capture_in_progress
);
2584 main_ui_
->actionFileExportAsCSV
->setEnabled(capture_in_progress
);
2585 main_ui_
->actionFileExportAsPDML
->setEnabled(capture_in_progress
);
2586 main_ui_
->actionFileExportAsPlainText
->setEnabled(capture_in_progress
);
2587 main_ui_
->actionFileExportAsPSML
->setEnabled(capture_in_progress
);
2588 main_ui_
->actionFileExportAsJSON
->setEnabled(capture_in_progress
);
2590 main_ui_
->actionFileExportPDU
->setEnabled(!capture_in_progress
);
2591 main_ui_
->actionFileStripHeaders
->setEnabled(!capture_in_progress
);
2592 main_ui_
->actionFileExportTLSSessionKeys
->setEnabled(capture_in_progress
);
2594 foreach(QAction
*eo_action
, main_ui_
->menuFileExportObjects
->actions()) {
2595 eo_action
->setEnabled(capture_in_progress
);
2598 main_ui_
->menuFileSet
->setEnabled(!capture_in_progress
);
2599 main_ui_
->actionFileQuit
->setEnabled(true);
2600 #ifdef HAVE_SOFTWARE_UPDATE
2601 // We might want to enable or disable automatic checks here as well.
2602 update_action_
->setEnabled(!capture_in_progress
);
2605 main_ui_
->actionStatisticsCaptureFileProperties
->setEnabled(capture_in_progress
);
2607 // XXX Fix packet list heading menu sensitivity
2608 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2609 // !capture_in_progress);
2610 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2611 // !capture_in_progress);
2612 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2613 // !capture_in_progress);
2616 main_ui_
->actionCaptureOptions
->setEnabled(!capture_in_progress
);
2617 main_ui_
->actionCaptureStart
->setEnabled(!capture_in_progress
);
2618 main_ui_
->actionCaptureStart
->setChecked(capture_in_progress
);
2619 main_ui_
->actionCaptureStop
->setEnabled(capture_in_progress
);
2620 main_ui_
->actionCaptureRestart
->setEnabled(capture_in_progress
);
2621 main_ui_
->actionCaptureRefreshInterfaces
->setEnabled(!capture_in_progress
);
2622 #endif /* HAVE_LIBPCAP */
2626 void WiresharkMainWindow::setMenusForCaptureStopping() {
2627 main_ui_
->actionFileQuit
->setEnabled(false);
2628 #ifdef HAVE_SOFTWARE_UPDATE
2629 update_action_
->setEnabled(false);
2631 main_ui_
->actionStatisticsCaptureFileProperties
->setEnabled(false);
2633 main_ui_
->actionCaptureStart
->setChecked(false);
2634 main_ui_
->actionCaptureStop
->setEnabled(false);
2635 main_ui_
->actionCaptureRestart
->setEnabled(false);
2636 #endif /* HAVE_LIBPCAP */
2639 void WiresharkMainWindow::setForCapturedPackets(bool have_captured_packets
)
2641 main_ui_
->actionFilePrint
->setEnabled(have_captured_packets
);
2643 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2644 // have_captured_packets);
2646 main_ui_
->actionEditFindPacket
->setEnabled(have_captured_packets
);
2647 main_ui_
->actionEditFindNext
->setEnabled(have_captured_packets
);
2648 main_ui_
->actionEditFindPrevious
->setEnabled(have_captured_packets
);
2650 main_ui_
->actionGoGoToPacket
->setEnabled(have_captured_packets
);
2651 main_ui_
->actionGoPreviousPacket
->setEnabled(have_captured_packets
);
2652 main_ui_
->actionGoNextPacket
->setEnabled(have_captured_packets
);
2653 main_ui_
->actionGoFirstPacket
->setEnabled(have_captured_packets
);
2654 main_ui_
->actionGoLastPacket
->setEnabled(have_captured_packets
);
2655 main_ui_
->actionGoNextConversationPacket
->setEnabled(have_captured_packets
);
2656 main_ui_
->actionGoPreviousConversationPacket
->setEnabled(have_captured_packets
);
2658 main_ui_
->actionViewZoomIn
->setEnabled(have_captured_packets
);
2659 main_ui_
->actionViewZoomOut
->setEnabled(have_captured_packets
);
2660 main_ui_
->actionViewNormalSize
->setEnabled(have_captured_packets
);
2661 main_ui_
->actionViewResizeColumns
->setEnabled(have_captured_packets
);
2663 main_ui_
->actionStatisticsCaptureFileProperties
->setEnabled(have_captured_packets
);
2664 main_ui_
->actionStatisticsProtocolHierarchy
->setEnabled(have_captured_packets
);
2665 main_ui_
->actionStatisticsIOGraph
->setEnabled(have_captured_packets
);
2668 void WiresharkMainWindow::setMenusForFileSet(bool enable_list_files
) {
2669 bool enable_next
= fileset_get_next() != NULL
&& enable_list_files
;
2670 bool enable_prev
= fileset_get_previous() != NULL
&& enable_list_files
;
2672 main_ui_
->actionFileSetListFiles
->setEnabled(enable_list_files
);
2673 main_ui_
->actionFileSetNextFile
->setEnabled(enable_next
);
2674 main_ui_
->actionFileSetPreviousFile
->setEnabled(enable_prev
);
2677 void WiresharkMainWindow::setWindowIcon(const QIcon
&icon
) {
2678 mainApp
->setWindowIcon(icon
);
2679 QMainWindow::setWindowIcon(icon
);
2682 void WiresharkMainWindow::updateForUnsavedChanges() {
2684 setMenusForCaptureFile();
2687 void WiresharkMainWindow::changeEvent(QEvent
* event
)
2691 switch (event
->type())
2693 case QEvent::LanguageChange
:
2694 main_ui_
->retranslateUi(this);
2695 // make sure that the "Clear Menu" item is retranslated
2696 mainApp
->emitAppSignal(WiresharkApplication::RecentCapturesChanged
);
2697 // make sure that the color actions in the PacketList and ProtoTree
2699 initConversationMenus();
2702 case QEvent::LocaleChange
: {
2703 QString locale
= QLocale::system().name();
2704 locale
.truncate(locale
.lastIndexOf('_'));
2705 mainApp
->loadLanguage(locale
);
2708 case QEvent::WindowStateChange
:
2709 main_ui_
->actionViewFullScreen
->setChecked(this->isFullScreen());
2715 QMainWindow::changeEvent(event
);
2718 /* Update main window items based on whether there's a capture in progress. */
2719 void WiresharkMainWindow::setForCaptureInProgress(bool capture_in_progress
, bool handle_toolbars
, GArray
*ifaces
)
2721 setMenusForCaptureInProgress(capture_in_progress
);
2723 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
2724 wireless_frame_
->setCaptureInProgress(capture_in_progress
);
2728 packet_list_
->setCaptureInProgress(capture_in_progress
, main_ui_
->actionGoAutoScroll
->isChecked());
2730 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2733 if (handle_toolbars
) {
2734 QList
<InterfaceToolbar
*> toolbars
= findChildren
<InterfaceToolbar
*>();
2735 foreach(InterfaceToolbar
*toolbar
, toolbars
) {
2736 if (capture_in_progress
) {
2737 toolbar
->startCapture(ifaces
);
2739 toolbar
->stopCapture();
2745 void WiresharkMainWindow::addMenuActions(QList
<QAction
*> &actions
, int menu_group
)
2747 foreach(QAction
*action
, actions
) {
2748 switch (menu_group
) {
2749 case REGISTER_PACKET_ANALYZE_GROUP_UNSORTED
:
2750 case REGISTER_PACKET_STAT_GROUP_UNSORTED
:
2751 case REGISTER_STAT_GROUP_GENERIC
:
2752 // XXX - The Lua documentation claims that ANALYZE_GROUP_UNSORTED
2753 // is under the Analyze menu, and STAT_GROUP_GENERIC and
2754 // PACKET_STAT_GROUP_UNSORTED are distinguished by whether they
2755 // go before the separator in the group of non protocol-specific
2756 // actions or after the separator with the protocol-specific
2757 // actions. We currently put them all in the same place.
2758 main_ui_
->menuStatistics
->insertAction(
2759 main_ui_
->actionStatistics_REGISTER_STAT_GROUP_UNSORTED
,
2762 case REGISTER_STAT_GROUP_RESPONSE_TIME
:
2763 main_ui_
->menuServiceResponseTime
->addAction(action
);
2765 case REGISTER_STAT_GROUP_RSERPOOL
:
2766 main_ui_
->menuRSerPool
->addAction(action
);
2768 case REGISTER_TELEPHONY_GROUP_UNSORTED
:
2769 main_ui_
->menuTelephony
->addAction(action
);
2771 case REGISTER_TELEPHONY_GROUP_ANSI
:
2772 main_ui_
->menuANSI
->addAction(action
);
2774 case REGISTER_TELEPHONY_GROUP_GSM
:
2775 main_ui_
->menuGSM
->addAction(action
);
2777 case REGISTER_TELEPHONY_GROUP_3GPP_UU
:
2778 main_ui_
->menuLTE
->addAction(action
);
2780 case REGISTER_TELEPHONY_GROUP_MTP3
:
2781 main_ui_
->menuMTP3
->addAction(action
);
2783 case REGISTER_TELEPHONY_GROUP_SCTP
:
2784 // XXX - There are two SCTP menus, under Analyze and Telephony,
2785 // that have the same default actions. The default actions from
2786 // Analyze are copied to the PacketList context menu.
2787 main_ui_
->menuTelephonySCTP
->addAction(action
);
2789 case REGISTER_TOOLS_GROUP_UNSORTED
:
2791 // Allow the creation of submenus. Mimics the behavior of
2792 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2793 // and GtkUIManager.
2795 // For now we limit the insanity to the "Tools" menu.
2796 QStringList menu_path
= action
->text().split('/');
2797 QMenu
*cur_menu
= main_ui_
->menuTools
;
2798 while (menu_path
.length() > 1) {
2799 QString menu_title
= menu_path
.takeFirst();
2800 QMenu
*submenu
= cur_menu
->findChild
<QMenu
*>(menu_title
.toLower(), Qt::FindDirectChildrenOnly
);
2802 submenu
= cur_menu
->addMenu(menu_title
);
2803 submenu
->setObjectName(menu_title
.toLower());
2807 action
->setText(menu_path
.last());
2808 cur_menu
->addAction(action
);
2816 // Connect each action type to its corresponding slot. We to
2817 // distinguish various types of actions. Setting their objectName
2818 // seems to work OK.
2819 if (action
->objectName() == TapParameterDialog::actionName()) {
2820 connect(action
, &QAction::triggered
, this, [=]() { openTapParameterDialog(); });
2821 } else if (action
->objectName() == FunnelStatistics::actionName()) {
2822 connect(action
, &QAction::triggered
, funnel_statistics_
, &FunnelStatistics::funnelActionTriggered
);
2826 void WiresharkMainWindow::removeMenuActions(QList
<QAction
*> &actions
, int menu_group
)
2828 foreach(QAction
*action
, actions
) {
2829 switch (menu_group
) {
2830 case REGISTER_PACKET_ANALYZE_GROUP_UNSORTED
:
2831 case REGISTER_PACKET_STAT_GROUP_UNSORTED
:
2832 case REGISTER_STAT_GROUP_GENERIC
:
2833 main_ui_
->menuStatistics
->removeAction(action
);
2835 case REGISTER_STAT_GROUP_RESPONSE_TIME
:
2836 main_ui_
->menuServiceResponseTime
->removeAction(action
);
2838 case REGISTER_STAT_GROUP_RSERPOOL
:
2839 main_ui_
->menuRSerPool
->removeAction(action
);
2841 case REGISTER_TELEPHONY_GROUP_UNSORTED
:
2842 main_ui_
->menuTelephony
->removeAction(action
);
2844 case REGISTER_TELEPHONY_GROUP_ANSI
:
2845 main_ui_
->menuANSI
->removeAction(action
);
2847 case REGISTER_TELEPHONY_GROUP_GSM
:
2848 main_ui_
->menuGSM
->removeAction(action
);
2850 case REGISTER_TELEPHONY_GROUP_3GPP_UU
:
2851 main_ui_
->menuLTE
->removeAction(action
);
2853 case REGISTER_TELEPHONY_GROUP_MTP3
:
2854 main_ui_
->menuMTP3
->removeAction(action
);
2856 case REGISTER_TELEPHONY_GROUP_SCTP
:
2857 main_ui_
->menuTelephonySCTP
->removeAction(action
);
2859 case REGISTER_TOOLS_GROUP_UNSORTED
:
2861 // Allow removal of submenus.
2862 // For now we limit the insanity to the "Tools" menu.
2863 QStringList menu_path
= action
->text().split('/');
2864 QMenu
*cur_menu
= main_ui_
->menuTools
;
2865 while (menu_path
.length() > 1) {
2866 QString menu_title
= menu_path
.takeFirst();
2867 QMenu
*submenu
= cur_menu
->findChild
<QMenu
*>(menu_title
.toLower(), Qt::FindDirectChildrenOnly
);
2870 cur_menu
->removeAction(action
);
2871 // Remove empty submenus.
2872 while (cur_menu
!= main_ui_
->menuTools
) {
2873 QMenu
*empty_menu
= (cur_menu
->isEmpty() ? cur_menu
: NULL
);
2874 cur_menu
= dynamic_cast<QMenu
*>(cur_menu
->parent());
2880 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2886 void WiresharkMainWindow::addDynamicMenus()
2889 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_GSM
, main_ui_
->actionTelephonyGsmMapSummary
);
2890 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_3GPP_UU
, main_ui_
->actionTelephonyLteMacStatistics
);
2891 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_3GPP_UU
, main_ui_
->actionTelephonyLteRlcStatistics
);
2892 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_3GPP_UU
, main_ui_
->actionTelephonyLteRlcGraph
);
2893 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_MTP3
, main_ui_
->actionTelephonyMtp3Summary
);
2894 mainApp
->addDynamicMenuGroupItem(REGISTER_TELEPHONY_GROUP_UNSORTED
, main_ui_
->actionTelephonySipFlows
);
2896 // Fill in each menu
2897 foreach(register_stat_group_t menu_group
, menu_groups_
) {
2898 QList
<QAction
*>actions
= mainApp
->dynamicMenuGroupItems(menu_group
);
2899 addMenuActions(actions
, menu_group
);
2902 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2903 // We've added a placeholder in order to make sure some menus are visible.
2904 // Hide them as needed.
2905 if (mainApp
->dynamicMenuGroupItems(REGISTER_TELEPHONY_GROUP_ANSI
).length() > 0) {
2906 main_ui_
->actionTelephonyANSIPlaceholder
->setVisible(false);
2908 if (mainApp
->dynamicMenuGroupItems(REGISTER_TELEPHONY_GROUP_GSM
).length() > 0) {
2909 main_ui_
->actionTelephonyGSMPlaceholder
->setVisible(false);
2911 if (mainApp
->dynamicMenuGroupItems(REGISTER_TELEPHONY_GROUP_3GPP_UU
).length() > 0) {
2912 main_ui_
->actionTelephonyLTEPlaceholder
->setVisible(false);
2914 if (mainApp
->dynamicMenuGroupItems(REGISTER_TELEPHONY_GROUP_MTP3
).length() > 0) {
2915 main_ui_
->actionTelephonyMTP3Placeholder
->setVisible(false);
2919 void WiresharkMainWindow::reloadDynamicMenus()
2921 foreach(register_stat_group_t menu_group
, menu_groups_
) {
2922 QList
<QAction
*>actions
= mainApp
->removedMenuGroupItems(menu_group
);
2923 removeMenuActions(actions
, menu_group
);
2925 actions
= mainApp
->addedMenuGroupItems(menu_group
);
2926 addMenuActions(actions
, menu_group
);
2929 mainApp
->clearAddedMenuGroupItems();
2930 mainApp
->clearRemovedMenuGroupItems();
2933 void WiresharkMainWindow::externalMenuHelper(ext_menu_t
* menu
, QMenu
* subMenu
, int depth
)
2935 QAction
* itemAction
= Q_NULLPTR
;
2936 ext_menubar_t
* item
= Q_NULLPTR
;
2937 GList
* children
= Q_NULLPTR
;
2939 /* There must exists an xpath parent */
2940 Q_ASSERT(subMenu
!= NULL
);
2942 /* If the depth counter exceeds, something must have gone wrong */
2943 Q_ASSERT(depth
< EXT_MENUBAR_MAX_DEPTH
);
2945 children
= menu
->children
;
2946 /* Iterate the child entries */
2947 while (children
&& children
->data
) {
2948 item
= gxx_list_data(ext_menubar_t
*, children
);
2950 if (item
->type
== EXT_MENUBAR_MENU
) {
2951 /* Handle Submenu entry */
2952 this->externalMenuHelper(item
, subMenu
->addMenu(item
->label
), depth
++);
2953 } else if (item
->type
== EXT_MENUBAR_SEPARATOR
) {
2954 subMenu
->addSeparator();
2955 } else if (item
->type
== EXT_MENUBAR_ITEM
|| item
->type
== EXT_MENUBAR_URL
) {
2956 itemAction
= subMenu
->addAction(item
->name
);
2957 itemAction
->setData(QVariant::fromValue(static_cast<void *>(item
)));
2958 itemAction
->setText(item
->label
);
2959 connect(itemAction
, &QAction::triggered
, this, &WiresharkMainWindow::externalMenuItemTriggered
);
2963 children
= gxx_list_next(children
);
2967 QMenu
* WiresharkMainWindow::searchSubMenu(QString objectName
)
2971 if (objectName
.length() > 0) {
2972 QString searchName
= QStringLiteral("menu") + objectName
;
2974 lst
= main_ui_
->menuBar
->findChildren
<QMenu
*>();
2975 foreach(QMenu
* m
, lst
) {
2976 if (QString::compare(m
->objectName(), searchName
) == 0)
2984 void WiresharkMainWindow::addPluginIFStructures()
2986 GList
*user_menu
= ext_menubar_get_entries();
2988 while (user_menu
&& user_menu
->data
) {
2989 QMenu
*subMenu
= Q_NULLPTR
;
2990 ext_menu_t
*menu
= gxx_list_data(ext_menu_t
*, user_menu
);
2992 /* On this level only menu items should exist. Not doing an assert here,
2993 * as it could be an honest mistake */
2994 if (menu
->type
!= EXT_MENUBAR_MENU
) {
2995 user_menu
= gxx_list_next(user_menu
);
2999 /* Create main submenu and add it to the menubar */
3000 if (menu
->parent_menu
) {
3001 QMenu
*sortUnderneath
= searchSubMenu(QString(menu
->parent_menu
));
3003 subMenu
= sortUnderneath
->addMenu(menu
->label
);
3007 subMenu
= main_ui_
->menuBar
->addMenu(menu
->label
);
3009 /* This will generate the action structure for each menu. It is recursive,
3010 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
3011 this->externalMenuHelper(menu
, subMenu
, 0);
3014 user_menu
= gxx_list_next(user_menu
);
3017 int cntToolbars
= 0;
3019 QMenu
*tbMenu
= main_ui_
->menuAdditionalToolbars
;
3020 GList
*if_toolbars
= ext_toolbar_get_entries();
3021 while (if_toolbars
&& if_toolbars
->data
) {
3022 ext_toolbar_t
*toolbar
= gxx_list_data(ext_toolbar_t
*, if_toolbars
);
3024 if (toolbar
->type
!= EXT_TOOLBAR_BAR
) {
3025 if_toolbars
= gxx_list_next(if_toolbars
);
3029 bool visible
= g_list_find_custom(recent
.gui_additional_toolbars
, toolbar
->name
, reinterpret_cast<GCompareFunc
>(strcmp
)) ? true : false;
3031 AdditionalToolBar
*ifToolBar
= AdditionalToolBar::create(this, toolbar
);
3034 ifToolBar
->setVisible(visible
);
3036 QAction
*iftbAction
= new QAction(QString(toolbar
->name
), this);
3037 iftbAction
->setToolTip(toolbar
->tooltip
);
3038 iftbAction
->setEnabled(true);
3039 iftbAction
->setCheckable(true);
3040 iftbAction
->setChecked(visible
);
3041 iftbAction
->setToolTip(tr("Show or hide the toolbar"));
3042 iftbAction
->setData(VariantPointer
<ext_toolbar_t
>::asQVariant(toolbar
));
3044 QAction
*before
= Q_NULLPTR
;
3046 foreach(QAction
*action
, tbMenu
->actions()) {
3047 /* Ensure we add the menu entries in sorted order */
3048 if (action
->text().compare(toolbar
->name
, Qt::CaseInsensitive
) > 0) {
3054 tbMenu
->insertAction(before
, iftbAction
);
3056 addToolBar(Qt::TopToolBarArea
, ifToolBar
);
3057 insertToolBarBreak(ifToolBar
);
3059 if (show_hide_actions_
)
3060 show_hide_actions_
->addAction(iftbAction
);
3065 if_toolbars
= gxx_list_next(if_toolbars
);
3069 tbMenu
->menuAction()->setVisible(true);
3072 void WiresharkMainWindow::removeAdditionalToolbar(QString toolbarName
)
3074 if (toolbarName
.length() == 0)
3077 QList
<QToolBar
*> toolbars
= findChildren
<QToolBar
*>();
3078 foreach(QToolBar
*tb
, toolbars
) {
3079 AdditionalToolBar
*ifToolBar
= dynamic_cast<AdditionalToolBar
*>(tb
);
3081 if (ifToolBar
&& ifToolBar
->menuName().compare(toolbarName
)) {
3082 GList
*entry
= g_list_find_custom(recent
.gui_additional_toolbars
, qUtf8Printable(ifToolBar
->menuName()), reinterpret_cast<GCompareFunc
>(strcmp
));
3084 recent
.gui_additional_toolbars
= g_list_remove(recent
.gui_additional_toolbars
, entry
->data
);
3086 QList
<QAction
*> actions
= main_ui_
->menuAdditionalToolbars
->actions();
3087 foreach(QAction
*action
, actions
) {
3088 ext_toolbar_t
*item
= VariantPointer
<ext_toolbar_t
>::asPtr(action
->data());
3089 if (item
&& ifToolBar
->menuName().compare(item
->name
)) {
3090 if (show_hide_actions_
)
3091 show_hide_actions_
->removeAction(action
);
3092 main_ui_
->menuAdditionalToolbars
->removeAction(action
);
3101 QString
WiresharkMainWindow::getMwFileName()
3106 void WiresharkMainWindow::setMwFileName(QString fileName
)
3108 mwFileName_
= fileName
;
3112 // Finds rtp id for selected stream and adds it to stream_ids
3113 // If reverse is set, tries to find reverse stream and other streams
3114 // bundled on the same RTP session too
3115 // Return error string if error happens
3117 // Note: Caller must free each returned rtpstream_info_t
3118 QString
WiresharkMainWindow::findRtpStreams(QVector
<rtpstream_id_t
*> *stream_ids
, bool reverse
)
3120 rtpstream_tapinfo_t tapinfo
;
3121 rtpstream_id_t
*new_id
;
3122 const char filter_text
[] = "rtp && rtp.version == 2 && rtp.ssrc && (ip || ipv6)";
3124 df_error_t
*df_err
= NULL
;
3126 /* Try to get the hfid for "rtp.ssrc". */
3127 int hfid_rtp_ssrc
= proto_registrar_get_id_byname("rtp.ssrc");
3128 if (hfid_rtp_ssrc
== -1) {
3129 return tr("There is no \"rtp.ssrc\" field in this version of Wireshark.");
3132 /* Try to compile the filter. */
3133 if (!dfilter_compile(filter_text
, &sfcode
, &df_err
)) {
3134 QString err
= QString(df_err
->msg
);
3135 df_error_free(&df_err
);
3139 if (!capture_file_
.capFile() || !capture_file_
.capFile()->current_frame
) close();
3141 if (!cf_read_current_record(capture_file_
.capFile())) close();
3143 frame_data
*fdata
= capture_file_
.capFile()->current_frame
;
3147 epan_dissect_init(&edt
, capture_file_
.capFile()->epan
, true, false);
3148 epan_dissect_prime_with_dfilter(&edt
, sfcode
);
3149 epan_dissect_prime_with_hfid(&edt
, hfid_rtp_ssrc
);
3150 epan_dissect_run(&edt
, capture_file_
.capFile()->cd_t
,
3151 &capture_file_
.capFile()->rec
,
3152 frame_tvbuff_new_buffer(
3153 &capture_file_
.capFile()->provider
, fdata
,
3154 &capture_file_
.capFile()->buf
),
3158 * Packet must be an RTPv2 packet with an SSRC; we use the filter to
3161 if (!dfilter_apply_edt(sfcode
, &edt
)) {
3162 epan_dissect_cleanup(&edt
);
3163 dfilter_free(sfcode
);
3164 return tr("Please select an RTPv2 packet with an SSRC value");
3167 dfilter_free(sfcode
);
3170 // If we only want streams that match the SSRC in this frame, we
3171 // can just allocate an RTP stream ID directly instead of having
3172 // to redissect all the other packets.
3174 /* We need the SSRC value of the current frame; try to get it. */
3175 GPtrArray
*gp
= proto_get_finfo_ptr_array(edt
.tree
, hfid_rtp_ssrc
);
3176 if (gp
== NULL
|| gp
->len
== 0) {
3177 /* XXX - should not happen, as the filter includes rtp.ssrc */
3178 epan_dissect_cleanup(&edt
);
3179 return tr("SSRC value not found.");
3183 * OK, we have the SSRC value(s), so we can proceed.
3184 * (Try to handle the unlikely case of a frame with more than one
3185 * SSRC; perhaps a DVD-S2 Baseband frame? Does that even work
3188 for (unsigned i
= 0; i
< gp
->len
; i
++) {
3189 new_id
= g_new0(rtpstream_id_t
, 1);
3190 rtpstream_id_copy_pinfo(&(edt
.pi
), new_id
, false);
3191 new_id
->ssrc
= fvalue_get_uinteger(((field_info
*)gp
->pdata
[i
])->value
);
3192 *stream_ids
<< new_id
;
3195 // If we want to find all SSRCs with the same RTP session as this
3196 // frame, then we have to redissect all packets.
3198 /* Register the tap listener */
3199 memset(&tapinfo
, 0, sizeof(rtpstream_tapinfo_t
));
3200 tapinfo
.tap_data
= this;
3201 tapinfo
.mode
= TAP_ANALYSE
;
3203 /* Scan for RTP streams (redissect all packets) */
3204 rtpstream_scan(&tapinfo
, capture_file_
.capFile(), Q_NULLPTR
);
3206 for (GList
*strinfo_list
= g_list_first(tapinfo
.strinfo_list
); strinfo_list
; strinfo_list
= gxx_list_next(strinfo_list
)) {
3207 rtpstream_info_t
* strinfo
= gxx_list_data(rtpstream_info_t
*, strinfo_list
);
3208 // We want any RTP stream ID that matches the address and ports.
3209 // This could mean more than one in the forward direction, if
3210 // e.g., BUNDLE is used (RFC 9143).
3211 if (rtpstream_id_equal_pinfo(&(strinfo
->id
), &(edt
.pi
), false) ||
3212 rtpstream_id_equal_pinfo(&(strinfo
->id
), &(edt
.pi
), true)) {
3213 new_id
= g_new0(rtpstream_id_t
, 1);
3214 rtpstream_id_copy(&(strinfo
->id
), new_id
);
3215 *stream_ids
<< new_id
;
3218 rtpstream_reset_cb(&tapinfo
);
3221 epan_dissect_cleanup(&edt
);
3226 void WiresharkMainWindow::openTLSKeylogDialog()
3228 // Have a single instance of the dialog at any one time.
3229 if (!tlskeylog_dialog_
) {
3230 tlskeylog_dialog_
= new TLSKeylogDialog(*this);
3231 tlskeylog_dialog_
->setAttribute(Qt::WA_DeleteOnClose
);
3234 if (tlskeylog_dialog_
->isMinimized()) {
3235 tlskeylog_dialog_
->showNormal();
3238 tlskeylog_dialog_
->show();
3240 tlskeylog_dialog_
->raise();
3241 tlskeylog_dialog_
->activateWindow();