1 /* main_window.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include "main_window.h"
11 
12 /*
13  * The generated Ui_MainWindow::setupUi() can grow larger than our configured limit,
14  * so turn off -Wframe-larger-than= for ui_main_window.h.
15  */
16 DIAG_OFF(frame-larger-than=)
17 #include <ui_main_window.h>
18 DIAG_ON(frame-larger-than=)
19 
20 #include <epan/addr_resolv.h>
21 #include "epan/conversation_filter.h"
22 #include <epan/epan_dissect.h>
23 #include <wsutil/filesystem.h>
24 #include <wsutil/wslog.h>
25 #include <wsutil/ws_assert.h>
26 #include <ui/version_info.h>
27 #include <epan/prefs.h>
28 #include <epan/stats_tree_priv.h>
29 #include <epan/plugin_if.h>
30 #include <epan/export_object.h>
31 #include <frame_tvbuff.h>
32 
33 #include "ui/iface_toolbar.h"
34 
35 #ifdef HAVE_LIBPCAP
36 #include "ui/capture.h"
37 #include <capture/capture_session.h>
38 #endif
39 
40 #include "ui/alert_box.h"
41 #ifdef HAVE_LIBPCAP
42 #include "ui/capture_ui_utils.h"
43 #endif
44 #include "ui/capture_globals.h"
45 #include "ui/main_statusbar.h"
46 #include "ui/recent.h"
47 #include "ui/recent_utils.h"
48 #include "ui/util.h"
49 #include "ui/preference_utils.h"
50 
51 #include "byte_view_tab.h"
52 #ifdef HAVE_LIBPCAP
53 #include "capture_options_dialog.h"
54 #endif
55 #include "conversation_colorize_action.h"
56 #include "export_dissection_dialog.h"
57 #include "export_object_action.h"
58 #include "file_set_dialog.h"
59 #include "filter_dialog.h"
60 #include "funnel_statistics.h"
61 #include "import_text_dialog.h"
62 #include "interface_toolbar.h"
63 #include "packet_diagram.h"
64 #include "packet_list.h"
65 #include "proto_tree.h"
66 #include "simple_dialog.h"
67 #include "tap_parameter_dialog.h"
68 #include "wireless_frame.h"
69 #include <ui/qt/widgets/wireless_timeline.h>
70 #include "wireshark_application.h"
71 
72 #include <ui/qt/widgets/additional_toolbar.h>
73 #include <ui/qt/widgets/display_filter_edit.h>
74 #include <ui/qt/widgets/filter_expression_toolbar.h>
75 
76 #include <ui/qt/utils/color_utils.h>
77 #include <ui/qt/utils/qt_ui_utils.h>
78 #include <ui/qt/utils/stock_icon.h>
79 #include <ui/qt/utils/variant_pointer.h>
80 
81 #include <QAction>
82 #include <QActionGroup>
83 #include <QDesktopWidget>
84 #include <QKeyEvent>
85 #include <QList>
86 #include <QMessageBox>
87 #include <QMetaObject>
88 #include <QMimeData>
89 #include <QTabWidget>
90 #include <QTextCodec>
91 #include <QToolButton>
92 #include <QTreeWidget>
93 #include <QUrl>
94 
95 //menu_recent_file_write_all
96 
97 // If we ever add support for multiple windows this will need to be replaced.
98 static MainWindow *gbl_cur_main_window_ = NULL;
99 
pipe_input_set_handler(gint source,gpointer user_data,ws_process_id * child_process,pipe_input_cb_t input_cb)100 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
101 {
102     gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
103 }
104 
plugin_if_mainwindow_apply_filter(GHashTable * data_set)105 static void plugin_if_mainwindow_apply_filter(GHashTable * data_set)
106 {
107     if (!gbl_cur_main_window_ || !data_set)
108         return;
109 
110     if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
111         QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
112         gbl_cur_main_window_->filterPackets(filter);
113     }
114 }
115 
plugin_if_mainwindow_preference(GHashTable * data_set)116 static void plugin_if_mainwindow_preference(GHashTable * data_set)
117 {
118     if (!gbl_cur_main_window_ || !data_set)
119         return;
120 
121     const char * module_name;
122     const char * pref_name;
123     const char * pref_value;
124 
125 DIAG_OFF_CAST_AWAY_CONST
126     if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (gpointer *)&module_name) &&
127         g_hash_table_lookup_extended(data_set, "pref_key", NULL, (gpointer *)&pref_name) &&
128         g_hash_table_lookup_extended(data_set, "pref_value", NULL, (gpointer *)&pref_value))
129     {
130         unsigned int changed_flags = prefs_store_ext(module_name, pref_name, pref_value);
131         if (changed_flags) {
132             wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
133             wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
134         }
135     }
136 DIAG_ON_CAST_AWAY_CONST
137 }
138 
plugin_if_mainwindow_gotoframe(GHashTable * data_set)139 static void plugin_if_mainwindow_gotoframe(GHashTable * data_set)
140 {
141     if (!gbl_cur_main_window_ || !data_set)
142         return;
143 
144     gpointer framenr;
145 
146     if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
147         if (GPOINTER_TO_UINT(framenr) != 0)
148             gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
149     }
150 }
151 
152 #ifdef HAVE_LIBPCAP
153 
plugin_if_mainwindow_get_ws_info(GHashTable * data_set)154 static void plugin_if_mainwindow_get_ws_info(GHashTable * data_set)
155 {
156     if (!gbl_cur_main_window_ || !data_set)
157         return;
158 
159     ws_info_t *ws_info = NULL;
160 
161     if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
162         return;
163 
164     CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
165     capture_file *cf = cfWrap->capFile();
166 
167     ws_info->ws_info_supported = true;
168 
169     /* If we have a filename attached to ws_info clear it */
170     if (ws_info->cf_filename != NULL)
171     {
172         g_free(ws_info->cf_filename);
173         ws_info->cf_filename = NULL;
174     }
175 
176     /* Determine the true state of the capture file.  We return the true state in
177     the ws_info structure and DON'T CHANGE the cf->state as we don't want to cause problems
178     with code that follows this. */
179     if (cf)
180     {
181         if (cf->filename)
182         {
183             /* As we have a cf->filename we'll use the name and the state */
184             ws_info->cf_filename = g_strdup(cf->filename);
185             ws_info->cf_state = cf->state;
186         }
187         else
188         {
189             /* When we come through here the cf->state can show FILE_READ_DONE even though the
190             file is actually closed (no filename). A better fix would be to have a
191             FILE_CLOSE_PENDING state but that involves a lot of code change elsewhere. */
192             ws_info->cf_state = FILE_CLOSED;
193         }
194     }
195 
196     if (!ws_info->cf_filename)
197     {
198         /* We may have a filename associated with the main window so let's use it */
199         QString fileNameString = gbl_cur_main_window_->getMwFileName();
200         if (fileNameString.length())
201         {
202             QByteArray ba = fileNameString.toLatin1();
203             const char *c_file_name = ba.data();
204             ws_info->cf_filename = g_strdup(c_file_name);
205         }
206     }
207 
208     if (cf) {
209         ws_info->cf_count = cf->count;
210 
211         QList<int> rows = gbl_cur_main_window_->selectedRows();
212         frame_data * fdata = NULL;
213         if (rows.count() > 0)
214             fdata = gbl_cur_main_window_->frameDataForRow(rows.at(0));
215 
216         if (cf->state == FILE_READ_DONE && fdata) {
217             ws_info->cf_framenr = fdata->num;
218             ws_info->frame_passed_dfilter = (fdata->passed_dfilter == 1);
219         }
220         else {
221             ws_info->cf_framenr = 0;
222             ws_info->frame_passed_dfilter = FALSE;
223         }
224     }
225     else
226     {
227         /* Initialise the other ws_info structure values */
228         ws_info->cf_count = 0;
229         ws_info->cf_framenr = 0;
230         ws_info->frame_passed_dfilter = FALSE;
231     }
232 }
233 
234 #endif /* HAVE_LIBPCAP */
235 
plugin_if_mainwindow_get_frame_data(GHashTable * data_set)236 static void plugin_if_mainwindow_get_frame_data(GHashTable* data_set)
237 {
238     if (!gbl_cur_main_window_ || !data_set)
239         return;
240 
241     plugin_if_frame_data_cb extract_cb;
242     void* user_data;
243     void** ret_value_ptr;
244 
245     if (g_hash_table_lookup_extended(data_set, "extract_cb", NULL, (void**)&extract_cb) &&
246         g_hash_table_lookup_extended(data_set, "user_data", NULL, (void**)&user_data) &&
247         g_hash_table_lookup_extended(data_set, "ret_value_ptr", NULL, (void**)&ret_value_ptr))
248     {
249         QList<int> rows = gbl_cur_main_window_->selectedRows();
250         if (rows.count() > 0) {
251             frame_data* fdata = gbl_cur_main_window_->frameDataForRow(rows.at(0));
252             if (fdata) {
253                 *ret_value_ptr = extract_cb(fdata, user_data);
254             }
255         }
256     }
257 }
258 
plugin_if_mainwindow_get_capture_file(GHashTable * data_set)259 static void plugin_if_mainwindow_get_capture_file(GHashTable* data_set)
260 {
261     if (!gbl_cur_main_window_ || !data_set)
262         return;
263 
264     plugin_if_capture_file_cb extract_cb;
265     void* user_data;
266     void** ret_value_ptr;
267 
268     if (g_hash_table_lookup_extended(data_set, "extract_cb", NULL, (void**)&extract_cb) &&
269         g_hash_table_lookup_extended(data_set, "user_data", NULL, (void**)&user_data) &&
270         g_hash_table_lookup_extended(data_set, "ret_value_ptr", NULL, (void**)&ret_value_ptr))
271     {
272         CaptureFile* cfWrap = gbl_cur_main_window_->captureFile();
273         capture_file* cf = cfWrap->capFile();
274         if (cf) {
275             *ret_value_ptr = extract_cb(cf, user_data);
276         }
277     }
278 }
279 
plugin_if_mainwindow_update_toolbars(GHashTable * data_set)280 static void plugin_if_mainwindow_update_toolbars(GHashTable * data_set)
281 {
282     if (!gbl_cur_main_window_ || !data_set)
283         return;
284 
285     if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
286         QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
287         gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
288 
289     }
290 }
291 
mainwindow_add_toolbar(const iface_toolbar * toolbar_entry)292 static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
293 {
294     if (gbl_cur_main_window_ && toolbar_entry)
295     {
296         gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
297     }
298 }
299 
mainwindow_remove_toolbar(const gchar * menu_title)300 static void mainwindow_remove_toolbar(const gchar *menu_title)
301 {
302     if (gbl_cur_main_window_ && menu_title)
303     {
304         gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
305     }
306 }
307 
findOrAddMenu(QMenu * parent_menu,QString & menu_text)308 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
309     QList<QAction *> actions = parent_menu->actions();
310     QList<QAction *>::const_iterator i;
311     for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
312         if ((*i)->text()==menu_text) {
313             return (*i)->menu();
314         }
315     }
316     // If we get here there menu entry was not found, add a sub menu
317     return parent_menu->addMenu(menu_text);
318 }
319 
MainWindow(QWidget * parent)320 MainWindow::MainWindow(QWidget *parent) :
321     QMainWindow(parent),
322     main_ui_(new Ui::MainWindow),
323     cur_layout_(QVector<unsigned>()),
324     packet_list_(NULL),
325     proto_tree_(NULL),
326     previous_focus_(NULL),
327     file_set_dialog_(NULL),
328     show_hide_actions_(NULL),
329     time_display_actions_(NULL),
330     time_precision_actions_(NULL),
331     funnel_statistics_(NULL),
332     freeze_focus_(NULL),
333     was_maximized_(false),
334     capture_stopping_(false),
335     capture_filter_valid_(false)
336 #ifdef HAVE_LIBPCAP
337     , capture_options_dialog_(NULL)
338     , info_data_()
339 #endif
340     , display_filter_dlg_(NULL)
341     , capture_filter_dlg_(NULL)
342 #ifdef _WIN32
343     , pipe_timer_(NULL)
344 #else
345     , pipe_notifier_(NULL)
346 #endif
347 #if defined(Q_OS_MAC)
348     , dock_menu_(NULL)
349 #endif
350 {
351     if (!gbl_cur_main_window_) {
352         connect(wsApp, SIGNAL(openStatCommandDialog(QString, const char*, void*)),
353                 this, SLOT(openStatCommandDialog(QString, const char*, void*)));
354         connect(wsApp, SIGNAL(openTapParameterDialog(QString, const QString, void*)),
355                 this, SLOT(openTapParameterDialog(QString, const QString, void*)));
356     }
357     gbl_cur_main_window_ = this;
358 #ifdef HAVE_LIBPCAP
359     capture_input_init(&cap_session_, CaptureFile::globalCapFile());
360 #endif
361 
362     findTextCodecs();
363     // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
364     // iterates over *all* of our children, looking for matching "on_" slots.
365     // The fewer children we have at this point the better.
366     main_ui_->setupUi(this);
367 #ifdef HAVE_SOFTWARE_UPDATE
368     update_action_ = new QAction(tr("Check for Updates…"), main_ui_->menuHelp);
369 #endif
370 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
371     wireless_frame_ = new WirelessFrame(this);
372     main_ui_->wirelessToolBar->addWidget(wireless_frame_);
373 #else
374     removeToolBar(main_ui_->wirelessToolBar);
375     main_ui_->menuView->removeAction(main_ui_->actionViewWirelessToolbar);
376 #endif
377 
378     setWindowIcon(wsApp->normalIcon());
379     setTitlebarForCaptureFile();
380     setMenusForCaptureFile();
381     setForCapturedPackets(false);
382     setMenusForFileSet(false);
383     interfaceSelectionChanged();
384     loadWindowGeometry();
385 
386 #ifndef HAVE_LUA
387     main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
388 #endif
389 
390     qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
391     qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
392     connect(this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)),
393             this, SLOT(queuedFilterAction(QString, FilterAction::Action, FilterAction::ActionType)),
394             Qt::QueuedConnection);
395 
396     //To prevent users use features before initialization complete
397     //Otherwise unexpected problems may occur
398     setFeaturesEnabled(false);
399     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
400     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
401     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
402     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
403     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
404     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
405     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addPluginIFStructures()));
406     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
407     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus()));
408 
409     connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
410     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
411     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
412     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
413     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
414     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
415 
416     connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
417     updateRecentCaptures();
418 
419 #if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)
420     connect(wsApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()),
421         Qt::BlockingQueuedConnection);
422     connect(wsApp, SIGNAL(softwareUpdateClose()), this, SLOT(close()),
423         Qt::BlockingQueuedConnection);
424 #endif
425 
426     df_combo_box_ = new DisplayFilterCombo(this);
427 
428     funnel_statistics_ = new FunnelStatistics(this, capture_file_);
429     connect(funnel_statistics_, &FunnelStatistics::setDisplayFilter, this, &MainWindow::setDisplayFilter);
430     connect(funnel_statistics_, SIGNAL(openCaptureFile(QString, QString)),
431             this, SLOT(openCaptureFile(QString, QString)));
432 
433     file_set_dialog_ = new FileSetDialog(this);
434     connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
435             this, SLOT(openCaptureFile(QString)));
436 
437     initMainToolbarIcons();
438 
439     main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionNewDisplayFilterExpression, df_combo_box_);
440 
441     // Make sure filter expressions overflow into a menu instead of a
442     // larger toolbar. We do this by adding them to a child toolbar.
443     // https://bugreports.qt.io/browse/QTBUG-2472
444     FilterExpressionToolBar *filter_expression_toolbar_ = new FilterExpressionToolBar(this);
445     connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterPreferences, this, &MainWindow::onFilterPreferences);
446     connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterSelected, this, &MainWindow::onFilterSelected);
447     connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterEdit, this, &MainWindow::onFilterEdit);
448 
449     main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
450 
451 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
452     connect(wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
453             this, SLOT(showPreferencesDialog(QString)));
454 #endif
455 
456     main_ui_->goToFrame->hide();
457     connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
458             main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
459 
460     // XXX For some reason the cursor is drawn funny with an input mask set
461     // https://bugreports.qt-project.org/browse/QTBUG-7174
462 
463     main_ui_->searchFrame->hide();
464     connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
465             main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
466 
467     main_ui_->addressEditorFrame->hide();
468     main_ui_->columnEditorFrame->hide();
469     main_ui_->preferenceEditorFrame->hide();
470     main_ui_->filterExpressionFrame->hide();
471 
472 #ifndef HAVE_LIBPCAP
473     main_ui_->menuCapture->setEnabled(false);
474 #endif
475 
476     // Set OS specific shortcuts for fullscreen mode
477 #if defined(Q_OS_MAC)
478     main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen);
479 #else
480     main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
481 #endif
482 
483 #if defined(Q_OS_MAC)
484 
485     main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
486     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
487     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
488     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
489 
490     main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
491 
492 #endif // Q_OS_MAC
493 
494 #ifdef HAVE_SOFTWARE_UPDATE
495     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
496     main_ui_->menuHelp->insertAction(update_sep, update_action_);
497     connect(update_action_, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
498 #endif
499     master_split_.setObjectName("splitterMaster");
500     extra_split_.setObjectName("splitterExtra");
501     master_split_.setChildrenCollapsible(false);
502     extra_split_.setChildrenCollapsible(false);
503     main_ui_->mainStack->addWidget(&master_split_);
504 
505     empty_pane_.setObjectName("emptyPane");
506     empty_pane_.setVisible(false);
507 
508     packet_list_ = new PacketList(&master_split_);
509     main_ui_->wirelessTimelineWidget->setPacketList(packet_list_);
510     connect(packet_list_, SIGNAL(framesSelected(QList<int>)), this, SLOT(setMenusForSelectedPacket()));
511     connect(packet_list_, SIGNAL(framesSelected(QList<int>)), this, SIGNAL(framesSelected(QList<int>)));
512 
513     connect(main_ui_->menuPacketComment, SIGNAL(aboutToShow()), this, SLOT(setEditCommentsMenu()));
514 
515     proto_tree_ = new ProtoTree(&master_split_);
516     proto_tree_->installEventFilter(this);
517 
518     packet_list_->setProtoTree(proto_tree_);
519     packet_list_->installEventFilter(this);
520 
521     packet_diagram_ = new PacketDiagram(&master_split_);
522 
523     welcome_page_ = main_ui_->welcomePage;
524 
525     connect(proto_tree_, &ProtoTree::fieldSelected,
526             this, &MainWindow::fieldSelected);
527     connect(packet_list_, &PacketList::fieldSelected,
528             this, &MainWindow::fieldSelected);
529     connect(this, &MainWindow::fieldSelected,
530             this, &MainWindow::setMenusForSelectedTreeRow);
531     connect(this, &MainWindow::fieldSelected,
532             main_ui_->statusBar, &MainStatusBar::selectedFieldChanged);
533 
534     connect(this, &MainWindow::fieldHighlight,
535             main_ui_->statusBar, &MainStatusBar::highlightedFieldChanged);
536     connect(wsApp, &WiresharkApplication::captureActive,
537             this, &MainWindow::captureActive);
538 
539     byte_view_tab_ = new ByteViewTab(&master_split_);
540 
541     // Packet list and proto tree must exist before these are called.
542     setMenusForSelectedPacket();
543     setMenusForSelectedTreeRow();
544 
545     initShowHideMainWidgets();
546     initTimeDisplayFormatMenu();
547     initTimePrecisionFormatMenu();
548     initFreezeActions();
549     updatePreferenceActions();
550     updateRecentActions();
551     setForCaptureInProgress(false);
552 
553     setTabOrder(df_combo_box_->lineEdit(), packet_list_);
554     setTabOrder(packet_list_, proto_tree_);
555 
556     connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)),
557             this, SLOT(captureEventHandler(CaptureEvent)));
558     connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)),
559             wsApp, SLOT(captureEventHandler(CaptureEvent)));
560     connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)),
561             main_ui_->statusBar, SLOT(captureEventHandler(CaptureEvent)));
562 
563     connect(wsApp, SIGNAL(columnsChanged()),
564             packet_list_, SLOT(columnsChanged()));
565     connect(wsApp, SIGNAL(preferencesChanged()),
566             packet_list_, SLOT(preferencesChanged()));
567     connect(wsApp, SIGNAL(recentPreferencesRead()),
568             this, SLOT(applyRecentPaneGeometry()));
569     connect(wsApp, SIGNAL(recentPreferencesRead()),
570             this, SLOT(updateRecentActions()));
571     connect(wsApp, SIGNAL(packetDissectionChanged()),
572             this, SLOT(redissectPackets()), Qt::QueuedConnection);
573 
574     connect(wsApp, SIGNAL(checkDisplayFilter()),
575             this, SLOT(checkDisplayFilter()));
576     connect(wsApp, SIGNAL(fieldsChanged()),
577             this, SLOT(fieldsChanged()));
578     connect(wsApp, SIGNAL(reloadLuaPlugins()),
579             this, SLOT(reloadLuaPlugins()));
580 
581     connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
582             this, SLOT(mainStackChanged(int)));
583 
584     connect(welcome_page_, SIGNAL(startCapture()),
585             this, SLOT(startCapture()));
586     connect(welcome_page_, SIGNAL(recentFileActivated(QString)),
587             this, SLOT(openCaptureFile(QString)));
588 
589     connect(main_ui_->addressEditorFrame, &AddressEditorFrame::redissectPackets,
590             this, &MainWindow::redissectPackets);
591     connect(main_ui_->addressEditorFrame, &AddressEditorFrame::showNameResolutionPreferences,
592             this, &MainWindow::showPreferencesDialog);
593     connect(main_ui_->preferenceEditorFrame, &PreferenceEditorFrame::showProtocolPreferences,
594             this, &MainWindow::showPreferencesDialog);
595     connect(main_ui_->filterExpressionFrame, &FilterExpressionFrame::showPreferencesDialog,
596             this, &MainWindow::showPreferencesDialog);
597     connect(main_ui_->filterExpressionFrame, &FilterExpressionFrame::filterExpressionsChanged,
598             filter_expression_toolbar_, &FilterExpressionToolBar::filterExpressionsChanged);
599 
600     /* Connect change of capture file */
601     connect(this, &MainWindow::setCaptureFile,
602             main_ui_->searchFrame, &SearchFrame::setCaptureFile);
603     connect(this, &MainWindow::setCaptureFile,
604             main_ui_->statusBar, &MainStatusBar::setCaptureFile);
605     connect(this, &MainWindow::setCaptureFile,
606             packet_list_, &PacketList::setCaptureFile);
607     connect(this, &MainWindow::setCaptureFile,
608             proto_tree_, &ProtoTree::setCaptureFile);
609 
610     connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)),
611             packet_list_, SLOT(setMonospaceFont(QFont)));
612     connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)),
613             proto_tree_, SLOT(setMonospaceFont(QFont)));
614 
615     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
616             packet_list_, SLOT(goNextPacket()));
617     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
618             packet_list_, SLOT(goPreviousPacket()));
619     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
620             packet_list_, SLOT(goFirstPacket()));
621     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
622             packet_list_, SLOT(goLastPacket()));
623     connect(main_ui_->actionGoNextHistoryPacket, SIGNAL(triggered()),
624             packet_list_, SLOT(goNextHistoryPacket()));
625     connect(main_ui_->actionGoPreviousHistoryPacket, SIGNAL(triggered()),
626             packet_list_, SLOT(goPreviousHistoryPacket()));
627 
628     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
629             proto_tree_, SLOT(expandSubtrees()));
630     connect(main_ui_->actionViewCollapseSubtrees, SIGNAL(triggered()),
631             proto_tree_, SLOT(collapseSubtrees()));
632     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
633             proto_tree_, SLOT(expandAll()));
634     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
635             proto_tree_, SLOT(collapseAll()));
636 
637     connect(packet_list_, SIGNAL(packetDissectionChanged()),
638             this, SLOT(redissectPackets()));
639     connect(packet_list_, SIGNAL(showColumnPreferences(QString)),
640             this, SLOT(showPreferencesDialog(QString)));
641     connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
642             this, SLOT(showPreferencesDialog(QString)));
643     connect(packet_list_, SIGNAL(editProtocolPreference(preference*, pref_module*)),
644             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*, pref_module*)));
645     connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
646     connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
647             packet_list_, SLOT(columnsChanged()));
648     connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
649             this, SLOT(openPacketDialog()));
650     connect(packet_list_, SIGNAL(packetListScrolled(bool)),
651             main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
652 
653     connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
654             this, SLOT(openPacketDialog(bool)));
655     connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
656             this, SLOT(showPreferencesDialog(QString)));
657     connect(proto_tree_, SIGNAL(editProtocolPreference(preference*, pref_module*)),
658             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*, pref_module*)));
659 
660     connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
661             this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
662 
663     connect(main_ui_->statusBar, SIGNAL(stopLoading()),
664             &capture_file_, SLOT(stopLoading()));
665 
666     connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
667             this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
668 
669     connect(main_ui_->menuApplyAsFilter, &QMenu::aboutToShow,
670             this, &MainWindow::filterMenuAboutToShow);
671     connect(main_ui_->menuPrepareAFilter, &QMenu::aboutToShow,
672             this, &MainWindow::filterMenuAboutToShow);
673 
674 #ifdef HAVE_LIBPCAP
675     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
676     if (iface_tree) {
677         connect(iface_tree, SIGNAL(itemSelectionChanged()),
678                 this, SLOT(interfaceSelectionChanged()));
679     }
680     connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
681             this, SLOT(captureFilterSyntaxChanged(bool)));
682 
683     connect(this->welcome_page_, SIGNAL(showExtcapOptions(QString&)),
684             this, SLOT(showExtcapOptionsDialog(QString&)));
685 
686 #endif // HAVE_LIBPCAP
687 
688     /* Create plugin_if hooks */
689     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
690     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
691     plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
692     plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
693 #ifdef HAVE_LIBPCAP
694     plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
695 #endif
696     plugin_if_register_gui_cb(PLUGIN_IF_GET_FRAME_DATA, plugin_if_mainwindow_get_frame_data);
697     plugin_if_register_gui_cb(PLUGIN_IF_GET_CAPTURE_FILE, plugin_if_mainwindow_get_capture_file);
698     plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
699 
700     /* Register Interface Toolbar callbacks */
701     iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
702 
703     showWelcome();
704 }
705 
~MainWindow()706 MainWindow::~MainWindow()
707 {
708     disconnect(main_ui_->mainStack, 0, 0, 0);
709 
710 #ifndef Q_OS_MAC
711     // Below dialogs inherit GeometryStateDialog
712     // For reasons described in geometry_state_dialog.h no parent is set when
713     // instantiating the dialogs and as a resul objects are not automatically
714     // freed by its parent. Free then here explicitly to avoid leak and numerous
715     // Valgrind complaints.
716     delete file_set_dialog_;
717     delete capture_filter_dlg_;
718     delete display_filter_dlg_;
719 #ifdef HAVE_LIBPCAP
720     delete capture_options_dialog_;
721 #endif
722 
723 #endif
724     delete main_ui_;
725 }
726 
getFilter()727 QString MainWindow::getFilter()
728 {
729     return df_combo_box_->currentText();
730 }
731 
createPopupMenu()732 QMenu *MainWindow::createPopupMenu()
733 {
734     QMenu *menu = new QMenu();
735     menu->addAction(main_ui_->actionViewMainToolbar);
736     menu->addAction(main_ui_->actionViewFilterToolbar);
737 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
738     menu->addAction(main_ui_->actionViewWirelessToolbar);
739 #endif
740 
741     if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
742         QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
743         foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
744             submenu->addAction(action);
745         }
746     }
747 
748     if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
749         QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
750         foreach(QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
751             subMenu->addAction(action);
752         }
753     }
754 
755     menu->addAction(main_ui_->actionViewStatusBar);
756 
757     menu->addSeparator();
758     menu->addAction(main_ui_->actionViewPacketList);
759     menu->addAction(main_ui_->actionViewPacketDetails);
760     menu->addAction(main_ui_->actionViewPacketBytes);
761     menu->addAction(main_ui_->actionViewPacketDiagram);
762     return menu;
763 }
764 
addInterfaceToolbar(const iface_toolbar * toolbar_entry)765 void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
766 {
767     QMenu *menu = main_ui_->menuInterfaceToolbars;
768     bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc)strcmp) ? true : false;
769 
770     QString title = QString().fromUtf8(toolbar_entry->menu_title);
771     QAction *action = new QAction(title, menu);
772     action->setEnabled(true);
773     action->setCheckable(true);
774     action->setChecked(visible);
775     action->setToolTip(tr("Show or hide the toolbar"));
776 
777     QAction *before = NULL;
778     foreach(QAction *action, menu->actions()) {
779         // Ensure we add the menu entries in sorted order
780         if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
781             before = action;
782             break;
783         }
784     }
785     menu->insertAction(before, action);
786 
787     InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
788     connect(wsApp, SIGNAL(appInitialized()), interface_toolbar, SLOT(interfaceListChanged()));
789     connect(wsApp, SIGNAL(localInterfaceListChanged()), interface_toolbar, SLOT(interfaceListChanged()));
790 
791     QToolBar *toolbar = new QToolBar(this);
792     toolbar->addWidget(interface_toolbar);
793     toolbar->setMovable(false);
794     toolbar->setVisible(visible);
795 
796     action->setData(QVariant::fromValue(toolbar));
797 
798     addToolBar(Qt::TopToolBarArea, toolbar);
799     insertToolBarBreak(toolbar);
800 
801     if (show_hide_actions_) {
802         show_hide_actions_->addAction(action);
803     }
804 
805     menu->menuAction()->setVisible(true);
806 }
807 
removeInterfaceToolbar(const gchar * menu_title)808 void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
809 {
810     QMenu *menu = main_ui_->menuInterfaceToolbars;
811     QAction *action = NULL;
812     QMap<QAction *, QWidget *>::iterator i;
813 
814     QString title = QString().fromUtf8(menu_title);
815     foreach(action, menu->actions()) {
816         if (title.compare(action->text()) == 0) {
817             break;
818         }
819     }
820 
821     if (action) {
822         if (show_hide_actions_) {
823             show_hide_actions_->removeAction(action);
824         }
825         menu->removeAction(action);
826 
827         QToolBar *toolbar = action->data().value<QToolBar *>();
828         removeToolBar(toolbar);
829 
830         delete action;
831         delete toolbar;
832     }
833 
834     menu->menuAction()->setVisible(!menu->actions().isEmpty());
835 }
836 
setPipeInputHandler(gint source,gpointer user_data,ws_process_id * child_process,pipe_input_cb_t input_cb)837 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
838 {
839     pipe_source_        = source;
840     pipe_child_process_ = child_process;
841     pipe_user_data_     = user_data;
842     pipe_input_cb_      = input_cb;
843 
844 #ifdef _WIN32
845     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
846        do this but that doesn't cover all win32 platforms.  GTK can do
847        this but doesn't seem to work over processes.  Attempt to do
848        something similar here, start a timer and check for data on every
849        timeout. */
850        /*ws_log(NULL, LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
851 
852     if (pipe_timer_) {
853         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
854         delete pipe_timer_;
855     }
856 
857     pipe_timer_ = new QTimer(this);
858     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
859     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
860     pipe_timer_->start(200);
861 #else
862     if (pipe_notifier_) {
863         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
864         delete pipe_notifier_;
865     }
866 
867     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
868     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
869     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
870     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
871 #endif
872 }
873 
eventFilter(QObject * obj,QEvent * event)874 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
875 
876     // The user typed some text. Start filling in a filter.
877     // We may need to be more choosy here. We just need to catch events for the packet list,
878     // proto tree, and main welcome widgets.
879     if (event->type() == QEvent::KeyPress) {
880         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
881         if (kevt->text().length() > 0 && kevt->text()[0].isPrint() &&
882             !(kevt->modifiers() & Qt::ControlModifier)) {
883             df_combo_box_->lineEdit()->insert(kevt->text());
884             df_combo_box_->lineEdit()->setFocus();
885             return true;
886         }
887     }
888 
889     return QMainWindow::eventFilter(obj, event);
890 }
891 
event(QEvent * event)892 bool MainWindow::event(QEvent *event)
893 {
894     switch (event->type()) {
895     case QEvent::ApplicationPaletteChange:
896         initMainToolbarIcons();
897         break;
898     default:
899         break;
900 
901     }
902     return QMainWindow::event(event);
903 }
904 
keyPressEvent(QKeyEvent * event)905 void MainWindow::keyPressEvent(QKeyEvent *event) {
906 
907     // Explicitly focus on the display filter combo.
908     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
909         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
910         return;
911     }
912 
913     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
914         if (event->modifiers() == Qt::NoModifier) {
915             if (event->key() == Qt::Key_Escape) {
916                 on_goToCancel_clicked();
917             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
918                 on_goToGo_clicked();
919             }
920         }
921         return; // goToLineEdit didn't want it and we don't either.
922     }
923 
924     // Move up & down the packet list.
925     if (event->key() == Qt::Key_F7) {
926         packet_list_->goPreviousPacket();
927     } else if (event->key() == Qt::Key_F8) {
928         packet_list_->goNextPacket();
929     }
930 
931     // Move along, citizen.
932     QMainWindow::keyPressEvent(event);
933 }
934 
closeEvent(QCloseEvent * event)935 void MainWindow::closeEvent(QCloseEvent *event) {
936     saveWindowGeometry();
937 
938     /* If we're in the middle of stopping a capture, don't do anything;
939        the user can try deleting the window after the capture stops. */
940     if (capture_stopping_) {
941         event->ignore();
942         return;
943     }
944 
945     QString before_what(tr(" before quitting"));
946     if (!testCaptureFileClose(before_what, Quit)) {
947         event->ignore();
948         return;
949     }
950 
951 #ifdef HAVE_LIBPCAP
952     if (capture_options_dialog_) capture_options_dialog_->close();
953 #endif
954     // Make sure we kill any open dumpcap processes.
955     delete welcome_page_;
956 
957     // One of the many places we assume one main window.
958     if (!wsApp->isInitialized()) {
959         // If we're still initializing, QCoreApplication::quit() won't
960         // exit properly because we are not in the event loop. This
961         // means that the application won't clean up after itself. We
962         // might want to call wsApp->processEvents() during startup
963         // instead so that we can do a normal exit here.
964         exit(0);
965     }
966     wsApp->quit();
967     // When the main loop is not yet running (i.e. when openCaptureFile is
968     // executing in main.cpp), the above quit action has no effect.
969     // Schedule a quit action for the next execution of the main loop.
970     QMetaObject::invokeMethod(wsApp, "quit", Qt::QueuedConnection);
971 }
972 
973 // XXX On windows the drag description is "Copy". It should be "Open" or
974 // "Merge" as appropriate. It looks like we need access to IDataObject in
975 // order to set DROPDESCRIPTION.
dragEnterEvent(QDragEnterEvent * event)976 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
977 {
978     if (!event->mimeData()->hasUrls())
979     {
980         event->ignore();
981         return;
982     }
983 
984     if (!main_ui_->actionFileOpen->isEnabled()) {
985         // We could alternatively call setAcceptDrops(!capture_in_progress)
986         // in setMenusForCaptureInProgress but that wouldn't provide feedback.
987 
988         wsApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("Unable to drop files during capture."));
989         event->setDropAction(Qt::IgnoreAction);
990         event->ignore();
991         return;
992     }
993 
994     bool have_files = false;
995     foreach(QUrl drag_url, event->mimeData()->urls()) {
996         if (!drag_url.toLocalFile().isEmpty()) {
997             have_files = true;
998             break;
999         }
1000     }
1001 
1002     if (have_files) {
1003         event->acceptProposedAction();
1004     }
1005 }
1006 
dropEvent(QDropEvent * event)1007 void MainWindow::dropEvent(QDropEvent *event)
1008 {
1009     if (!event->mimeData()->hasUrls())
1010     {
1011         event->ignore();
1012         return;
1013     }
1014 
1015     QList<QByteArray> local_files;
1016     int max_dropped_files = 100; // Arbitrary
1017 
1018     foreach(QUrl drop_url, event->mimeData()->urls()) {
1019         QString drop_file = drop_url.toLocalFile();
1020         if (!drop_file.isEmpty()) {
1021             local_files << drop_file.toUtf8();
1022             if (local_files.size() >= max_dropped_files) {
1023                 break;
1024             }
1025         }
1026     }
1027 
1028     event->acceptProposedAction();
1029 
1030     if (local_files.size() < 1) {
1031         event->ignore();
1032         return;
1033     }
1034 
1035     event->accept();
1036 
1037     if (local_files.size() == 1) {
1038         openCaptureFile(local_files.at(0));
1039         return;
1040     }
1041 
1042     const char **in_filenames = g_new(const char *, local_files.size());
1043     char *tmpname = NULL;
1044 
1045     for (int i = 0; i < local_files.size(); i++) {
1046         in_filenames[i] = local_files.at(i).constData();
1047     }
1048 
1049     /* merge the files in chronological order */
1050     if (cf_merge_files_to_tempfile(this, &tmpname, local_files.size(),
1051                                    in_filenames,
1052                                    wtap_pcapng_file_type_subtype(),
1053                                    FALSE) == CF_OK) {
1054         /* Merge succeeded; close the currently-open file and try
1055            to open the merged capture file. */
1056         openCaptureFile(tmpname, QString(), WTAP_TYPE_AUTO, TRUE);
1057     }
1058 
1059     g_free(tmpname);
1060     g_free(in_filenames);
1061 }
1062 
1063 // Apply recent settings to the main window geometry.
1064 // We haven't loaded the preferences at this point so we assume that the
1065 // position and size preference are enabled.
1066 // Note we might end up with unexpected screen geometries if the user
1067 // unplugs or plugs in a monitor:
1068 // https://bugreports.qt.io/browse/QTBUG-44213
loadWindowGeometry()1069 void MainWindow::loadWindowGeometry()
1070 {
1071     int min_sensible_dimension = 200;
1072 
1073 #ifndef Q_OS_MAC
1074     if (recent.gui_geometry_main_maximized) {
1075         setWindowState(Qt::WindowMaximized);
1076     } else
1077 #endif
1078     {
1079         QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
1080                           recent.gui_geometry_main_width, recent.gui_geometry_main_height);
1081         if (!rect_on_screen(recent_geom)) {
1082             // We're not visible on any screens. See if we can move onscreen
1083             // without resizing.
1084             recent_geom.moveTo(50, 50); // recent.c defaults to 20.
1085         }
1086 
1087         if (!rect_on_screen(recent_geom)) {
1088             // Give up and use the default geometry.
1089             return;
1090         }
1091 
1092 //        if (prefs.gui_geometry_save_position) {
1093         move(recent_geom.topLeft());
1094 //        }
1095 
1096         if (// prefs.gui_geometry_save_size &&
1097                 recent_geom.width() > min_sensible_dimension &&
1098                 recent_geom.height() > min_sensible_dimension) {
1099             resize(recent_geom.size());
1100         }
1101     }
1102 }
1103 
saveWindowGeometry()1104 void MainWindow::saveWindowGeometry()
1105 {
1106     if (prefs.gui_geometry_save_position) {
1107         recent.gui_geometry_main_x = pos().x();
1108         recent.gui_geometry_main_y = pos().y();
1109     }
1110 
1111     if (prefs.gui_geometry_save_size) {
1112         recent.gui_geometry_main_width = size().width();
1113         recent.gui_geometry_main_height = size().height();
1114     }
1115 
1116     if (prefs.gui_geometry_save_maximized) {
1117         // On macOS this is false when it shouldn't be
1118         recent.gui_geometry_main_maximized = isMaximized();
1119     }
1120 
1121     if (master_split_.sizes().length() > 0) {
1122         recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
1123     }
1124 
1125     if (master_split_.sizes().length() > 2) {
1126         recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
1127     } else if (extra_split_.sizes().length() > 0) {
1128         recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
1129     }
1130 }
1131 
1132 // Our event loop becomes nested whenever we call update_progress_dlg, which
1133 // includes several places in file.c. The GTK+ UI stays out of trouble by
1134 // showing a modal progress dialog. We attempt to do the equivalent below by
1135 // disabling parts of the main window. At a minumum the ProgressFrame in the
1136 // main status bar must remain accessible.
1137 //
1138 // We might want to do this any time the main status bar progress frame is
1139 // shown and hidden.
freeze()1140 void MainWindow::freeze()
1141 {
1142     freeze_focus_ = wsApp->focusWidget();
1143 
1144     // XXX Alternatively we could just disable and enable the main menu.
1145     for (int i = 0; i < freeze_actions_.size(); i++) {
1146         QAction *action = freeze_actions_[i].first;
1147         freeze_actions_[i].second = action->isEnabled();
1148         action->setEnabled(false);
1149     }
1150     main_ui_->centralWidget->setEnabled(false);
1151 }
1152 
thaw()1153 void MainWindow::thaw()
1154 {
1155     main_ui_->centralWidget->setEnabled(true);
1156     for (int i = 0; i < freeze_actions_.size(); i++) {
1157         freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
1158     }
1159 
1160     if (freeze_focus_) freeze_focus_->setFocus();
1161     freeze_focus_ = NULL;
1162 }
1163 
mergeCaptureFile()1164 void MainWindow::mergeCaptureFile()
1165 {
1166     QString file_name = "";
1167     QString read_filter = "";
1168     dfilter_t *rfcode = NULL;
1169     int err;
1170 
1171     if (!capture_file_.capFile())
1172         return;
1173 
1174     if (prefs.gui_ask_unsaved) {
1175         if (cf_has_unsaved_data(capture_file_.capFile())) {
1176             QMessageBox msg_dialog;
1177             gchar *display_basename;
1178             int response;
1179 
1180             msg_dialog.setIcon(QMessageBox::Question);
1181             /* This file has unsaved data; ask the user whether to save
1182                the capture. */
1183             if (capture_file_.capFile()->is_tempfile) {
1184                 msg_dialog.setText(tr("Save packets before merging?"));
1185                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
1186             } else {
1187                 /*
1188                  * Format the message.
1189                  */
1190                 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1191                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
1192                 g_free(display_basename);
1193                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
1194             }
1195 
1196             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
1197             msg_dialog.setDefaultButton(QMessageBox::Save);
1198 
1199             response = msg_dialog.exec();
1200 
1201             switch (response) {
1202 
1203             case QMessageBox::Save:
1204                 /* Save the file but don't close it */
1205                 saveCaptureFile(capture_file_.capFile(), false);
1206                 break;
1207 
1208             case QMessageBox::Cancel:
1209             default:
1210                 /* Don't do the merge. */
1211                 return;
1212             }
1213         }
1214     }
1215 
1216     for (;;) {
1217         CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1218         int file_type;
1219         cf_status_t  merge_status;
1220         char        *in_filenames[2];
1221         char        *tmpname;
1222 
1223         if (merge_dlg.merge(file_name)) {
1224             gchar *err_msg;
1225 
1226             if (!dfilter_compile(qUtf8Printable(read_filter), &rfcode, &err_msg)) {
1227                 /* Not valid. Tell the user, and go back and run the file
1228                    selection box again once they dismiss the alert. */
1229                 // Similar to commandline_info.jfilter section in main().
1230                 QMessageBox::warning(this, tr("Invalid Read Filter"),
1231                                      QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1232                                      QMessageBox::Ok);
1233                 g_free(err_msg);
1234                 continue;
1235             }
1236         } else {
1237             return;
1238         }
1239 
1240         file_type = capture_file_.capFile()->cd_t;
1241 
1242         /* Try to merge or append the two files */
1243         if (merge_dlg.mergeType() == 0) {
1244             /* chronological order */
1245             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1246             in_filenames[1] = qstring_strdup(file_name);
1247             merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, FALSE);
1248         } else if (merge_dlg.mergeType() <= 0) {
1249             /* prepend file */
1250             in_filenames[0] = qstring_strdup(file_name);
1251             in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1252             merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1253         } else {
1254             /* append file */
1255             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1256             in_filenames[1] = qstring_strdup(file_name);
1257             merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1258         }
1259 
1260         g_free(in_filenames[0]);
1261         g_free(in_filenames[1]);
1262 
1263         if (merge_status != CF_OK) {
1264             dfilter_free(rfcode);
1265             g_free(tmpname);
1266             continue;
1267         }
1268 
1269         cf_close(capture_file_.capFile());
1270 
1271         /* Try to open the merged capture file. */
1272         CaptureFile::globalCapFile()->window = this;
1273         if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1274             /* We couldn't open it; fail. */
1275             CaptureFile::globalCapFile()->window = NULL;
1276             dfilter_free(rfcode);
1277             g_free(tmpname);
1278             return;
1279         }
1280 
1281         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1282            it closed the previous capture file, and thus destroyed any
1283            previous read filter attached to "cf"). */
1284         cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1285 
1286         switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1287 
1288         case CF_READ_OK:
1289         case CF_READ_ERROR:
1290             /* Just because we got an error, that doesn't mean we were unable
1291              to read any of the file; we handle what we could get from the
1292              file. */
1293             break;
1294 
1295         case CF_READ_ABORTED:
1296             /* The user bailed out of re-reading the capture file; the
1297              capture file has been closed - just free the capture file name
1298              string and return (without changing the last containing
1299              directory). */
1300             g_free(tmpname);
1301             return;
1302         }
1303 
1304         /* Save the name of the containing directory specified in the path name. */
1305         wsApp->setLastOpenDirFromFilename(tmpname);
1306         g_free(tmpname);
1307         main_ui_->statusBar->showExpert();
1308         return;
1309     }
1310 
1311 }
1312 
importCaptureFile()1313 void MainWindow::importCaptureFile() {
1314     ImportTextDialog import_dlg;
1315 
1316     QString before_what(tr(" before importing a capture"));
1317     if (!testCaptureFileClose(before_what))
1318         return;
1319 
1320     import_dlg.exec();
1321 
1322     if (import_dlg.result() != QDialog::Accepted) {
1323         showWelcome();
1324         return;
1325     }
1326 
1327     openCaptureFile(import_dlg.capfileName());
1328 }
1329 
saveCaptureFile(capture_file * cf,bool dont_reopen)1330 bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1331     QString file_name;
1332     gboolean discard_comments;
1333 
1334     if (cf->is_tempfile) {
1335         /* This is a temporary capture file, so saving it means saving
1336            it to a permanent file.  Prompt the user for a location
1337            to which to save it.  Don't require that the file format
1338            support comments - if it's a temporary capture file, it's
1339            probably pcapng, which supports comments and, if it's
1340            not pcapng, let the user decide what they want to do
1341            if they've added comments. */
1342         return saveAsCaptureFile(cf, FALSE, dont_reopen);
1343     } else {
1344         if (cf->unsaved_changes) {
1345             cf_write_status_t status;
1346 
1347             /* This is not a temporary capture file, but it has unsaved
1348                changes, so saving it means doing a "safe save" on top
1349                of the existing file, in the same format - no UI needed
1350                unless the file has comments and the file's format doesn't
1351                support them.
1352 
1353                If the file has comments, does the file's format support them?
1354                If not, ask the user whether they want to discard the comments
1355                or choose a different format. */
1356             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1357 
1358             case SAVE:
1359                 /* The file can be saved in the specified format as is;
1360                    just drive on and save in the format they selected. */
1361                 discard_comments = FALSE;
1362                 break;
1363 
1364             case SAVE_WITHOUT_COMMENTS:
1365                 /* The file can't be saved in the specified format as is,
1366                    but it can be saved without the comments, and the user
1367                    said "OK, discard the comments", so save it in the
1368                    format they specified without the comments. */
1369                 discard_comments = TRUE;
1370                 break;
1371 
1372             case SAVE_IN_ANOTHER_FORMAT:
1373                 /* There are file formats in which we can save this that
1374                    support comments, and the user said not to delete the
1375                    comments.  Do a "Save As" so the user can select
1376                    one of those formats and choose a file name. */
1377                 return saveAsCaptureFile(cf, TRUE, dont_reopen);
1378 
1379             case CANCELLED:
1380                 /* The user said "forget it".  Just return. */
1381                 return false;
1382 
1383             default:
1384                 /* Squelch warnings that discard_comments is being used
1385                    uninitialized. */
1386                 ws_assert_not_reached();
1387                 return false;
1388             }
1389 
1390             /* XXX - cf->filename might get freed out from under us, because
1391                the code path through which cf_save_records() goes currently
1392                closes the current file and then opens and reloads the saved file,
1393                so make a copy and free it later. */
1394             file_name = cf->filename;
1395             status = cf_save_records(cf, qUtf8Printable(file_name), cf->cd_t, cf->compression_type,
1396                                      discard_comments, dont_reopen);
1397             switch (status) {
1398 
1399             case CF_WRITE_OK:
1400                 /* The save succeeded; we're done.
1401                    If we discarded comments, redraw the packet list to reflect
1402                    any packets that no longer have comments. */
1403                 if (discard_comments)
1404                     packet_list_queue_draw();
1405 
1406                 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1407                 updateForUnsavedChanges(); // we update the title bar to remove the *
1408                 break;
1409 
1410             case CF_WRITE_ERROR:
1411                 /* The write failed.
1412                    XXX - OK, what do we do now?  Let them try a
1413                    "Save As", in case they want to try to save to a
1414                    different directory or file system? */
1415                 break;
1416 
1417             case CF_WRITE_ABORTED:
1418                 /* The write was aborted; just drive on. */
1419                 return false;
1420             }
1421         }
1422         /* Otherwise just do nothing. */
1423     }
1424 
1425     return true;
1426 }
1427 
saveAsCaptureFile(capture_file * cf,bool must_support_comments,bool dont_reopen)1428 bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1429     QString file_name = "";
1430     int file_type;
1431     wtap_compression_type compression_type;
1432     cf_write_status_t status;
1433     gchar   *dirname;
1434     gboolean discard_comments = FALSE;
1435 
1436     if (!cf) {
1437         return false;
1438     }
1439 
1440     for (;;) {
1441         CaptureFileDialog save_as_dlg(this, cf);
1442 
1443         /* If the file has comments, does the format the user selected
1444            support them?  If not, ask the user whether they want to
1445            discard the comments or choose a different format. */
1446         switch (save_as_dlg.saveAs(file_name, must_support_comments)) {
1447 
1448         case SAVE:
1449             /* The file can be saved in the specified format as is;
1450                just drive on and save in the format they selected. */
1451             discard_comments = FALSE;
1452             break;
1453 
1454         case SAVE_WITHOUT_COMMENTS:
1455             /* The file can't be saved in the specified format as is,
1456                but it can be saved without the comments, and the user
1457                said "OK, discard the comments", so save it in the
1458                format they specified without the comments. */
1459             discard_comments = TRUE;
1460             break;
1461 
1462         case SAVE_IN_ANOTHER_FORMAT:
1463             /* There are file formats in which we can save this that
1464                support comments, and the user said not to delete the
1465                comments.  The combo box of file formats has had the
1466                formats that don't support comments trimmed from it,
1467                so run the dialog again, to let the user decide
1468                whether to save in one of those formats or give up. */
1469             must_support_comments = TRUE;
1470             continue;
1471 
1472         case CANCELLED:
1473             /* The user said "forget it".  Just get rid of the dialog box
1474                and return. */
1475             return false;
1476         }
1477         file_type = save_as_dlg.selectedFileType();
1478         if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) {
1479             /* This "should not happen". */
1480             QMessageBox msg_dialog;
1481 
1482             msg_dialog.setIcon(QMessageBox::Critical);
1483             msg_dialog.setText(tr("Unknown file type returned by merge dialog."));
1484             msg_dialog.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues."));
1485             msg_dialog.exec();
1486             return false;
1487 	}
1488         compression_type = save_as_dlg.compressionType();
1489 
1490 #ifdef Q_OS_WIN
1491         // the Windows dialog does not fixup extensions, do it manually here.
1492         fileAddExtension(file_name, file_type, compression_type);
1493 #endif // Q_OS_WIN
1494 
1495 //#ifndef _WIN32
1496 //        /* If the file exists and it's user-immutable or not writable,
1497 //                       ask the user whether they want to override that. */
1498 //        if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) {
1499 //            /* They don't.  Let them try another file name or cancel. */
1500 //            continue;
1501 //        }
1502 //#endif
1503 
1504         /* Attempt to save the file */
1505         status = cf_save_records(cf, qUtf8Printable(file_name), file_type, compression_type,
1506                                  discard_comments, dont_reopen);
1507         switch (status) {
1508 
1509         case CF_WRITE_OK:
1510             /* The save succeeded; we're done. */
1511             /* Save the directory name for future file dialogs. */
1512             dirname = qstring_strdup(file_name);  /* Overwrites cf_name */
1513             set_last_open_dir(get_dirname(dirname));
1514             g_free(dirname);
1515             /* If we discarded comments, redraw the packet list to reflect
1516                any packets that no longer have comments. */
1517             if (discard_comments)
1518                 packet_list_queue_draw();
1519 
1520             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1521             updateForUnsavedChanges(); // we update the title bar to remove the *
1522             /* Add this filename to the list of recent files in the "Recent Files" submenu */
1523             add_menu_recent_capture_file(qUtf8Printable(file_name));
1524             return true;
1525 
1526         case CF_WRITE_ERROR:
1527             /* The save failed; let the user try again. */
1528             continue;
1529 
1530         case CF_WRITE_ABORTED:
1531             /* The user aborted the save; just return. */
1532             return false;
1533         }
1534     }
1535     return true;
1536 }
1537 
exportSelectedPackets()1538 void MainWindow::exportSelectedPackets() {
1539     QString file_name = "";
1540     int file_type;
1541     wtap_compression_type compression_type;
1542     packet_range_t range;
1543     cf_write_status_t status;
1544     gchar   *dirname;
1545     bool discard_comments = false;
1546 
1547     if (!capture_file_.capFile())
1548         return;
1549 
1550     /* Init the packet range */
1551     packet_range_init(&range, capture_file_.capFile());
1552     range.process_filtered = TRUE;
1553     range.include_dependents = TRUE;
1554 
1555     QList<int> rows = packet_list_->selectedRows(true);
1556 
1557     QStringList entries;
1558     foreach (int row, rows)
1559         entries << QString::number(row);
1560     QString selRange = entries.join(",");
1561 
1562     for (;;) {
1563         CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1564 
1565         /* If the file has comments, does the format the user selected
1566            support them?  If not, ask the user whether they want to
1567            discard the comments or choose a different format. */
1568         switch (esp_dlg.exportSelectedPackets(file_name, &range, selRange)) {
1569 
1570         case SAVE:
1571             /* The file can be saved in the specified format as is;
1572                just drive on and save in the format they selected. */
1573             discard_comments = FALSE;
1574             break;
1575 
1576         case SAVE_WITHOUT_COMMENTS:
1577             /* The file can't be saved in the specified format as is,
1578                but it can be saved without the comments, and the user
1579                said "OK, discard the comments", so save it in the
1580                format they specified without the comments. */
1581             discard_comments = TRUE;
1582             break;
1583 
1584         case SAVE_IN_ANOTHER_FORMAT:
1585             /* There are file formats in which we can save this that
1586                support comments, and the user said not to delete the
1587                comments.  The combo box of file formats has had the
1588                formats that don't support comments trimmed from it,
1589                so run the dialog again, to let the user decide
1590                whether to save in one of those formats or give up. */
1591             continue;
1592 
1593         case CANCELLED:
1594             /* The user said "forget it".  Just get rid of the dialog box
1595                and return. */
1596             goto cleanup;
1597         }
1598 
1599         /*
1600          * Check that we're not going to save on top of the current
1601          * capture file.
1602          * We do it here so we catch all cases ...
1603          * Unfortunately, the file requester gives us an absolute file
1604          * name and the read file name may be relative (if supplied on
1605          * the command line). From Joerg Mayer.
1606          */
1607         if (files_identical(capture_file_.capFile()->filename, qUtf8Printable(file_name))) {
1608             QMessageBox msg_box;
1609             gchar *display_basename = g_filename_display_basename(qUtf8Printable(file_name));
1610 
1611             msg_box.setIcon(QMessageBox::Critical);
1612             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1613             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1614             msg_box.setStandardButtons(QMessageBox::Ok);
1615             msg_box.setDefaultButton(QMessageBox::Ok);
1616             msg_box.exec();
1617             g_free(display_basename);
1618             continue;
1619         }
1620 
1621         file_type = esp_dlg.selectedFileType();
1622         if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) {
1623             /* This "should not happen". */
1624             QMessageBox msg_box;
1625 
1626             msg_box.setIcon(QMessageBox::Critical);
1627             msg_box.setText(tr("Unknown file type returned by export dialog."));
1628             msg_box.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues."));
1629             msg_box.exec();
1630             goto cleanup;
1631 	}
1632         compression_type = esp_dlg.compressionType();
1633 #ifdef Q_OS_WIN
1634         // the Windows dialog does not fixup extensions, do it manually here.
1635         fileAddExtension(file_name, file_type, compression_type);
1636 #endif // Q_OS_WIN
1637 
1638 //#ifndef _WIN32
1639 //        /* If the file exists and it's user-immutable or not writable,
1640 //                       ask the user whether they want to override that. */
1641 //        if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) {
1642 //            /* They don't.  Let them try another file name or cancel. */
1643 //            continue;
1644 //        }
1645 //#endif
1646 
1647         /* Attempt to save the file */
1648         status = cf_export_specified_packets(capture_file_.capFile(), qUtf8Printable(file_name), &range, file_type, compression_type);
1649         switch (status) {
1650 
1651         case CF_WRITE_OK:
1652             /* The save succeeded; we're done. */
1653             /* Save the directory name for future file dialogs. */
1654             dirname = qstring_strdup(file_name);  /* Overwrites cf_name */
1655             set_last_open_dir(get_dirname(dirname));
1656             g_free(dirname);
1657             /* If we discarded comments, redraw the packet list to reflect
1658                any packets that no longer have comments. */
1659             if (discard_comments)
1660                 packet_list_queue_draw();
1661             /* Add this filename to the list of recent files in the "Recent Files" submenu */
1662             add_menu_recent_capture_file(qUtf8Printable(file_name));
1663             goto cleanup;
1664 
1665         case CF_WRITE_ERROR:
1666             /* The save failed; let the user try again. */
1667             continue;
1668 
1669         case CF_WRITE_ABORTED:
1670             /* The user aborted the save; just return. */
1671             goto cleanup;
1672         }
1673     }
1674 
1675 cleanup:
1676     packet_range_cleanup(&range);
1677 }
1678 
exportDissections(export_type_e export_type)1679 void MainWindow::exportDissections(export_type_e export_type) {
1680     capture_file *cf = capture_file_.capFile();
1681     g_return_if_fail(cf);
1682 
1683     QList<int> rows = packet_list_->selectedRows(true);
1684 
1685     QStringList entries;
1686     foreach (int row, rows)
1687         entries << QString::number(row);
1688     QString selRange = entries.join(",");
1689 
1690     ExportDissectionDialog *ed_dlg = new ExportDissectionDialog(this, cf, export_type, selRange);
1691     ed_dlg->setWindowModality(Qt::ApplicationModal);
1692     ed_dlg->setAttribute(Qt::WA_DeleteOnClose);
1693     ed_dlg->show();
1694 }
1695 
1696 #ifdef Q_OS_WIN
1697 /*
1698  * Ensure that:
1699  *
1700  * If the file is to be compressed:
1701  *
1702  *    if there is a set of extensions used by the file type to be used,
1703  *    the file name has one of those extensions followed by the extension
1704  *    for the compression type to be used;
1705  *
1706  *    otherwise, the file name has the extension for the compression type
1707  *    to be used;
1708  *
1709  * otherwise:
1710  *
1711  *    if there is a set of extensions used by the file type to be used,
1712  *    the file name has one of those extensions.
1713  */
fileAddExtension(QString & file_name,int file_type,wtap_compression_type compression_type)1714 void MainWindow::fileAddExtension(QString &file_name, int file_type, wtap_compression_type compression_type) {
1715     QString file_name_lower;
1716     GSList  *extensions_list;
1717     const char *compressed_file_extension;
1718     gboolean add_extension_for_file_type;
1719 
1720     /* Lower-case the file name, so the extension matching is case-insensitive. */
1721     file_name_lower = file_name.toLower();
1722 
1723     /* Get a list of all extensions used for this file type; don't
1724        include the ones with compression type extensions, as we
1725        only want to check for the extension for the compression
1726        type we'll be using. */
1727     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1728 
1729     /* Get the extension for the compression type we'll be using;
1730        NULL is returned if the type isn't supported or compression
1731        is not being done. */
1732     compressed_file_extension = wtap_compression_type_extension(compression_type);
1733 
1734     if (extensions_list != NULL) {
1735         GSList *extension;
1736 
1737         /* This file type has one or more extensions.
1738            Start out assuming we need to add the default one. */
1739         add_extension_for_file_type = TRUE;
1740 
1741         /* OK, see if the file has one of those extensions, followed
1742            by the appropriate compression type extension if it's to be
1743            compressed. */
1744         for (extension = extensions_list; extension != NULL;
1745              extension = g_slist_next(extension)) {
1746             QString file_suffix = QString(".") + (char *)extension->data;
1747             if (compressed_file_extension != NULL)
1748                 file_suffix += QString(".") + compressed_file_extension;
1749             if (file_name_lower.endsWith(file_suffix)) {
1750                 /*
1751                  * The file name has one of the extensions for this file
1752                  * type, followed by a compression type extension if
1753                  * appropriate, so we don't need to add an extension for
1754                  * the file type or the compression type.
1755                  */
1756                 add_extension_for_file_type = FALSE;
1757                 break;
1758             }
1759         }
1760     } else {
1761         /* We have no extensions for this file type.  Just check
1762            to see if we need to add an extension for the compressed
1763            file type.
1764 
1765            Start out assuming we do. */
1766         add_extension_for_file_type = TRUE;
1767         if (compressed_file_extension != NULL) {
1768             QString file_suffix = QString(".") + compressed_file_extension;
1769             if (file_name_lower.endsWith(file_suffix)) {
1770                 /*
1771                  * The file name has the appropriate compressed file extension,
1772                  * so we don't need to add an extension for the compression
1773                  * type.
1774                  */
1775                 add_extension_for_file_type = FALSE;
1776             }
1777         }
1778     }
1779 
1780     /*
1781      * If we need to add an extension for the file type or compressed
1782      * file type, do so.
1783      */
1784     if (add_extension_for_file_type) {
1785         if (wtap_default_file_extension(file_type) != NULL) {
1786             /* This file type has a default extension; append it. */
1787             file_name += QString(".") + wtap_default_file_extension(file_type);
1788         }
1789         if (compression_type != WTAP_UNCOMPRESSED) {
1790             /*
1791              * The file is to be compressed, so append the extension for
1792              * its compression type.
1793              */
1794             file_name += QString(".") + compressed_file_extension;
1795         }
1796     }
1797 }
1798 #endif // Q_OS_WIN
1799 
testCaptureFileClose(QString before_what,FileCloseContext context)1800 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1801     bool capture_in_progress = false;
1802     bool do_close_file = false;
1803 
1804     if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1805         return true; /* Already closed, nothing to do */
1806 
1807     if (capture_file_.capFile()->read_lock) {
1808         /*
1809          * If the file is being redissected, we cannot stop the capture since
1810          * that would crash and burn "cf_read", so stop early. Ideally all
1811          * callers should be modified to check this condition and act
1812          * accordingly (ignore action or queue it up), so print a warning.
1813          */
1814         ws_warning("Refusing to close \"%s\" which is being read.", capture_file_.capFile()->filename);
1815         return false;
1816     }
1817 
1818 #ifdef HAVE_LIBPCAP
1819     if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1820         /*
1821          * This (FILE_READ_IN_PROGRESS) is true if we're reading a capture file
1822          * *or* if we're doing a live capture. From the capture file itself we
1823          * cannot differentiate the cases, so check the current capture session.
1824          */
1825         capture_in_progress = captureSession()->state != CAPTURE_STOPPED;
1826     }
1827 #endif
1828 
1829     if (prefs.gui_ask_unsaved) {
1830         if (cf_has_unsaved_data(capture_file_.capFile())) {
1831             QMessageBox msg_dialog;
1832             QString question;
1833             QString infotext;
1834             QPushButton *save_button;
1835             QPushButton *discard_button;
1836 
1837             msg_dialog.setIcon(QMessageBox::Question);
1838             msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1839 
1840             /* This file has unsaved data or there's a capture in
1841                progress; ask the user whether to save the data. */
1842             if (capture_in_progress && context != Restart) {
1843                 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1844                 infotext = tr("Your captured packets will be lost if you don't save them.");
1845             } else if (capture_file_.capFile()->is_tempfile) {
1846                 if (context == Reload) {
1847                     // Reloading a tempfile will keep the packets, so this is not unsaved packets
1848                     question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1849                     infotext = tr("Your changes will be lost if you don't save them.");
1850                 } else {
1851                     question = tr("Do you want to save the captured packets%1?").arg(before_what);
1852                     infotext = tr("Your captured packets will be lost if you don't save them.");
1853                 }
1854             } else {
1855                 // No capture in progress and not a tempfile, so this is not unsaved packets
1856                 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1857                 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1858                 infotext = tr("Your changes will be lost if you don't save them.");
1859                 g_free(display_basename);
1860             }
1861 
1862             msg_dialog.setText(question);
1863             msg_dialog.setInformativeText(infotext);
1864 
1865             // XXX Text comes from ui/gtk/stock_icons.[ch]
1866             // Note that the button roles differ from the GTK+ version.
1867             // Cancel = RejectRole
1868             // Save = AcceptRole
1869             // Don't Save = DestructiveRole
1870             msg_dialog.addButton(QMessageBox::Cancel);
1871 
1872             if (capture_in_progress) {
1873                 QString save_button_text;
1874                 if (context == Restart) {
1875                     save_button_text = tr("Save before Continue");
1876                 } else {
1877                     save_button_text = tr("Stop and Save");
1878                 }
1879                 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1880             } else {
1881                 save_button = msg_dialog.addButton(QMessageBox::Save);
1882             }
1883             msg_dialog.setDefaultButton(save_button);
1884 
1885             QString discard_button_text;
1886             if (capture_in_progress) {
1887                 switch (context) {
1888                 case Quit:
1889                     discard_button_text = tr("Stop and Quit &without Saving");
1890                     break;
1891                 case Restart:
1892                     discard_button_text = tr("Continue &without Saving");
1893                     break;
1894                 default:
1895                     discard_button_text = tr("Stop and Continue &without Saving");
1896                     break;
1897                 }
1898             } else {
1899                 switch (context) {
1900                 case Quit:
1901                     discard_button_text = tr("Quit &without Saving");
1902                     break;
1903                 case Restart:
1904                 default:
1905                     discard_button_text = tr("Continue &without Saving");
1906                     break;
1907                 }
1908             }
1909             discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1910 
1911 #if defined(Q_OS_MAC)
1912             /*
1913              * In macOS, the "default button" is not necessarily the
1914              * button that has the input focus; Enter/Return activates
1915              * the default button, and the spacebar activates the button
1916              * that has the input focus, and they might be different
1917              * buttons.
1918              *
1919              * In a "do you want to save" dialog, for example, the
1920              * "save" button is the default button, and the "don't
1921              * save" button has the input focus, so you can press
1922              * Enter/Return to save or space not to save (or Escape
1923              * to dismiss the dialog).
1924              *
1925              * In Qt terms, this means "no auto-default", as auto-default
1926              * makes the button with the input focus the default button,
1927              * so that Enter/Return will activate it.
1928              */
1929             QList<QAbstractButton *> buttons = msg_dialog.buttons();
1930             for (int i = 0; i < buttons.size(); ++i) {
1931                 QPushButton *button = static_cast<QPushButton *>(buttons.at(i));;
1932                 button->setAutoDefault(false);
1933             }
1934 
1935             /*
1936              * It also means that the "don't save" button should be the one
1937              * initially given the focus.
1938              */
1939             discard_button->setFocus();
1940 #endif
1941 
1942             msg_dialog.exec();
1943             /* According to the Qt doc:
1944              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1945              *
1946              * Therefore we should use clickedButton() to determine which button was clicked. */
1947 
1948             if (msg_dialog.clickedButton() == save_button) {
1949 #ifdef HAVE_LIBPCAP
1950                 /* If there's a capture in progress, we have to stop the capture
1951                    and then do the save. */
1952                 if (capture_in_progress)
1953                     captureStop();
1954 #endif
1955                 /* Save the file and close it */
1956                 // XXX if no packets were captured, any unsaved comments set by
1957                 // the user are silently discarded because capFile() is null.
1958                 if (capture_file_.capFile() && saveCaptureFile(capture_file_.capFile(), true) == false)
1959                     return false;
1960                 do_close_file = true;
1961             } else if (msg_dialog.clickedButton() == discard_button) {
1962                 /* Just close the file, discarding changes */
1963                 do_close_file = true;
1964             } else {
1965                 // cancelButton or some other unspecified button
1966                 return false;
1967             }
1968         } else {
1969             /* Unchanged file or capturing with no packets */
1970             do_close_file = true;
1971         }
1972     } else {
1973         /* User asked not to be bothered by those prompts, just close it.
1974          XXX - should that apply only to saving temporary files? */
1975         do_close_file = true;
1976     }
1977 
1978     /*
1979      * Are we done with this file and should we close the file?
1980      */
1981     if (do_close_file) {
1982 #ifdef HAVE_LIBPCAP
1983         /* If there's a capture in progress, we have to stop the capture
1984            and then do the close. */
1985         if (capture_in_progress)
1986             captureStop();
1987         else if (capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1988             /*
1989              * When an offline capture is being read, mark it as aborted.
1990              * cf_read will be responsible for actually closing the capture.
1991              *
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.
1996              */
1997             capture_file_.capFile()->state = FILE_READ_ABORTED;
1998             return true;
1999         }
2000 #endif
2001         /* Clear MainWindow file name details */
2002         gbl_cur_main_window_->setMwFileName("");
2003 
2004         /* captureStop() will close the file if not having any packets */
2005         if (capture_file_.capFile() && context != Restart && context != Reload)
2006             // Don't really close if Restart or Reload
2007             cf_close(capture_file_.capFile());
2008     }
2009 
2010     return true; /* File closed */
2011 }
2012 
captureStop()2013 void MainWindow::captureStop() {
2014     stopCapture();
2015 
2016     while (capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2017         WiresharkApplication::processEvents();
2018     }
2019 }
2020 
findTextCodecs()2021 void MainWindow::findTextCodecs() {
2022     const QList<int> mibs = QTextCodec::availableMibs();
2023     QRegularExpression ibmRegExp("^IBM([0-9]+).*$");
2024     QRegularExpression iso8859RegExp("^ISO-8859-([0-9]+).*$");
2025     QRegularExpression windowsRegExp("^WINDOWS-([0-9]+).*$");
2026     QRegularExpressionMatch match;
2027     for (int mib : mibs) {
2028         QTextCodec *codec = QTextCodec::codecForMib(mib);
2029         QString key = codec->name().toUpper();
2030         char rank;
2031 
2032         if (key.localeAwareCompare("IBM") < 0) {
2033             rank = 1;
2034         } else if ((match = ibmRegExp.match(key)).hasMatch()) {
2035             rank = match.capturedRef(1).size(); // Up to 5
2036         } else if (key.localeAwareCompare("ISO-8859-") < 0) {
2037             rank = 6;
2038         } else if ((match = iso8859RegExp.match(key)).hasMatch()) {
2039             rank = 6 + match.capturedRef(1).size(); // Up to 6 + 2
2040         } else if (key.localeAwareCompare("WINDOWS-") < 0) {
2041             rank = 9;
2042         } else if ((match = windowsRegExp.match(key)).hasMatch()) {
2043             rank = 9 + match.capturedRef(1).size(); // Up to 9 + 4
2044         } else {
2045             rank = 14;
2046         }
2047         // This doesn't perfectly well order the IBM codecs because it's
2048         // annoying to properly place IBM00858 and IBM00924 in the middle of
2049         // code page numbers not zero padded to 5 digits.
2050         // We could manipulate the key further to have more commonly used
2051         // charsets earlier. IANA MIB ordering would be unxpected:
2052         // https://www.iana.org/assignments/character-sets/character-sets.xml
2053         // For data about use in HTTP (other protocols can be quite different):
2054         // https://w3techs.com/technologies/overview/character_encoding
2055 
2056         key.prepend('0' + rank);
2057         // We use a map here because, due to backwards compatibility,
2058         // the same QTextCodec may be returned for multiple MIBs, which
2059         // happens for GBK/GB2312, EUC-KR/windows-949/UHC, and others.
2060         text_codec_map_.insert(key, codec);
2061     }
2062 }
2063 
initMainToolbarIcons()2064 void MainWindow::initMainToolbarIcons()
2065 {
2066     // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
2067     int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
2068 #if !defined(Q_OS_WIN)
2069     // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
2070     // The macOS HIG specifies 32-pixel icons but they're a little too
2071     // large IMHO.
2072     icon_size = icon_size * 3 / 2;
2073 #endif
2074     main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
2075 
2076     // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
2077     // toolbar item but that clutters up our menu. Set menu icons sparingly.
2078 
2079     main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
2080     main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
2081     main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
2082     main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
2083 
2084     // Menu icons are disabled in main_window.ui for these items.
2085     main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
2086     main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
2087     main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
2088     main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
2089 
2090     main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
2091     main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
2092     main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
2093     main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
2094     main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
2095     main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
2096     main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
2097     main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
2098 #if defined(Q_OS_MAC)
2099     main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
2100     main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
2101 #endif
2102     main_ui_->actionGoPreviousHistoryPacket->setIcon(StockIcon("go-previous"));
2103     main_ui_->actionGoNextHistoryPacket->setIcon(StockIcon("go-next"));
2104     main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
2105 
2106     main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
2107 
2108     QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
2109     zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
2110     main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
2111     main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
2112     main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
2113     main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
2114     main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
2115 
2116     main_ui_->actionNewDisplayFilterExpression->setIcon(StockIcon("list-add"));
2117 }
2118 
initShowHideMainWidgets()2119 void MainWindow::initShowHideMainWidgets()
2120 {
2121     if (show_hide_actions_) {
2122         return;
2123     }
2124 
2125     show_hide_actions_ = new QActionGroup(this);
2126     QMap<QAction *, QWidget *> shmw_actions;
2127 
2128     show_hide_actions_->setExclusive(false);
2129     shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
2130     shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
2131 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
2132     shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
2133 #endif
2134     shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
2135     shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
2136     shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
2137     shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
2138     shmw_actions[main_ui_->actionViewPacketDiagram] = packet_diagram_;
2139 
2140     foreach(QAction *shmwa, shmw_actions.keys()) {
2141         shmwa->setData(QVariant::fromValue(shmw_actions[shmwa]));
2142         show_hide_actions_->addAction(shmwa);
2143     }
2144 
2145     // Initial hide the Interface Toolbar submenu
2146     main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
2147 
2148     /* Initially hide the additional toolbars menus */
2149     main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
2150 
2151     connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
2152 }
2153 
initTimeDisplayFormatMenu()2154 void MainWindow::initTimeDisplayFormatMenu()
2155 {
2156     if (time_display_actions_) {
2157         return;
2158     }
2159 
2160     time_display_actions_ = new QActionGroup(this);
2161 
2162     td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
2163     td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
2164     td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
2165     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
2166     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
2167     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
2168     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
2169     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
2170     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
2171     td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
2172 
2173     foreach(QAction* tda, td_actions.keys()) {
2174         tda->setData(QVariant::fromValue(td_actions[tda]));
2175         time_display_actions_->addAction(tda);
2176     }
2177 
2178     connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
2179 }
2180 
initTimePrecisionFormatMenu()2181 void MainWindow::initTimePrecisionFormatMenu()
2182 {
2183     if (time_precision_actions_) {
2184         return;
2185     }
2186 
2187     time_precision_actions_ = new QActionGroup(this);
2188 
2189     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
2190     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
2191     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
2192     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
2193     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
2194     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
2195     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
2196 
2197     foreach(QAction* tpa, tp_actions.keys()) {
2198         tpa->setData(QVariant::fromValue(tp_actions[tpa]));
2199         time_precision_actions_->addAction(tpa);
2200     }
2201 
2202     connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
2203 }
2204 
2205 // Menu items which will be disabled when we freeze() and whose state will
2206 // be restored when we thaw(). Add to the list as needed.
initFreezeActions()2207 void MainWindow::initFreezeActions()
2208 {
2209     QList<QAction *> freeze_actions = QList<QAction *>()
2210             << main_ui_->actionFileClose
2211             << main_ui_->actionViewReload
2212             << main_ui_->actionEditMarkPacket
2213             << main_ui_->actionEditMarkAllDisplayed
2214             << main_ui_->actionEditUnmarkAllDisplayed
2215             << main_ui_->actionEditIgnorePacket
2216             << main_ui_->actionEditIgnoreAllDisplayed
2217             << main_ui_->actionEditUnignoreAllDisplayed
2218             << main_ui_->actionEditSetTimeReference
2219             << main_ui_->actionEditUnsetAllTimeReferences;
2220 
2221     foreach(QAction *action, freeze_actions) {
2222         freeze_actions_ << QPair<QAction *, bool>(action, false);
2223     }
2224 }
2225 
initConversationMenus()2226 void MainWindow::initConversationMenus()
2227 {
2228     int i;
2229 
2230     QList<QAction *> cc_actions = QList<QAction *>()
2231             << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2
2232             << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4
2233             << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6
2234             << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8
2235             << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10;
2236 
2237     for (GList *conv_filter_list_entry = conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = gxx_list_next(conv_filter_list_entry)) {
2238         // Main menu items
2239         conversation_filter_t* conv_filter = gxx_list_data(conversation_filter_t *, conv_filter_list_entry);
2240         ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter);
2241         main_ui_->menuConversationFilter->addAction(conv_action);
2242 
2243         connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2244         connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()));
2245 
2246         // Packet list context menu items
2247         packet_list_->conversationMenu()->addAction(conv_action);
2248 
2249         QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text());
2250         i = 1;
2251 
2252         foreach(QAction *cc_action, cc_actions) {
2253             conv_action = new ConversationAction(submenu, conv_filter);
2254             conv_action->setText(cc_action->text());
2255             conv_action->setIcon(cc_action->icon());
2256             conv_action->setColorNumber(i++);
2257             submenu->addAction(conv_action);
2258             connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2259             connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2260         }
2261 
2262         conv_action = new ConversationAction(submenu, conv_filter);
2263         conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2264         submenu->addAction(conv_action);
2265         connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2266         connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2267 
2268         // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
2269         // We should probably do that here.
2270     }
2271 
2272     // Proto tree colorization items
2273     i = 1;
2274     ColorizeAction *colorize_action;
2275     foreach(QAction *cc_action, cc_actions) {
2276         colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2277         colorize_action->setText(cc_action->text());
2278         colorize_action->setIcon(cc_action->icon());
2279         colorize_action->setColorNumber(i++);
2280         proto_tree_->colorizeMenu()->addAction(colorize_action);
2281         connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2282         connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2283     }
2284 
2285     colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2286     colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2287     proto_tree_->colorizeMenu()->addAction(colorize_action);
2288     connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2289     connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2290 }
2291 
addExportObjectsMenuItem(const void *,void * value,void * userdata)2292 gboolean MainWindow::addExportObjectsMenuItem(const void *, void *value, void *userdata)
2293 {
2294     register_eo_t *eo = (register_eo_t*)value;
2295     MainWindow *window = (MainWindow*)userdata;
2296 
2297     ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo);
2298     window->main_ui_->menuFileExportObjects->addAction(export_action);
2299 
2300     //initially disable until a file is loaded (then file signals will take over)
2301     export_action->setEnabled(false);
2302 
2303     connect(&window->capture_file_, SIGNAL(captureEvent(CaptureEvent)), export_action, SLOT(captureFileEvent(CaptureEvent)));
2304     connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject()));
2305     return FALSE;
2306 }
2307 
initExportObjectsMenus()2308 void MainWindow::initExportObjectsMenus()
2309 {
2310     eo_iterate_tables(addExportObjectsMenuItem, this);
2311 }
2312 
2313 // Titlebar
setTitlebarForCaptureFile()2314 void MainWindow::setTitlebarForCaptureFile()
2315 {
2316     if (capture_file_.capFile() && capture_file_.capFile()->filename) {
2317         setWSWindowTitle(QString("[*]%1").arg(capture_file_.fileDisplayName()));
2318         //
2319         // XXX - on non-Mac platforms, put in the application
2320         // name?  Or do so only for temporary files?
2321         //
2322         if (!capture_file_.capFile()->is_tempfile) {
2323             //
2324             // Set the file path; that way, for macOS, it'll set the
2325             // "proxy icon".
2326             //
2327             setWindowFilePath(capture_file_.filePath());
2328         }
2329         setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
2330     } else {
2331         /* We have no capture file. */
2332         setWSWindowTitle();
2333     }
2334 }
2335 
replaceWindowTitleVariables(QString title)2336 QString MainWindow::replaceWindowTitleVariables(QString title)
2337 {
2338     title.replace("%P", get_profile_name());
2339     title.replace("%V", get_ws_vcs_version_info());
2340 
2341     if (title.contains("%F")) {
2342         // %F is file path of the capture file.
2343         if (capture_file_.capFile()) {
2344             // get_dirname() will overwrite the argument so make a copy first
2345             char *filename = g_strdup(capture_file_.capFile()->filename);
2346             QString file(get_dirname(filename));
2347             g_free(filename);
2348 #ifndef _WIN32
2349             // Substitute HOME with ~
2350             QString homedir(g_getenv("HOME"));
2351             if (!homedir.isEmpty()) {
2352                 homedir.remove(QRegExp("[/]+$"));
2353                 file.replace(homedir, "~");
2354             }
2355 #endif
2356             title.replace("%F", file);
2357         } else {
2358             // No file loaded, no folder name
2359             title.remove("%F");
2360         }
2361     }
2362 
2363     if (title.contains("%S")) {
2364         // %S is a conditional separator (" - ") that only shows when surrounded by variables
2365         // with values or static text. Remove repeating, leading and trailing separators.
2366         title.replace(QRegExp("(%S)+"), "%S");
2367         title.remove(QRegExp("^%S|%S$"));
2368 #ifdef __APPLE__
2369         // On macOS we separate with a unicode em dash
2370         title.replace("%S", " " UTF8_EM_DASH " ");
2371 #else
2372         title.replace("%S", " - ");
2373 #endif
2374     }
2375 
2376     return title;
2377 }
2378 
setWSWindowTitle(QString title)2379 void MainWindow::setWSWindowTitle(QString title)
2380 {
2381     if (title.isEmpty()) {
2382         title = tr("The Wireshark Network Analyzer");
2383     }
2384 
2385     if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
2386         QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
2387         if (custom_title.length() > 0) {
2388             title.prepend(QString("[%1] ").arg(custom_title));
2389         }
2390     }
2391 
2392     if (prefs.gui_window_title && prefs.gui_window_title[0]) {
2393         QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
2394         if (custom_title.length() > 0) {
2395 #ifdef __APPLE__
2396             // On macOS we separate the titles with a unicode em dash
2397             title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
2398 #else
2399             title.append(QString(" [%1]").arg(custom_title));
2400 #endif
2401         }
2402     }
2403 
2404     setWindowTitle(title);
2405     setWindowFilePath(NULL);
2406 }
2407 
setTitlebarForCaptureInProgress()2408 void MainWindow::setTitlebarForCaptureInProgress()
2409 {
2410     if (capture_file_.capFile()) {
2411         setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2412     } else {
2413         /* We have no capture in progress. */
2414         setWSWindowTitle();
2415     }
2416 }
2417 
2418 // Menu state
2419 
2420 /* Enable or disable menu items based on whether you have a capture file
2421    you've finished reading and, if you have one, whether it's been saved
2422    and whether it could be saved except by copying the raw packet data. */
setMenusForCaptureFile(bool force_disable)2423 void MainWindow::setMenusForCaptureFile(bool force_disable)
2424 {
2425     bool enable = true;
2426     bool can_write = false;
2427     bool can_save = false;
2428     bool can_save_as = false;
2429 
2430     if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2431         /* We have no capture file or we're currently reading a file */
2432         enable = false;
2433     } else {
2434         /* We have a capture file. Can we write or save? */
2435         can_write = cf_can_write_with_wiretap(capture_file_.capFile());
2436         can_save = cf_can_save(capture_file_.capFile());
2437         can_save_as = cf_can_save_as(capture_file_.capFile());
2438     }
2439 
2440     main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
2441     main_ui_->actionFileMerge->setEnabled(can_write);
2442     main_ui_->actionFileClose->setEnabled(enable);
2443     main_ui_->actionFileSave->setEnabled(can_save);
2444     main_ui_->actionFileSaveAs->setEnabled(can_save_as);
2445     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
2446     /*
2447      * "Export Specified Packets..." should be available only if
2448      * we can write the file out in at least one format.
2449      */
2450     main_ui_->actionFileExportPackets->setEnabled(can_write);
2451 
2452     main_ui_->actionFileExportAsCArrays->setEnabled(enable);
2453     main_ui_->actionFileExportAsCSV->setEnabled(enable);
2454     main_ui_->actionFileExportAsPDML->setEnabled(enable);
2455     main_ui_->actionFileExportAsPlainText->setEnabled(enable);
2456     main_ui_->actionFileExportAsPSML->setEnabled(enable);
2457     main_ui_->actionFileExportAsJSON->setEnabled(enable);
2458 
2459     main_ui_->actionFileExportPacketBytes->setEnabled(enable);
2460     main_ui_->actionFileExportPDU->setEnabled(enable);
2461     main_ui_->actionFileExportTLSSessionKeys->setEnabled(enable);
2462 
2463     foreach(QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2464         eo_action->setEnabled(enable);
2465     }
2466 
2467     main_ui_->actionViewReload->setEnabled(enable);
2468 
2469 #ifdef HAVE_SOFTWARE_UPDATE
2470     // We might want to enable or disable automatic checks here as well.
2471     update_action_->setEnabled(!can_save);
2472 #endif
2473 }
2474 
setMenusForCaptureInProgress(bool capture_in_progress)2475 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2476     /* Either a capture was started or stopped; in either case, it's not
2477        in the process of stopping, so allow quitting. */
2478 
2479     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2480     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2481 
2482     main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress);
2483     main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress);
2484     main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
2485     main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
2486     main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
2487     main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
2488 
2489     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2490     main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress);
2491     main_ui_->actionFileExportTLSSessionKeys->setEnabled(capture_in_progress);
2492 
2493     foreach(QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2494         eo_action->setEnabled(capture_in_progress);
2495     }
2496 
2497     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2498     main_ui_->actionFileQuit->setEnabled(true);
2499 #ifdef HAVE_SOFTWARE_UPDATE
2500     // We might want to enable or disable automatic checks here as well.
2501     update_action_->setEnabled(!capture_in_progress);
2502 #endif
2503 
2504     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2505 
2506     // XXX Fix packet list heading menu sensitivity
2507     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2508     //                         !capture_in_progress);
2509     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2510     //                         !capture_in_progress);
2511     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2512     //                         !capture_in_progress);
2513 
2514 #ifdef HAVE_LIBPCAP
2515     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2516     main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2517     main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2518     main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2519     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2520     main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2521 #endif /* HAVE_LIBPCAP */
2522 
2523 }
2524 
setMenusForCaptureStopping()2525 void MainWindow::setMenusForCaptureStopping() {
2526     main_ui_->actionFileQuit->setEnabled(false);
2527 #ifdef HAVE_SOFTWARE_UPDATE
2528     update_action_->setEnabled(false);
2529 #endif
2530     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2531 #ifdef HAVE_LIBPCAP
2532     main_ui_->actionCaptureStart->setChecked(false);
2533     main_ui_->actionCaptureStop->setEnabled(false);
2534     main_ui_->actionCaptureRestart->setEnabled(false);
2535 #endif /* HAVE_LIBPCAP */
2536 }
2537 
setForCapturedPackets(bool have_captured_packets)2538 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2539 {
2540     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2541 
2542 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2543 //                         have_captured_packets);
2544 
2545     main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2546     main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2547     main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2548 
2549     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2550     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2551     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2552     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2553     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2554     main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2555     main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2556 
2557     main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2558     main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2559     main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2560     main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2561 
2562     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2563     main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2564     main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2565 }
2566 
setMenusForFileSet(bool enable_list_files)2567 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2568     bool enable_next = fileset_get_next() != NULL && enable_list_files;
2569     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2570 
2571     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2572     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2573     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2574 }
2575 
setWindowIcon(const QIcon & icon)2576 void MainWindow::setWindowIcon(const QIcon &icon) {
2577     wsApp->setWindowIcon(icon);
2578     QMainWindow::setWindowIcon(icon);
2579 }
2580 
updateForUnsavedChanges()2581 void MainWindow::updateForUnsavedChanges() {
2582     setTitlebarForCaptureFile();
2583     setMenusForCaptureFile();
2584 }
2585 
changeEvent(QEvent * event)2586 void MainWindow::changeEvent(QEvent* event)
2587 {
2588     if (0 != event)
2589     {
2590         switch (event->type())
2591         {
2592         case QEvent::LanguageChange:
2593             main_ui_->retranslateUi(this);
2594             // make sure that the "Clear Menu" item is retranslated
2595             wsApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged);
2596             break;
2597         case QEvent::LocaleChange: {
2598             QString locale = QLocale::system().name();
2599             locale.truncate(locale.lastIndexOf('_'));
2600             wsApp->loadLanguage(locale);
2601         }
2602         break;
2603         case QEvent::WindowStateChange:
2604             main_ui_->actionViewFullScreen->setChecked(this->isFullScreen());
2605             break;
2606         default:
2607             break;
2608         }
2609     }
2610     QMainWindow::changeEvent(event);
2611 }
2612 
2613 /* Update main window items based on whether there's a capture in progress. */
setForCaptureInProgress(bool capture_in_progress,bool handle_toolbars,GArray * ifaces)2614 void MainWindow::setForCaptureInProgress(bool capture_in_progress, bool handle_toolbars, GArray *ifaces)
2615 {
2616     setMenusForCaptureInProgress(capture_in_progress);
2617 
2618 #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
2619     wireless_frame_->setCaptureInProgress(capture_in_progress);
2620 #endif
2621 
2622 #ifdef HAVE_LIBPCAP
2623     packet_list_->setCaptureInProgress(capture_in_progress);
2624     packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
2625 
2626 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2627 #endif
2628 
2629     if (handle_toolbars) {
2630         QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
2631         foreach(InterfaceToolbar *toolbar, toolbars) {
2632             if (capture_in_progress) {
2633                 toolbar->startCapture(ifaces);
2634             } else {
2635                 toolbar->stopCapture();
2636             }
2637         }
2638     }
2639 }
2640 
2641 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2642             << REGISTER_ANALYZE_GROUP_UNSORTED
2643             << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2644             << REGISTER_STAT_GROUP_UNSORTED
2645             << REGISTER_STAT_GROUP_GENERIC
2646             << REGISTER_STAT_GROUP_CONVERSATION_LIST
2647             << REGISTER_STAT_GROUP_ENDPOINT_LIST
2648             << REGISTER_STAT_GROUP_RESPONSE_TIME
2649             << REGISTER_STAT_GROUP_RSERPOOL
2650             << REGISTER_STAT_GROUP_TELEPHONY
2651             << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2652             << REGISTER_STAT_GROUP_TELEPHONY_GSM
2653             << REGISTER_STAT_GROUP_TELEPHONY_LTE
2654             << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2655             << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2656             << REGISTER_TOOLS_GROUP_UNSORTED;
2657 
addMenuActions(QList<QAction * > & actions,int menu_group)2658 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2659 {
2660     foreach(QAction *action, actions) {
2661         switch (menu_group) {
2662         case REGISTER_ANALYZE_GROUP_UNSORTED:
2663         case REGISTER_STAT_GROUP_UNSORTED:
2664             main_ui_->menuStatistics->insertAction(
2665                             main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2666                             action);
2667             break;
2668         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2669             main_ui_->menuServiceResponseTime->addAction(action);
2670             break;
2671         case REGISTER_STAT_GROUP_RSERPOOL:
2672             main_ui_->menuRSerPool->addAction(action);
2673             break;
2674         case REGISTER_STAT_GROUP_TELEPHONY:
2675             main_ui_->menuTelephony->addAction(action);
2676             break;
2677         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2678             main_ui_->menuANSI->addAction(action);
2679             break;
2680         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2681             main_ui_->menuGSM->addAction(action);
2682             break;
2683         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2684             main_ui_->menuLTE->addAction(action);
2685             break;
2686         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2687             main_ui_->menuMTP3->addAction(action);
2688             break;
2689         case REGISTER_TOOLS_GROUP_UNSORTED:
2690         {
2691             // Allow the creation of submenus. Mimics the behavor of
2692             // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2693             // and GtkUIManager.
2694             //
2695             // For now we limit the insanity to the "Tools" menu.
2696             QStringList menu_path = action->text().split('/');
2697             QMenu *cur_menu = main_ui_->menuTools;
2698             while (menu_path.length() > 1) {
2699                 QString menu_title = menu_path.takeFirst();
2700                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2701                 if (!submenu) {
2702                     submenu = cur_menu->addMenu(menu_title);
2703                     submenu->setObjectName(menu_title.toLower());
2704                 }
2705                 cur_menu = submenu;
2706             }
2707             action->setText(menu_path.last());
2708             cur_menu->addAction(action);
2709             break;
2710         }
2711         default:
2712 //            qDebug() << "FIX: Add" << action->text() << "to the menu";
2713             break;
2714         }
2715 
2716         // Connect each action type to its corresponding slot. We to
2717         // distinguish various types of actions. Setting their objectName
2718         // seems to work OK.
2719         if (action->objectName() == TapParameterDialog::actionName()) {
2720             connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2721         } else if (action->objectName() == FunnelStatistics::actionName()) {
2722             connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2723         }
2724     }
2725 }
removeMenuActions(QList<QAction * > & actions,int menu_group)2726 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2727 {
2728     foreach(QAction *action, actions) {
2729         switch (menu_group) {
2730         case REGISTER_ANALYZE_GROUP_UNSORTED:
2731         case REGISTER_STAT_GROUP_UNSORTED:
2732             main_ui_->menuStatistics->removeAction(action);
2733             break;
2734         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2735             main_ui_->menuServiceResponseTime->removeAction(action);
2736             break;
2737         case REGISTER_STAT_GROUP_RSERPOOL:
2738             main_ui_->menuRSerPool->removeAction(action);
2739             break;
2740         case REGISTER_STAT_GROUP_TELEPHONY:
2741             main_ui_->menuTelephony->removeAction(action);
2742             break;
2743         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2744             main_ui_->menuANSI->removeAction(action);
2745             break;
2746         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2747             main_ui_->menuGSM->removeAction(action);
2748             break;
2749         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2750             main_ui_->menuLTE->removeAction(action);
2751             break;
2752         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2753             main_ui_->menuMTP3->removeAction(action);
2754             break;
2755         case REGISTER_TOOLS_GROUP_UNSORTED:
2756         {
2757             // Allow removal of submenus.
2758             // For now we limit the insanity to the "Tools" menu.
2759             QStringList menu_path = action->text().split('/');
2760             QMenu *cur_menu = main_ui_->menuTools;
2761             while (menu_path.length() > 1) {
2762                 QString menu_title = menu_path.takeFirst();
2763                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2764                 cur_menu = submenu;
2765             }
2766             cur_menu->removeAction(action);
2767             break;
2768         }
2769         default:
2770 //            qDebug() << "FIX: Remove" << action->text() << "from the menu";
2771             break;
2772         }
2773     }
2774 }
2775 
addDynamicMenus()2776 void MainWindow::addDynamicMenus()
2777 {
2778     // Manual additions
2779     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2780     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2781     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2782     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2783     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2784     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2785 
2786     // Fill in each menu
2787     foreach(register_stat_group_t menu_group, menu_groups) {
2788         QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2789         addMenuActions(actions, menu_group);
2790     }
2791 
2792     // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2793     // We've added a placeholder in order to make sure some menus are visible.
2794     // Hide them as needed.
2795     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2796         main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2797     }
2798     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2799         main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2800     }
2801     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2802         main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2803     }
2804     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2805         main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2806     }
2807 }
2808 
reloadDynamicMenus()2809 void MainWindow::reloadDynamicMenus()
2810 {
2811     foreach(register_stat_group_t menu_group, menu_groups) {
2812         QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2813         removeMenuActions(actions, menu_group);
2814 
2815         actions = wsApp->addedMenuGroupItems(menu_group);
2816         addMenuActions(actions, menu_group);
2817     }
2818 
2819     wsApp->clearAddedMenuGroupItems();
2820     wsApp->clearRemovedMenuGroupItems();
2821 }
2822 
externalMenuHelper(ext_menu_t * menu,QMenu * subMenu,gint depth)2823 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu  * subMenu, gint depth)
2824 {
2825     QAction * itemAction = Q_NULLPTR;
2826     ext_menubar_t * item = Q_NULLPTR;
2827     GList * children = Q_NULLPTR;
2828 
2829     /* There must exists an xpath parent */
2830     Q_ASSERT(subMenu != NULL);
2831 
2832     /* If the depth counter exceeds, something must have gone wrong */
2833     Q_ASSERT(depth < EXT_MENUBAR_MAX_DEPTH);
2834 
2835     children = menu->children;
2836     /* Iterate the child entries */
2837     while (children && children->data) {
2838         item = gxx_list_data(ext_menubar_t *, children);
2839 
2840         if (item->type == EXT_MENUBAR_MENU) {
2841             /* Handle Submenu entry */
2842             this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2843         } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2844             subMenu->addSeparator();
2845         } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2846             itemAction = subMenu->addAction(item->name);
2847             itemAction->setData(QVariant::fromValue(static_cast<void *>(item)));
2848             itemAction->setText(item->label);
2849             connect(itemAction, SIGNAL(triggered()),
2850                     this, SLOT(externalMenuItem_triggered()));
2851         }
2852 
2853         /* Iterate Loop */
2854         children = gxx_list_next(children);
2855     }
2856 }
2857 
searchSubMenu(QString objectName)2858 QMenu * MainWindow::searchSubMenu(QString objectName)
2859 {
2860     QList<QMenu*> lst;
2861 
2862     if (objectName.length() > 0) {
2863         QString searchName = QString("menu") + objectName;
2864 
2865         lst = main_ui_->menuBar->findChildren<QMenu*>();
2866         foreach(QMenu* m, lst) {
2867             if (QString::compare(m->objectName(), searchName) == 0)
2868                 return m;
2869         }
2870     }
2871 
2872     return 0;
2873 }
2874 
addPluginIFStructures()2875 void MainWindow::addPluginIFStructures()
2876 {
2877     GList *user_menu = ext_menubar_get_entries();
2878 
2879     while (user_menu && user_menu->data) {
2880         QMenu *subMenu = Q_NULLPTR;
2881         ext_menu_t *menu = gxx_list_data(ext_menu_t *, user_menu);
2882 
2883         /* On this level only menu items should exist. Not doing an assert here,
2884          * as it could be an honest mistake */
2885         if (menu->type != EXT_MENUBAR_MENU) {
2886             user_menu = gxx_list_next(user_menu);
2887             continue;
2888         }
2889 
2890         /* Create main submenu and add it to the menubar */
2891         if (menu->parent_menu) {
2892             QMenu *sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2893             if (sortUnderneath)
2894                 subMenu = sortUnderneath->addMenu(menu->label);
2895         }
2896 
2897         if (!subMenu)
2898             subMenu = main_ui_->menuBar->addMenu(menu->label);
2899 
2900         /* This will generate the action structure for each menu. It is recursive,
2901          * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2902         this->externalMenuHelper(menu, subMenu, 0);
2903 
2904         /* Iterate Loop */
2905         user_menu = gxx_list_next(user_menu);
2906     }
2907 
2908     int cntToolbars = 0;
2909 
2910     QMenu *tbMenu = main_ui_->menuAdditionalToolbars;
2911     GList *if_toolbars = ext_toolbar_get_entries();
2912     while (if_toolbars && if_toolbars->data) {
2913         ext_toolbar_t *toolbar = gxx_list_data(ext_toolbar_t*, if_toolbars);
2914 
2915         if (toolbar->type != EXT_TOOLBAR_BAR) {
2916             if_toolbars = gxx_list_next(if_toolbars);
2917             continue;
2918         }
2919 
2920         bool visible = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, reinterpret_cast<GCompareFunc>(strcmp)) ? true : false;
2921 
2922         AdditionalToolBar *ifToolBar = AdditionalToolBar::create(this, toolbar);
2923 
2924         if (ifToolBar) {
2925             ifToolBar->setVisible(visible);
2926 
2927             QAction *iftbAction = new QAction(QString(toolbar->name), this);
2928             iftbAction->setToolTip(toolbar->tooltip);
2929             iftbAction->setEnabled(true);
2930             iftbAction->setCheckable(true);
2931             iftbAction->setChecked(visible);
2932             iftbAction->setToolTip(tr("Show or hide the toolbar"));
2933             iftbAction->setData(VariantPointer<ext_toolbar_t>::asQVariant(toolbar));
2934 
2935             QAction *before = Q_NULLPTR;
2936 
2937             foreach(QAction *action, tbMenu->actions()) {
2938                 /* Ensure we add the menu entries in sorted order */
2939                 if (action->text().compare(toolbar->name, Qt::CaseInsensitive) > 0) {
2940                     before = action;
2941                     break;
2942                 }
2943             }
2944 
2945             tbMenu->insertAction(before, iftbAction);
2946 
2947             addToolBar(Qt::TopToolBarArea, ifToolBar);
2948             insertToolBarBreak(ifToolBar);
2949 
2950             if (show_hide_actions_)
2951                 show_hide_actions_->addAction(iftbAction);
2952 
2953             cntToolbars++;
2954         }
2955 
2956         if_toolbars = gxx_list_next(if_toolbars);
2957     }
2958 
2959     if (cntToolbars)
2960         tbMenu->menuAction()->setVisible(true);
2961 }
2962 
removeAdditionalToolbar(QString toolbarName)2963 void MainWindow::removeAdditionalToolbar(QString toolbarName)
2964 {
2965     if (toolbarName.length() == 0)
2966         return;
2967 
2968     QList<QToolBar *> toolbars = findChildren<QToolBar *>();
2969     foreach(QToolBar *tb, toolbars) {
2970         AdditionalToolBar *ifToolBar = dynamic_cast<AdditionalToolBar *>(tb);
2971 
2972         if (ifToolBar && ifToolBar->menuName().compare(toolbarName)) {
2973             GList *entry = g_list_find_custom(recent.gui_additional_toolbars, qUtf8Printable(ifToolBar->menuName()), reinterpret_cast<GCompareFunc>(strcmp));
2974             if (entry) {
2975                 recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data);
2976             }
2977             QList<QAction *> actions = main_ui_->menuAdditionalToolbars->actions();
2978             foreach(QAction *action, actions) {
2979                 ext_toolbar_t *item = VariantPointer<ext_toolbar_t>::asPtr(action->data());
2980                 if (item && ifToolBar->menuName().compare(item->name)) {
2981                     if (show_hide_actions_)
2982                         show_hide_actions_->removeAction(action);
2983                     main_ui_->menuAdditionalToolbars->removeAction(action);
2984                 }
2985             }
2986             break;
2987         }
2988     }
2989 
2990 }
2991 
getMwFileName()2992 QString MainWindow::getMwFileName()
2993 {
2994     return mwFileName_;
2995 }
2996 
setMwFileName(QString fileName)2997 void MainWindow::setMwFileName(QString fileName)
2998 {
2999     mwFileName_ = fileName;
3000     return;
3001 }
3002 
hasSelection()3003 bool MainWindow::hasSelection()
3004 {
3005     if (packet_list_)
3006         return packet_list_->multiSelectActive();
3007     return false;
3008 }
3009 
selectedRows(bool useFrameNum)3010 QList<int> MainWindow::selectedRows(bool useFrameNum)
3011 {
3012     if (packet_list_)
3013         return packet_list_->selectedRows(useFrameNum);
3014     return QList<int>();
3015 }
3016 
frameDataForRow(int row) const3017 frame_data * MainWindow::frameDataForRow(int row) const
3018 {
3019     if (packet_list_)
3020         return packet_list_->getFDataForRow(row);
3021 
3022     return Q_NULLPTR;
3023 }
3024 
3025 // Finds rtp id for selected stream and adds it to stream_ids
3026 // If reverse is set, tries to find reverse stream too
3027 // Return error string if error happens
3028 //
3029 // Note: Caller must free each returned rtpstream_info_t
findRtpStreams(QVector<rtpstream_id_t * > * stream_ids,bool reverse)3030 QString MainWindow::findRtpStreams(QVector<rtpstream_id_t *> *stream_ids, bool reverse)
3031 {
3032     rtpstream_tapinfo_t tapinfo;
3033     rtpstream_id_t *fwd_id, *rev_id;
3034     bool fwd_id_used, rev_id_used;
3035     const gchar filter_text[] = "rtp && rtp.version == 2 && rtp.ssrc && (ip || ipv6)";
3036     dfilter_t *sfcode;
3037     gchar *err_msg;
3038 
3039     /* Try to get the hfid for "rtp.ssrc". */
3040     int hfid_rtp_ssrc = proto_registrar_get_id_byname("rtp.ssrc");
3041     if (hfid_rtp_ssrc == -1) {
3042         return tr("There is no \"rtp.ssrc\" field in this version of Wireshark.");
3043     }
3044 
3045     /* Try to compile the filter. */
3046     if (!dfilter_compile(filter_text, &sfcode, &err_msg)) {
3047         QString err = QString(err_msg);
3048         g_free(err_msg);
3049         return err;
3050     }
3051 
3052     if (!capture_file_.capFile() || !capture_file_.capFile()->current_frame) close();
3053 
3054     if (!cf_read_current_record(capture_file_.capFile())) close();
3055 
3056     frame_data *fdata = capture_file_.capFile()->current_frame;
3057 
3058     epan_dissect_t edt;
3059 
3060     epan_dissect_init(&edt, capture_file_.capFile()->epan, true, false);
3061     epan_dissect_prime_with_dfilter(&edt, sfcode);
3062     epan_dissect_prime_with_hfid(&edt, hfid_rtp_ssrc);
3063     epan_dissect_run(&edt, capture_file_.capFile()->cd_t,
3064                      &capture_file_.capFile()->rec,
3065                      frame_tvbuff_new_buffer(
3066                          &capture_file_.capFile()->provider, fdata,
3067                          &capture_file_.capFile()->buf),
3068                      fdata, NULL);
3069 
3070     /*
3071      * Packet must be an RTPv2 packet with an SSRC; we use the filter to
3072      * check.
3073      */
3074     if (!dfilter_apply_edt(sfcode, &edt)) {
3075         epan_dissect_cleanup(&edt);
3076         dfilter_free(sfcode);
3077         return tr("Please select an RTPv2 packet with an SSRC value");
3078     }
3079 
3080     dfilter_free(sfcode);
3081 
3082     /* We need the SSRC value of the current frame; try to get it. */
3083     GPtrArray *gp = proto_get_finfo_ptr_array(edt.tree, hfid_rtp_ssrc);
3084     if (gp == NULL || gp->len == 0) {
3085         /* XXX - should not happen, as the filter includes rtp.ssrc */
3086         epan_dissect_cleanup(&edt);
3087         return tr("SSRC value not found.");
3088     }
3089 
3090     /*
3091      * OK, we have the SSRC value, so we can proceed.
3092      * Allocate RTP stream ID structures.
3093      */
3094     fwd_id = g_new0(rtpstream_id_t, 1);
3095     fwd_id_used = false;
3096     rev_id = g_new0(rtpstream_id_t, 1);
3097     rev_id_used = false;
3098 
3099     /* Get the IP and port values for the forward direction. */
3100     rtpstream_id_copy_pinfo(&(edt.pi), fwd_id, false);
3101 
3102     /* assume the inverse ip/port combination for the reverse direction */
3103     rtpstream_id_copy_pinfo(&(edt.pi), rev_id, true);
3104 
3105     /* Save the SSRC value for the forward direction. */
3106     fwd_id->ssrc = fvalue_get_uinteger(&((field_info *)gp->pdata[0])->value);
3107 
3108     epan_dissect_cleanup(&edt);
3109 
3110     /* Register the tap listener */
3111     memset(&tapinfo, 0, sizeof(rtpstream_tapinfo_t));
3112     tapinfo.tap_data = this;
3113     tapinfo.mode = TAP_ANALYSE;
3114 
3115     /* Scan for RTP streams (redissect all packets) */
3116     rtpstream_scan(&tapinfo, capture_file_.capFile(), Q_NULLPTR);
3117 
3118     for (GList *strinfo_list = g_list_first(tapinfo.strinfo_list); strinfo_list; strinfo_list = gxx_list_next(strinfo_list)) {
3119         rtpstream_info_t * strinfo = gxx_list_data(rtpstream_info_t*, strinfo_list);
3120         if (rtpstream_id_equal(&(strinfo->id), fwd_id,RTPSTREAM_ID_EQUAL_NONE))
3121         {
3122             *stream_ids << fwd_id;
3123             fwd_id_used = true;
3124         }
3125 
3126         if (rtpstream_id_equal(&(strinfo->id), rev_id,RTPSTREAM_ID_EQUAL_NONE))
3127         {
3128             if (rev_id->ssrc == 0) {
3129                 rev_id->ssrc = strinfo->id.ssrc;
3130             }
3131             if (reverse) {
3132                 *stream_ids << rev_id;
3133                 rev_id_used = true;
3134             }
3135         }
3136     }
3137 
3138     //
3139     // XXX - is it guaranteed that fwd_id and rev_id were both added to
3140     // *stream_ids?  If so, this isn't necessary.
3141     //
3142     if (!fwd_id_used) {
3143         rtpstream_id_free(fwd_id);
3144     }
3145     if (!rev_id_used) {
3146         rtpstream_id_free(rev_id);
3147     }
3148     return NULL;
3149 }
3150 
3151