Bug Summary

File:builds/wireshark/wireshark/ui/qt/main_application.cpp
Warning:line 472, column 13
Potential memory leak

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name main_application.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /builds/wireshark/wireshark/ui/qt/lua_debugger -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6/QtSvg -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_SVG_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-10-23-100316-3661-1 -x c++ /builds/wireshark/wireshark/ui/qt/main_application.cpp
1/* main_application.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <[email protected]>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10// warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4267)
14#endif
15
16#define WS_LOG_DOMAIN"Main" LOG_DOMAIN_MAIN"Main"
17
18#include "main_application.h"
19
20#include <algorithm>
21#include <errno(*__errno_location ()).h>
22
23#include "wsutil/filesystem.h"
24#include "app/application_flavor.h"
25
26#include "epan/addr_resolv.h"
27#include "epan/prefs.h"
28#include "epan/proto.h"
29#include "epan/tap.h"
30#include "epan/timestamp.h"
31#include "epan/dfilter/dfilter-macro.h"
32
33#include "ui/commandline.h"
34#include "ui/preference_utils.h"
35#include "ui/language.h"
36#include "ui/recent.h"
37#include "ui/simple_dialog.h"
38#include "ui/util.h"
39
40#include <ui/qt/utils/qt_ui_utils.h>
41#include <ui/qt/utils/color_utils.h>
42#include <ui/qt/utils/software_update.h>
43#include <ui/qt/utils/theme_manager.h>
44
45#include "epan/color_filters.h"
46#include "epan/tag_rules.h"
47
48#include "extcap.h"
49#ifdef HAVE_LIBPCAP1
50#include <capture/iface_monitor.h>
51#endif
52
53#include "ui/capture_globals.h"
54#include "ui/recent_utils.h"
55
56#include "wsutil/utf8_entities.h"
57
58#ifdef _WIN32
59# include "wsutil/file_util.h"
60# include <QMessageBox>
61# include <QSettings>
62#endif /* _WIN32 */
63
64#include <ui/qt/capture_file.h>
65
66#include <ui/qt/main_window.h>
67#include <ui/qt/manager/interface_list_manager.h>
68#include <ui/qt/main_status_bar.h>
69#include <ui/qt/utils/workspace_state.h>
70#include <ui/qt/utils/theme_styler.h>
71
72#include <QAction>
73#include <QApplication>
74#include <QColorDialog>
75#include <QDesktopServices>
76#include <QDir>
77#include <QEvent>
78#include <QFileOpenEvent>
79#include <QFontInfo>
80#include <QFontMetrics>
81#include <QLibraryInfo>
82#include <QLocale>
83#include <QMainWindow>
84#include <QMutableListIterator>
85#include <QProxyStyle>
86#include <QSocketNotifier>
87#include <QThreadPool>
88#include <QUrl>
89#include <qmath.h>
90
91#include <QMimeDatabase>
92
93#include <QStyleHints>
94
95#if QT_VERSION((6<<16)|(10<<8)|(2)) >= QT_VERSION_CHECK(6, 5, 0)((6<<16)|(5<<8)|(0)) && defined(Q_OS_WIN)
96#include <QStyleFactory>
97#endif
98
99#ifdef _MSC_VER
100#pragma warning(pop)
101#endif
102
103MainApplication *mainApp;
104
105// XXX - Copied from ui/gtk/file_dlg.c
106
107static QHash<int, QList<QAction *> > dynamic_menu_groups_;
108static QHash<int, QList<QAction *> > added_menu_groups_;
109static QHash<int, QList<QAction *> > removed_menu_groups_;
110
111QString MainApplication::window_title_separator_ = QString::fromUtf8(" " UTF8_MIDDLE_DOT"\u00b7" " ");
112
113// QMimeDatabase parses a large-ish XML file and can be slow to initialize.
114// Do so in a worker thread as early as possible.
115// https://github.com/lxde/pcmanfm-qt/issues/415
116class MimeDatabaseInitThread : public QRunnable
117{
118private:
119 void run() override
120 {
121 QMimeDatabase mime_db;
122 mime_db.mimeTypeForData(QByteArray());
123 }
124};
125
126void
127topic_action(topic_action_e action)
128{
129 if (mainApp) mainApp->helpTopicAction(action);
130}
131
132/* write all capture filenames of the menu to the user's recent file */
133extern "C" void menu_recent_file_write_all(FILE *rf) {
134
135 const QList<RecentFileInfo>& recentFiles = WorkspaceState::instance()->recentCaptureFiles();
136 int rFSize = static_cast<int>(recentFiles.size());
137 for (int i = 0; i < rFSize; i++) {
138 const RecentFileInfo& rfi = recentFiles.at(i);
139
140 QString cf_name = rfi.filename;
141 if (!cf_name.isNull()) {
142 fprintf (rf, RECENT_KEY_CAPTURE_FILE"recent.capture_file" ": %s\n", qUtf8Printable(cf_name)QtPrivate::asString(cf_name).toUtf8().constData());
143 }
144 }
145}
146
147void MainApplication::refreshPacketData()
148{
149 if (host_name_lookup_process()) {
150 emit addressResolutionChanged();
151 } else if (col_data_changed()) {
152 emit columnDataChanged();
153 }
154}
155
156void MainApplication::updateTaps()
157{
158 draw_tap_listeners(false);
159}
160
161QDir MainApplication::openDialogInitialDir() {
162 return QDir(get_open_dialog_initial_dir());
163}
164
165void MainApplication::setLastOpenDirFromFilename(const QString file_name)
166{
167 /* XXX - Use canonicalPath() instead of absolutePath()? */
168 QString directory = QDir::toNativeSeparators(QFileInfo(file_name).absolutePath());
169 /* XXX - printable? */
170 set_last_open_dir(qUtf8Printable(directory)QtPrivate::asString(directory).toUtf8().constData());
171}
172
173void MainApplication::helpTopicAction(topic_action_e action)
174{
175 QString url = gchar_free_to_qstring(topic_action_url(action));
176
177 if (!url.isEmpty()) {
178 QDesktopServices::openUrl(QUrl(QDir::fromNativeSeparators(url)));
179 }
180}
181
182void MainApplication::setConfigurationProfile(const char *profile_name, bool write_recent_file)
183{
184 char *rf_path;
185 int rf_open_errno;
186 char *err_msg = NULL__null;
187 const char* env_prefix = application_configuration_environment_prefix();
188
189 /* First check if profile exists */
190 if (!profile_exists(env_prefix, profile_name, false)) {
191 if (profile_exists(env_prefix, profile_name, true)) {
192 char *pf_dir_path, *pf_dir_path2, *pf_filename;
193 /* Copy from global profile */
194 if (create_persconffile_profile(env_prefix, profile_name, &pf_dir_path) == -1) {
195 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK0x01,
196 "Can't create directory\n\"%s\":\n%s.",
197 pf_dir_path, g_strerror(errno(*__errno_location ())));
198
199 g_free(pf_dir_path)(__builtin_object_size ((pf_dir_path), 0) != ((size_t) - 1)) ?
g_free_sized (pf_dir_path, __builtin_object_size ((pf_dir_path
), 0)) : (g_free) (pf_dir_path)
;
200 }
201
202 if (copy_persconffile_profile(env_prefix, profile_name, profile_name, true, &pf_filename,
203 &pf_dir_path, &pf_dir_path2) == -1) {
204 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK0x01,
205 "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
206 pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno(*__errno_location ())));
207
208 g_free(pf_filename)(__builtin_object_size ((pf_filename), 0) != ((size_t) - 1)) ?
g_free_sized (pf_filename, __builtin_object_size ((pf_filename
), 0)) : (g_free) (pf_filename)
;
209 g_free(pf_dir_path)(__builtin_object_size ((pf_dir_path), 0) != ((size_t) - 1)) ?
g_free_sized (pf_dir_path, __builtin_object_size ((pf_dir_path
), 0)) : (g_free) (pf_dir_path)
;
210 g_free(pf_dir_path2)(__builtin_object_size ((pf_dir_path2), 0) != ((size_t) - 1))
? g_free_sized (pf_dir_path2, __builtin_object_size ((pf_dir_path2
), 0)) : (g_free) (pf_dir_path2)
;
211 }
212 } else {
213 /* No personal and no global profile exists */
214 return;
215 }
216 }
217
218 /* Then check if changing to another profile */
219 if (profile_name && strcmp (profile_name, get_profile_name()) == 0) {
220 return;
221 }
222
223 /* Get the current geometry, before writing it to disk */
224 emit profileChanging();
225
226 if (write_recent_file && profile_exists(env_prefix, get_profile_name(), false))
227 {
228 /* Write recent file for profile we are leaving, if it still exists */
229 write_profile_recent();
230 }
231
232 // Freeze the packet list early to avoid updating column data before doing a
233 // full redissection. The packet list will be thawed when redissection is done.
234 emit freezePacketList(true);
235
236 /* Set profile name and update the status bar */
237 set_profile_name (profile_name);
238 emit profileNameChanged(profile_name);
239
240 /* Apply new preferences */
241 readConfigurationFiles(true);
242
243 /* Reading the new profile's preferences reset every registered preference,
244 * including the ones that are shared by all of our profiles. Those aren't
245 * part of the profile we're leaving, so read them back in. */
246 extcap_read_preferences();
247
248 /* Apply command-line preferences */
249 commandline_options_reapply();
250 extcap_register_preferences(NULL__null, NULL__null);
251
252 /* Switching profile requires reloading the macro list. */
253 reloadDisplayFilterMacros();
254
255 if (!recent_read_profile_static(&rf_path, &rf_open_errno)) {
256 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK0x01,
257 "Could not open common recent file\n\"%s\": %s.",
258 rf_path, g_strerror(rf_open_errno));
259 g_free(rf_path)(__builtin_object_size ((rf_path), 0) != ((size_t) - 1)) ? g_free_sized
(rf_path, __builtin_object_size ((rf_path), 0)) : (g_free) (
rf_path)
;
260 }
261 if (recent.gui_fileopen_remembered_dir &&
262 test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR21) {
263 set_last_open_dir(recent.gui_fileopen_remembered_dir);
264 }
265 timestamp_set_type(recent.gui_time_format);
266 timestamp_set_precision(recent.gui_time_precision);
267 timestamp_set_seconds_type (recent.gui_seconds_format);
268
269 prefs_to_capture_opts(&global_capture_opts);
270 prefs_apply_all();
271#ifdef HAVE_LIBPCAP1
272 /* Re-apply interface display attributes from the new profile's prefs before
273 the preferencesChanged() emit below, so its listeners see fresh data. The
274 manager owns interface enumeration/attributes now. */
275 if (MainWindow *mw = mainWindow())
276 if (InterfaceListManager *mgr = mw->interfaceListManager())
277 mgr->reapplyInterfacePreferences();
278#endif
279
280 emit columnsChanged();
281 emit preferencesChanged();
282 emit recentPreferencesRead();
283 emit filterExpressionsChanged();
284 emit checkDisplayFilter();
285 emit captureFilterListChanged();
286 emit displayFilterListChanged();
287
288 /* Reload color filters */
289 if (!color_filters_reload(&err_msg, color_filter_add_cb, application_configuration_environment_prefix())) {
290 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK0x01, "%s", err_msg);
291 g_free(err_msg)(__builtin_object_size ((err_msg), 0) != ((size_t) - 1)) ? g_free_sized
(err_msg, __builtin_object_size ((err_msg), 0)) : (g_free) (
err_msg)
;
292 }
293
294 /* Reload tag rules (profile-specific file changes; global file stays the same) */
295 tag_rules_reload();
296
297 /* Capture-interface prefs are now watched by InterfaceListManager, which
298 rescans when capture_no_interface_load / capture_no_extcap flips. A profile
299 switch can also change interface display attributes, so notify subscribers
300 (the manager owns the interface-list-changed signal now). */
301 MainWindow *mw = mainWindow();
302 if (mw && mw->interfaceListManager())
303 mw->interfaceListManager()->notifyListChanged();
304 emit packetDissectionChanged();
305
306 /* Write recent_common file to ensure last used profile setting is stored. */
307 write_recent();
308}
309
310void MainApplication::reloadLuaPluginsDelayed()
311{
312 QTimer::singleShot(0, this, [this]() {
313 /* Clear the reloading flag so the re-triggered reload
314 * is not blocked by the isReloadingLua() guard. */
315 setReloadingLua(false);
316 emit reloadLuaPlugins();
317 });
318}
319
320const QIcon &MainApplication::normalIcon()
321{
322 if (normal_icon_.isNull()) {
323 initializeIcons();
324 }
325 return normal_icon_;
326}
327
328const QIcon &MainApplication::captureIcon()
329{
330 if (capture_icon_.isNull()) {
331 initializeIcons();
332 }
333 return capture_icon_;
334}
335
336const QString MainApplication::windowTitleString(QStringList title_parts)
337{
338 QMutableStringListIterator tii(title_parts);
339 while (tii.hasNext()) {
340 QString ti = tii.next();
341 if (ti.isEmpty()) tii.remove();
342 }
343 title_parts.prepend(applicationName());
344 return title_parts.join(window_title_separator_);
345}
346
347void MainApplication::applyCustomColorsFromRecent()
348{
349 int i = 0;
350 bool ok;
351 for (GList *custom_color = recent.custom_colors; custom_color; custom_color = custom_color->next) {
352 QRgb rgb = QString((const char *)custom_color->data).toUInt(&ok, 16);
353 if (ok) {
354 QColorDialog::setCustomColor(i++, QColor(rgb));
355 }
356 }
357}
358
359// Return the first top-level MainWindow.
360MainWindow *MainApplication::mainWindow()
361{
362 foreach (QWidget *tlw, topLevelWidgets())for (auto _container_362 = QtPrivate::qMakeForeachContainer(topLevelWidgets
()); _container_362.i != _container_362.e; ++_container_362.i
) if (QWidget *tlw = *_container_362.i; false) {} else
{
363 MainWindow *tlmw = qobject_cast<MainWindow *>(tlw);
364 if (tlmw && tlmw->isVisible()) {
365 return tlmw;
366 }
367 }
368 return nullptr;
369}
370
371void MainApplication::storeCustomColorsInRecent()
372{
373 if (QColorDialog::customCount()) {
374 prefs_clear_string_list(recent.custom_colors);
375 recent.custom_colors = NULL__null;
376 for (int i = 0; i < QColorDialog::customCount(); i++) {
377 QRgb rgb = QColorDialog::customColor(i).rgb();
378 recent.custom_colors = g_list_append(recent.custom_colors, ws_strdup_printf("%08x", rgb)wmem_strdup_printf(__null, "%08x", rgb));
379 }
380 }
381}
382
383bool MainApplication::event(QEvent *event)
384{
385 QString display_filter = NULL__null;
386 if (event->type() == QEvent::FileOpen) {
387 QFileOpenEvent *foe = static_cast<QFileOpenEvent *>(event);
388 if (foe && foe->file().length() > 0) {
389 QString cf_path(foe->file());
390 if (initialized_) {
391 emit openCaptureFile(cf_path, display_filter, WTAP_TYPE_AUTO0);
392 } else {
393 pending_open_files_.append(cf_path);
394 }
395 }
396 return true;
397 }
398 return QApplication::event(event);
399}
400
401void MainApplication::cleanup()
402{
403 SoftwareUpdate::instance()->cleanup();
404 storeCustomColorsInRecent();
405 // Write the user's recent file(s) to disk.
406 write_profile_recent();
407 write_recent();
408
409 // We might end up here via exit_application.
410 QThreadPool::globalInstance()->waitForDone();
411}
412
413MainApplication::MainApplication(int &argc, char **argv) :
414 QApplication(argc, argv),
415 initialized_(false),
416 is_reloading_lua_(false),
417 if_notifier_(NULL__null),
418 active_captures_(0)
419#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
420 , normal_icon_(windowIcon())
421#endif
422{
423 mainApp = this;
424
425 MimeDatabaseInitThread *mime_db_init_thread = new(MimeDatabaseInitThread);
426 QThreadPool::globalInstance()->start(mime_db_init_thread);
427
428 Q_INIT_RESOURCE(about)do { extern int qInitResources_about (); qInitResources_about
(); } while (false)
;
1
Loop condition is false. Exiting loop
429 Q_INIT_RESOURCE(i18n)do { extern int qInitResources_i18n (); qInitResources_i18n (
); } while (false)
;
2
Loop condition is false. Exiting loop
430 Q_INIT_RESOURCE(languages)do { extern int qInitResources_languages (); qInitResources_languages
(); } while (false)
;
3
Loop condition is false. Exiting loop
431
432 // Initialize the ThemeManager as early as possible so that any
433 // widget constructed afterwards can resolve themed stylesheets and
434 // color tokens. This must run after QApplication's base ctor (so
435 // that the palette is queryable for light/dark detection) but
436 // before any UI is built. recent_common has already been read in
437 // main()/stratoshark_main() prior to constructing this application
438 // object, so we can read any configured themes as well.
439 // Theme selection is persisted in recent_common (recent.gui_theme_name),
440 // not in the preferences file — so it survives profile switches and
441 // stays global to the install. Empty / missing value, or the legacy
442 // "default" sentinel, get resolved by ThemeManager itself to the
443 // current flavor's preferred default (wireshark / stratoshark).
444 ThemeManager::init(ThemeManager::resolveThemeName(
445 QString::fromUtf8(recent.gui_theme_name)));
446
447#ifdef Q_OS_WIN
448 /* RichEd20.DLL is needed for native file dialog filter entries. */
449 ws_load_library("riched20.dll");
450#endif // Q_OS_WIN
451
452 // We use a lot of style sheets that base their colors on the main
453 // application palette, so this works better.
454 setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
455
456 // Throw various settings at the wall with the hope that one of them will
457 // enable context menu shortcuts QTBUG-69452, QTBUG-109590
458 setAttribute(Qt::AA_DontShowShortcutsInContextMenus, false);
459 styleHints()->setShowShortcutsInContextMenus(true);
460
461 packet_data_timer_.setParent(this);
462 connect(&packet_data_timer_, &QTimer::timeout, this, &MainApplication::refreshPacketData);
463 packet_data_timer_.start(1000);
464
465 tap_update_timer_.setParent(this);
466 // tap_update_timer interval is set when preferences are set before init
467 connect(this, &MainApplication::appInitialized, &tap_update_timer_, [&]() { tap_update_timer_.start(); });
468 connect(&tap_update_timer_, &QTimer::timeout, this, &MainApplication::updateTaps);
469
470 setStyle(new ThemeStyler);
4
Memory is allocated
471
472 connect(qApp(static_cast<QApplication *>(QCoreApplication::instance
()))
, &QApplication::aboutToQuit, this, &MainApplication::cleanup);
5
Potential memory leak
473}
474
475MainApplication::~MainApplication()
476{
477 mainApp = NULL__null;
478 clearDynamicMenuGroupItems();
479}
480
481void MainApplication::emitAppSignal(AppSignal signal)
482{
483 switch (signal) {
484 case ColumnsChanged:
485 emit columnsChanged();
486 break;
487 case CaptureFilterListChanged:
488 emit captureFilterListChanged();
489 break;
490 case DisplayFilterListChanged:
491 emit displayFilterListChanged();
492 break;
493 case FilterExpressionsChanged:
494 emit filterExpressionsChanged();
495 break;
496 case NameResolutionChanged:
497 emit addressResolutionChanged();
498 break;
499 case PreferencesChanged:
500 tap_update_timer_.setInterval(prefs.tap_update_interval);
501 emit preferencesChanged();
502 break;
503 case PacketDissectionChanged:
504 emit packetDissectionChanged();
505 break;
506 case RecentPreferencesRead:
507 emit recentPreferencesRead();
508 break;
509 case FieldsChanged:
510 emit fieldsChanged();
511 break;
512 case FreezePacketList:
513 emit freezePacketList(false);
514 break;
515 case AggregationChanged:
516 emit aggregationChanged();
517 break;
518 default:
519 break;
520 }
521}
522
523// Flush any collected app signals.
524//
525// On macOS emitting PacketDissectionChanged from a dialog can
526// render the application unusable:
527// https://gitlab.com/wireshark/wireshark/-/issues/11361
528// https://gitlab.com/wireshark/wireshark/-/issues/11448
529// Work around the problem by queueing up app signals and emitting them
530// after the dialog is closed.
531//
532// The following bugs might be related although they don't describe the
533// exact behavior we're working around here:
534// https://bugreports.qt.io/browse/QTBUG-38512
535// https://bugreports.qt.io/browse/QTBUG-38600
536void MainApplication::flushAppSignals()
537{
538 while (!app_signals_.isEmpty()) {
539 mainApp->emitAppSignal(app_signals_.takeFirst());
540 }
541}
542
543void MainApplication::emitStatCommandSignal(const QString &menu_path, const char *arg, void *userdata)
544{
545 emit openStatCommandDialog(menu_path, arg, userdata);
546}
547
548void MainApplication::emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata)
549{
550 emit openTapParameterDialog(cfg_abbr, arg, userdata);
551}
552
553// XXX Combine statistics and funnel routines into addGroupItem + groupItems?
554void MainApplication::addDynamicMenuGroupItem(int group, QAction *sg_action)
555{
556 if (!dynamic_menu_groups_.contains(group)) {
557 dynamic_menu_groups_[group] = QList<QAction *>();
558 }
559 dynamic_menu_groups_[group] << sg_action;
560}
561
562void MainApplication::appendDynamicMenuGroupItem(int group, QAction *sg_action)
563{
564 if (!added_menu_groups_.contains(group)) {
565 added_menu_groups_[group] = QList<QAction *>();
566 }
567 added_menu_groups_[group] << sg_action;
568 addDynamicMenuGroupItem(group, sg_action);
569}
570
571void MainApplication::removeDynamicMenuGroupItem(int group, QAction *sg_action)
572{
573 if (!removed_menu_groups_.contains(group)) {
574 removed_menu_groups_[group] = QList<QAction *>();
575 }
576 removed_menu_groups_[group] << sg_action;
577 dynamic_menu_groups_[group].removeAll(sg_action);
578}
579
580void MainApplication::clearDynamicMenuGroupItems()
581{
582 foreach (int group, dynamic_menu_groups_.keys())for (auto _container_582 = QtPrivate::qMakeForeachContainer(dynamic_menu_groups_
.keys()); _container_582.i != _container_582.e; ++_container_582
.i) if (int group = *_container_582.i; false) {} else
{
583 dynamic_menu_groups_[group].clear();
584 }
585}
586
587QList<QAction *> MainApplication::dynamicMenuGroupItems(int group)
588{
589 if (!dynamic_menu_groups_.contains(group)) {
590 return QList<QAction *>();
591 }
592
593 QList<QAction *> sgi_list = dynamic_menu_groups_[group];
594 std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
595 return sgi_list;
596}
597
598QList<QAction *> MainApplication::addedMenuGroupItems(int group)
599{
600 if (!added_menu_groups_.contains(group)) {
601 return QList<QAction *>();
602 }
603
604 QList<QAction *> sgi_list = added_menu_groups_[group];
605 std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
606 return sgi_list;
607}
608
609QList<QAction *> MainApplication::removedMenuGroupItems(int group)
610{
611 if (!removed_menu_groups_.contains(group)) {
612 return QList<QAction *>();
613 }
614
615 QList<QAction *> sgi_list = removed_menu_groups_[group];
616 std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
617 return sgi_list;
618}
619
620void MainApplication::clearAddedMenuGroupItems()
621{
622 foreach (int group, added_menu_groups_.keys())for (auto _container_622 = QtPrivate::qMakeForeachContainer(added_menu_groups_
.keys()); _container_622.i != _container_622.e; ++_container_622
.i) if (int group = *_container_622.i; false) {} else
{
623 added_menu_groups_[group].clear();
624 }
625}
626
627void MainApplication::clearRemovedMenuGroupItems()
628{
629 foreach (int group, removed_menu_groups_.keys())for (auto _container_629 = QtPrivate::qMakeForeachContainer(removed_menu_groups_
.keys()); _container_629.i != _container_629.e; ++_container_629
.i) if (int group = *_container_629.i; false) {} else
{
630 foreach (QAction *action, removed_menu_groups_[group])for (auto _container_630 = QtPrivate::qMakeForeachContainer(removed_menu_groups_
[group]); _container_630.i != _container_630.e; ++_container_630
.i) if (QAction *action = *_container_630.i; false) {} else
{
631 delete action;
632 }
633 removed_menu_groups_[group].clear();
634 }
635}
636
637#ifdef HAVE_LIBPCAP1
638
639static void
640iface_mon_event_cb(const char *iface, int added, int up)
641{
642 int present = 0;
643 unsigned ifs, j;
644 interface_t *device;
645 interface_options *interface_opts;
646
647 for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
648 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, ifs)(((interface_t*) (void *) (global_capture_opts.all_ifaces)->
data) [(ifs)])
;
649 if (strcmp(device->name, iface) == 0) {
650 present = 1;
651 if (!up) {
652 /*
653 * Interface went down or disappeared; remove all instances
654 * of it from the current list of interfaces selected
655 * for capturing.
656 */
657 for (j = 0; j < global_capture_opts.ifaces->len; j++) {
658 interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, j)(((interface_options*) (void *) (global_capture_opts.ifaces)->
data) [(j)])
;
659 if (strcmp(interface_opts->name, device->name) == 0) {
660 capture_opts_del_iface(&global_capture_opts, j);
661 }
662 }
663 }
664 }
665 }
666
667 mainApp->emitLocalInterfaceEvent(iface, added, up);
668 if (present != up) {
669 /*
670 * We've been told that there's a new interface or that an old
671 * interface is gone; reload the local interface list.
672 *
673 * XXX: We also want to reload the local interface list if [what
674 * we can retrieve about] the capabilities of the device have changed.
675 * Ideally we'd update the capabilities of just the one device in
676 * the cache and signal that the list has been updated, instead of
677 * freeing the entire cache and scanning again - but some extcaps
678 * depend on other interfaces being up; e.g. by default androiddump
679 * tries to connect to the loopback interface to look for adb running,
680 * so if the loopback interface changes so does the status of
681 * androiddump.
682 *
683 * On Linux, at least, you can't get the capabilities from a down
684 * interface, but it's still present in all_ifaces - dumpcap returns
685 * it in the list, and we show it so the user can get a status / error
686 * message when trying to capture on it instead of it vanishing.
687 * So if both present and up are true, then we still want to refresh
688 * to update the capabilities and restart the stats.
689 *
690 * We also store the address in all_ifaces and show them to the user,
691 * so we probably should monitor those events as well and update
692 * the interface list appropriately when those change.
693 */
694 MainWindow *mainWindow = mainApp->mainWindow();
695 if (mainWindow && mainWindow->interfaceListManager())
696 mainWindow->interfaceListManager()->requestRefresh();
697 }
698}
699
700#endif
701
702void MainApplication::ifChangeEventsAvailable()
703{
704#ifdef HAVE_LIBPCAP1
705 /*
706 * Something's readable from the descriptor for interface
707 * monitoring.
708 *
709 * Have the interface-monitoring code Read whatever interface-change
710 * events are available, and call the callback for them.
711 */
712 iface_mon_event();
713#endif
714}
715
716void MainApplication::emitLocalInterfaceEvent(const char *ifname, int added, int up)
717{
718 emit localInterfaceEvent(ifname, added, up);
719}
720
721void MainApplication::whenInitializedDispatch(const QObject *context, std::function<void()> fn)
722{
723 // POLICY DECISION (your input shapes behavior here):
724 //
725 // We get here only when a caller registers via whenInitialized() *after*
726 // appInitialized() has already fired. The not-yet-initialized path runs the
727 // callback later, from the event loop, once construction is long finished.
728 // How should the already-initialized path behave?
729 //
730 // A) Synchronous: fn(); right now. Simplest. But the callback runs while
731 // the caller's constructor is still on the stack -- the object may be
732 // only partly built, and any code after the whenInitialized() call in
733 // that constructor runs *after* the callback. Timing differs from the
734 // deferred path.
735 //
736 // B) Deferred to the event loop (e.g. QTimer::singleShot(0, context, fn)
737 // or QMetaObject::invokeMethod(..., Qt::QueuedConnection)): callback
738 // always runs after the current call returns, matching the signal path
739 // exactly, so callers see one consistent ordering. Costs one event-loop
740 // turn and needs the context-still-alive guarantee.
741 //
742 // Option A (current behavior) is intentional for now: it exactly reproduces
743 // the old open-coded `if (isInitialized()) doThing();` -- a synchronous call
744 // on the caller's stack -- so routing those sites through whenInitialized()
745 // is a pure no-op refactor. Option B is the intended end state, but it
746 // shifts the already-initialized callback by one event-loop turn and so
747 // changes ordering for every converted site at once; switch to it only
748 // behind broader ordering/lifetime tests (dialogs opened mid-session,
749 // welcome/interface frames at cold start, context destroyed same-turn).
750 //
751 // To switch, delete the fn() below and enable the deferred dispatch:
752 //
753 // QTimer::singleShot(0, const_cast<QObject *>(context), std::move(fn));
754 //
755 // (context is the receiver, so the call is dropped if it dies first, and
756 // runs on context's thread -- matching the connect() branch.)
757 Q_UNUSED(context)(void)context; // option A ignores context; option B uses it.
758 fn(); // option A: synchronous, matches legacy behavior.
759}
760
761void MainApplication::allSystemsGo()
762{
763 QString display_filter = NULL__null;
764 initialized_ = true;
765 emit appInitialized();
766 while (pending_open_files_.length() > 0) {
767 emit openCaptureFile(pending_open_files_.front(), display_filter, WTAP_TYPE_AUTO0);
768 pending_open_files_.pop_front();
769 }
770
771 bool sideBarVisible = recent.gui_welcome_page_sidebar_tips_visible ||
772 recent.gui_welcome_page_sidebar_learn_visible;
773 SoftwareUpdate::instance()->init(!sideBarVisible);
774
775#ifdef HAVE_LIBPCAP1
776 int err;
777 err = iface_mon_start(&iface_mon_event_cb);
778 if (err == 0) {
779 if_notifier_ = new QSocketNotifier(iface_mon_get_sock(),
780 QSocketNotifier::Read, this);
781 connect(if_notifier_, &QSocketNotifier::activated, this, &MainApplication::ifChangeEventsAvailable);
782 }
783#endif
784}
785
786_e_prefs *MainApplication::readConfigurationFiles(bool reset)
787{
788 e_prefs *prefs_p;
789
790 if (reset) {
791 //
792 // Reset current preferences and enabled/disabled protocols and
793 // heuristic dissectors before reading.
794 // (Needed except when this is called at startup.)
795 //
796 prefs_reset(application_configuration_environment_prefix(), application_columns(), application_num_columns());
797 proto_reenable_all();
798 }
799
800 /* Load libwireshark settings from the current profile. */
801 prefs_p = epan_load_settings();
802
803 return prefs_p;
804}
805
806static void switchTranslator(QTranslator& myTranslator, const QLocale &locale, const QString& filename, const QStringList &searchPath)
807{
808 mainApp->removeTranslator(&myTranslator);
809 for (const QString &path : searchPath) {
810 if (myTranslator.load(locale, filename, QStringLiteral("_")(QString(QtPrivate::qMakeStringPrivate(u"" "_"))), path)) {
811 mainApp->installTranslator(&myTranslator);
812 return;
813 }
814 }
815 if (locale.language() != QLocale::C) {
816 /* Don't compare the locale itself, see:
817 * https://doc.qt.io/qt-6/qlocale.html#operator-eq-eq
818 *
819 * Note that the ordered list of languages that were tried is that of
820 * locale.uiLanguages(); the first language in that list is not
821 * necessarily locale.language(), especially on Windows (See #17221.)
822 */
823 qWarningQMessageLogger(static_cast<const char *>("ui/qt/main_application.cpp"
), 823, static_cast<const char *>(__PRETTY_FUNCTION__))
.warning
() << "Couldn't load" << filename << "translations." << "Searched:" << searchPath;
824 }
825}
826
827QStringList MainApplication::getTranslationsPaths() const
828{
829 const char* env_prefix = application_configuration_environment_prefix();
830
831 // Search path list ordered by priority. Prefer personal configuration
832 // to global datadir to embedded resources to Qt global directory.
833 QStringList searchPath;
834 searchPath.emplaceBack(gchar_free_to_qstring(get_persconffile_path("languages", false, env_prefix)));
835 searchPath.emplaceBack(QStringLiteral("%1/languages")(QString(QtPrivate::qMakeStringPrivate(u"" "%1/languages"))).arg(get_datafile_dir(env_prefix)));
836 searchPath.emplaceBack(QStringLiteral(":/i18n/")(QString(QtPrivate::qMakeStringPrivate(u"" ":/i18n/"))));
837
838#if QT_VERSION((6<<16)|(10<<8)|(2)) >= QT_VERSION_CHECK(6, 8, 0)((6<<16)|(8<<8)|(0))
839 searchPath.append(QLibraryInfo::paths(QLibraryInfo::TranslationsPath));
840#else
841 searchPath.emplaceBack(QLibraryInfo::path(QLibraryInfo::TranslationsPath));
842#endif
843
844 return searchPath;
845}
846
847void MainApplication::loadLanguage(const QString newLanguage)
848{
849 QLocale locale;
850
851 if (newLanguage.isEmpty() || newLanguage == USE_SYSTEM_LANGUAGE"system") {
852 locale = QLocale::system();
853 } else {
854 locale = QLocale(newLanguage);
855 }
856
857 QLocale::setDefault(locale);
858
859 QStringList searchPath = getTranslationsPaths();
860
861 // Translations are searched for in the reverse order in which they were
862 // installed, so install the Qt generic translator first and ours last.
863 switchTranslator(mainApp->translatorQt, locale, QStringLiteral("qt")(QString(QtPrivate::qMakeStringPrivate(u"" "qt"))), searchPath);
864
865 // XXX - Yes, the translation files are also wireshark_%1.qm for Stratoshark.
866 // There is a stratoshark_en.[ts|qm] file too (for plurals?) though I'm
867 // not sure if it's used properly.
868 switchTranslator(mainApp->translator, locale, QStringLiteral("wireshark")(QString(QtPrivate::qMakeStringPrivate(u"" "wireshark"))), searchPath);
869}
870
871QString MainApplication::translateSystemLocale(const char *context, const char *sourceText, const char *disambiguation, int n) const
872{
873 // If we called this a lot, we could store the system QTranslators as
874 // member variables, but we only call this when constructing the
875 // language preference QComboBox.
876 QTranslator systemTranslator;
877 QString translated;
878 QStringList searchPath = getTranslationsPaths();
879
880 // Search in our translations first
881 for (const QString &path : searchPath) {
882 if (systemTranslator.load(QLocale::system(), QStringLiteral("wireshark")(QString(QtPrivate::qMakeStringPrivate(u"" "wireshark"))), QStringLiteral("_")(QString(QtPrivate::qMakeStringPrivate(u"" "_"))), path)) {
883 translated = systemTranslator.translate(context, sourceText, disambiguation, n);
884 if (!translated.isEmpty()) {
885 return translated;
886 }
887 }
888 }
889
890 // Try the generic Qt translations
891 for (const QString &path : searchPath) {
892 if (systemTranslator.load(QLocale::system(), QStringLiteral("qt")(QString(QtPrivate::qMakeStringPrivate(u"" "qt"))), QStringLiteral("_")(QString(QtPrivate::qMakeStringPrivate(u"" "_"))), path)) {
893 translated = systemTranslator.translate(context, sourceText, disambiguation, n);
894 if (!translated.isEmpty()) {
895 return translated;
896 }
897 }
898 }
899
900 return QString::fromUtf8(sourceText);
901}
902
903void MainApplication::doTriggerMenuItem(MainMenuItem menuItem)
904{
905 switch (menuItem)
906 {
907 case FileOpenDialog:
908 emit openCaptureFile(QString(), QString(), WTAP_TYPE_AUTO0);
909 break;
910 case CaptureOptionsDialog:
911 emit openCaptureOptions();
912 break;
913 }
914}
915
916void MainApplication::captureEventHandler(CaptureEvent ev)
917{
918 switch(ev.captureContext())
919 {
920#ifdef HAVE_LIBPCAP1
921 case CaptureEvent::Update:
922 case CaptureEvent::Fixed:
923 switch (ev.eventType())
924 {
925 case CaptureEvent::Prepared:
926 iface_mon_enable(true);
927 break;
928 case CaptureEvent::Started:
929 active_captures_++;
930 emit captureActive(active_captures_);
931 break;
932 case CaptureEvent::Finished:
933 active_captures_--;
934 emit captureActive(active_captures_);
935 // A refresh requested during the capture was deferred by
936 // InterfaceListManager (capture-active guard) and is serviced now via
937 // the captureActive signal above; no explicit re-trigger needed.
938 break;
939 default:
940 break;
941 }
942 break;
943#endif
944 case CaptureEvent::File:
945 case CaptureEvent::Reload:
946 case CaptureEvent::Rescan:
947 switch (ev.eventType())
948 {
949 case CaptureEvent::Started:
950 QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL3000 / 5, this, SLOT(updateTaps())qFlagLocation("1" "updateTaps()" "\0" "ui/qt/main_application.cpp"
":" "950")
);
951 QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL3000 / 2, this, SLOT(updateTaps())qFlagLocation("1" "updateTaps()" "\0" "ui/qt/main_application.cpp"
":" "951")
);
952 break;
953 case CaptureEvent::Finished:
954 updateTaps();
955 break;
956 default:
957 break;
958 }
959 break;
960 default:
961 break;
962 }
963}
964
965void MainApplication::pushStatus(StatusInfo status, const QString &message, const QString &messagetip)
966{
967 MainWindow * mw = mainWindow();
968 if (! mw) {
969 return;
970 }
971
972 MainStatusBar * bar = mw->statusBar();
973 if (! bar) {
974 return;
975 }
976
977 switch(status)
978 {
979 case FilterSyntax:
980 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FILTER, message);
981 break;
982 case FieldStatus:
983 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FIELD, message);
984 break;
985 case FileStatus:
986 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FILE, message, messagetip);
987 break;
988 case ByteStatus:
989 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_BYTE, message);
990 break;
991 case BusyStatus:
992 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_PROGRESS, message, messagetip);
993 break;
994 case TemporaryStatus:
995 bar->pushGenericStatus(MainStatusBar::STATUS_CTX_TEMPORARY, message);
996 break;
997 }
998}
999
1000void MainApplication::popStatus(StatusInfo status)
1001{
1002 MainWindow * mw = mainWindow();
1003 if (! mw) {
1004 return;
1005 }
1006
1007 MainStatusBar * bar = mw->statusBar();
1008 if (! bar) {
1009 return;
1010 }
1011
1012 switch(status)
1013 {
1014 case FilterSyntax:
1015 bar->popGenericStatus(MainStatusBar::STATUS_CTX_FILTER);
1016 break;
1017 case FieldStatus:
1018 bar->popGenericStatus(MainStatusBar::STATUS_CTX_FIELD);
1019 break;
1020 case FileStatus:
1021 bar->popGenericStatus(MainStatusBar::STATUS_CTX_FILE);
1022 break;
1023 case ByteStatus:
1024 bar->popGenericStatus(MainStatusBar::STATUS_CTX_BYTE);
1025 break;
1026 case BusyStatus:
1027 bar->popGenericStatus(MainStatusBar::STATUS_CTX_PROGRESS);
1028 break;
1029 case TemporaryStatus:
1030 bar->popGenericStatus(MainStatusBar::STATUS_CTX_TEMPORARY);
1031 break;
1032 }
1033}
1034
1035void MainApplication::gotoFrame(int frame)
1036{
1037 MainWindow * mw = mainWindow();
1038 if (! mw) {
1039 return;
1040 }
1041
1042 mw->gotoFrame(frame);
1043}
1044
1045void MainApplication::reloadDisplayFilterMacros()
1046{
1047 dfilter_macro_reload(application_configuration_environment_prefix());
1048 // The signal is needed when the display filter grammar changes for
1049 // any reason (not just "fields".)
1050 mainApp->emitAppSignal(MainApplication::FieldsChanged);
1051}