CMake: Add /WX
[metze/wireshark/wip.git] / ui / qt / wireshark_application.cpp
1 /* wireshark_application.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 // warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
23 #ifdef _MSC_VER
24 #pragma warning(push)
25 #pragma warning(disable : 4267)
26 #endif
27
28 #include "wireshark_application.h"
29
30 #include <algorithm>
31 #include <errno.h>
32
33 #include "wsutil/filesystem.h"
34
35 #include "epan/addr_resolv.h"
36 #include "epan/disabled_protos.h"
37 #include "epan/ftypes/ftypes.h"
38 #include "epan/prefs.h"
39 #include "epan/proto.h"
40 #include "epan/tap.h"
41 #include "epan/timestamp.h"
42
43 #include "ui/decode_as_utils.h"
44 #include "ui/preference_utils.h"
45 #include "ui/iface_lists.h"
46 #include "ui/recent.h"
47 #include "ui/simple_dialog.h"
48 #include "ui/util.h"
49
50 #include "qt_ui_utils.h"
51
52 #include "color_filters.h"
53 #include "log.h"
54 #include "recent_file_status.h"
55
56 #ifdef HAVE_LIBPCAP
57 #include <caputils/iface_monitor.h>
58 #endif
59
60 #include "ui/capture.h"
61 #include "ui/filters.h"
62 #include "ui/capture_globals.h"
63 #include "ui/software_update.h"
64 #include "ui/last_open_dir.h"
65 #include "ui/recent_utils.h"
66 #include <wsutil/utf8_entities.h>
67
68 #ifdef _WIN32
69 #  include "ui/win32/console_win32.h"
70 #endif /* _WIN32 */
71
72 #include <QAction>
73 #include <QDesktopServices>
74 #include <QDir>
75 #include <QEvent>
76 #include <QFileOpenEvent>
77 #include <QFontMetrics>
78 #include <QLibraryInfo>
79 #include <QLocale>
80 #include <QMutableListIterator>
81 #include <QSocketNotifier>
82 #include <QThread>
83 #include <QUrl>
84
85 #ifdef Q_OS_WIN
86 #include <QDebug>
87 #include <QLibrary>
88 #endif
89
90 #ifdef _MSC_VER
91 #pragma warning(pop)
92 #endif
93
94 WiresharkApplication *wsApp = NULL;
95
96 // XXX - Copied from ui/gtk/file_dlg.c
97
98 // MUST be UTF-8
99 static char *last_open_dir = NULL;
100 static bool updated_last_open_dir = FALSE;
101 static QList<recent_item_status *> recent_items_;
102 static QHash<int, QList<QAction *> > dynamic_menu_groups_;
103 static QHash<int, QList<QAction *> > added_menu_groups_;
104 static QHash<int, QList<QAction *> > removed_menu_groups_;
105
106 QString WiresharkApplication::window_title_separator_ = QString::fromUtf8(" " UTF8_MIDDLE_DOT " ");
107
108 void
109 topic_action(topic_action_e action)
110 {
111     if (wsApp) wsApp->helpTopicAction(action);
112 }
113
114 extern "C" char *
115 get_last_open_dir(void)
116 {
117     return last_open_dir;
118 }
119
120 void
121 set_last_open_dir(const char *dirname)
122 {
123     if (wsApp) wsApp->setLastOpenDir(dirname);
124 }
125
126 /*
127  * Add the capture filename to the application-wide "Recent Files" list.
128  * Contrary to the name this isn't limited to the "recent" menu.
129  */
130 /*
131  * XXX - We might want to call SHAddToRecentDocs under Windows 7:
132  * http://stackoverflow.com/questions/437212/how-do-you-register-a-most-recently-used-list-with-windows-in-preparation-for-win
133  */
134 extern "C" void
135 add_menu_recent_capture_file(const gchar *cf_name) {
136     QString normalized_cf_name = QString::fromUtf8(cf_name);
137 //    QDir cf_path;
138
139 //    cf_path.setPath(normalized_cf_name);
140 //    normalized_cf_name = cf_path.absolutePath();
141     normalized_cf_name = QDir::cleanPath(normalized_cf_name);
142     normalized_cf_name = QDir::toNativeSeparators(normalized_cf_name);
143
144     /* Iterate through the recent items list, removing duplicate entries and every
145      * item above count_max
146      */
147     unsigned int cnt = 1;
148     QMutableListIterator<recent_item_status *> rii(recent_items_);
149     while (rii.hasNext()) {
150         recent_item_status *ri = rii.next();
151         /* if this element string is one of our special items (separator, ...) or
152          * already in the list or
153          * this element is above maximum count (too old), remove it
154          */
155         if (ri->filename.length() < 1 ||
156 #ifdef _WIN32
157             /* do a case insensitive compare on win32 */
158             ri->filename.compare(normalized_cf_name, Qt::CaseInsensitive) == 0 ||
159 #else   /* _WIN32 */
160             /* do a case sensitive compare on unix */
161             ri->filename.compare(normalized_cf_name) == 0 ||
162 #endif
163             cnt >= prefs.gui_recent_files_count_max) {
164             rii.remove();
165             delete(ri);
166             cnt--;
167         }
168         cnt++;
169     }
170     wsApp->addRecentItem(normalized_cf_name, 0, false);
171 }
172
173 /* write all capture filenames of the menu to the user's recent file */
174 extern "C" void menu_recent_file_write_all(FILE *rf) {
175
176     /* we have to iterate backwards through the children's list,
177      * so we get the latest item last in the file.
178      */
179     QListIterator<recent_item_status *> rii(recent_items_);
180     rii.toBack();
181     while (rii.hasPrevious()) {
182         QString cf_name;
183         /* get capture filename from the menu item label */
184         cf_name = rii.previous()->filename;
185         if (cf_name != NULL) {
186 //            if(u3_active())
187 //                fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", u3_contract_device_path(cf_name));
188 //            else
189                 fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", cf_name.toUtf8().constData());
190         }
191     }
192 }
193
194 // Check each recent item in a separate thread so that we don't hang while
195 // calling stat(). This is called periodically because files and entire
196 // volumes can disappear and reappear at any time.
197 void WiresharkApplication::refreshRecentFiles(void) {
198     recent_item_status *ri;
199     RecentFileStatus *rf_status;
200
201     // We're in the middle of a capture. Don't create traffic.
202     if (active_captures_ > 0) return;
203
204     foreach (ri, recent_items_) {
205         if (ri->in_thread) {
206             continue;
207         }
208
209         rf_status = new RecentFileStatus(ri->filename, this);
210
211         connect(rf_status, SIGNAL(statusFound(QString, qint64, bool)),
212                 this, SLOT(itemStatusFinished(QString, qint64, bool)), Qt::QueuedConnection);
213         connect(rf_status, SIGNAL(finished()), rf_status, SLOT(deleteLater()));
214         rf_status->start();
215     }
216 }
217
218 void WiresharkApplication::refreshAddressResolution()
219 {
220     // Anything new show up?
221     if (host_name_lookup_process()) {
222         emit addressResolutionChanged();
223     }
224 }
225
226 void WiresharkApplication::updateTaps()
227 {
228     draw_tap_listeners(FALSE);
229 }
230
231 QDir WiresharkApplication::lastOpenDir() {
232     return QDir(last_open_dir);
233 }
234
235 void WiresharkApplication::setLastOpenDir(QString *dir_str) {
236     setLastOpenDir(dir_str->toUtf8().constData());
237 }
238
239 void WiresharkApplication::helpTopicAction(topic_action_e action)
240 {
241     QString url = gchar_free_to_qstring(topic_action_url(action));
242
243     if(!url.isEmpty()) {
244         QDesktopServices::openUrl(QUrl(url));
245     }
246 }
247
248 void WiresharkApplication::setMonospaceFont(const char *font_string) {
249
250     if (font_string && strlen(font_string) > 0) {
251         mono_font_.fromString(font_string);
252 //        mono_bold_font_ = QFont(mono_regular_font_);
253 //        mono_bold_font_.setBold(true);
254         return;
255     }
256
257     // http://en.wikipedia.org/wiki/Category:Monospaced_typefaces
258     const char *win_default_font = "Consolas";
259     const char *win_alt_font = "Lucida Console";
260     const char *osx_default_font = "Menlo";
261     const char *osx_alt_font = "Monaco";
262     const char *x11_default_font = "Liberation Mono";
263     const QStringList x11_alt_fonts = QStringList() << "DejaVu Sans Mono" << "Bitstream Vera Sans Mono";
264     const QStringList fallback_fonts = QStringList() << "Lucida Sans Typewriter" << "Inconsolata" << "Droid Sans Mono" << "Andale Mono" << "Courier New" << "monospace";
265     QStringList substitutes;
266     int font_size_adjust = 0;
267
268     // Try to pick the latest, shiniest fixed-width font for our OS.
269 #if defined(Q_OS_WIN)
270     const char *default_font = win_default_font;
271     substitutes << win_alt_font << osx_default_font << osx_alt_font << x11_default_font << x11_alt_fonts << fallback_fonts;
272     font_size_adjust = 2;
273 #elif defined(Q_OS_MAC)
274     const char *default_font = osx_default_font;
275     substitutes << osx_alt_font << win_default_font << win_alt_font << x11_default_font << x11_alt_fonts << fallback_fonts;
276 #else
277     const char *default_font = x11_default_font;
278     substitutes << x11_alt_fonts << win_default_font << win_alt_font << osx_default_font << osx_alt_font << fallback_fonts;
279 #endif
280
281     mono_font_.setFamily(default_font);
282     mono_font_.insertSubstitutions(default_font, substitutes);
283     mono_font_.setPointSize(wsApp->font().pointSize() + font_size_adjust);
284     mono_font_.setBold(false);
285
286 //    mono_bold_font_ = QFont(mono_font_);
287 //    mono_bold_font_.setBold(true);
288
289     g_free(prefs.gui_qt_font_name);
290     prefs.gui_qt_font_name = qstring_strdup(mono_font_.toString());
291 }
292
293 int WiresharkApplication::monospaceTextSize(const char *str)
294 {
295     QFontMetrics fm(mono_font_);
296
297     return fm.width(str);
298 }
299
300 void WiresharkApplication::setConfigurationProfile(const gchar *profile_name)
301 {
302     char  *gdp_path, *dp_path;
303     char  *rf_path;
304     int    rf_open_errno;
305
306     /* First check if profile exists */
307     if (!profile_exists(profile_name, FALSE)) {
308         if (profile_exists(profile_name, TRUE)) {
309             char  *pf_dir_path, *pf_dir_path2, *pf_filename;
310             /* Copy from global profile */
311             if (create_persconffile_profile(profile_name, &pf_dir_path) == -1) {
312                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
313                     "Can't create directory\n\"%s\":\n%s.",
314                     pf_dir_path, g_strerror(errno));
315
316                 g_free(pf_dir_path);
317             }
318
319             if (copy_persconffile_profile(profile_name, profile_name, TRUE, &pf_filename,
320                     &pf_dir_path, &pf_dir_path2) == -1) {
321                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
322                     "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
323                     pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
324
325                 g_free(pf_filename);
326                 g_free(pf_dir_path);
327                 g_free(pf_dir_path2);
328             }
329         } else {
330             /* No personal and no global profile exists */
331             return;
332         }
333     }
334
335     /* Then check if changing to another profile */
336     if (profile_name && strcmp (profile_name, get_profile_name()) == 0) {
337         return;
338     }
339
340     /* Get the current geometry, before writing it to disk */
341     emit profileChanging();
342
343     if (profile_exists(get_profile_name(), FALSE)) {
344         /* Write recent file for profile we are leaving, if it still exists */
345         write_profile_recent();
346     }
347
348     /* Set profile name and update the status bar */
349     set_profile_name (profile_name);
350     emit profileNameChanged(profile_name);
351
352     /* Reset current preferences and apply the new */
353     prefs_reset();
354 //    menu_prefs_reset();
355
356     (void) readConfigurationFiles (&gdp_path, &dp_path);
357
358     if (!recent_read_profile_static(&rf_path, &rf_open_errno)) {
359         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
360             "Could not open common recent file\n\"%s\": %s.",
361             rf_path, g_strerror(rf_open_errno));
362         g_free(rf_path);
363     }
364     if (recent.gui_fileopen_remembered_dir &&
365         test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
366         set_last_open_dir(recent.gui_fileopen_remembered_dir);
367     }
368     timestamp_set_type (recent.gui_time_format);
369     timestamp_set_seconds_type (recent.gui_seconds_format);
370     color_filters_enable(recent.packet_list_colorize);
371     tap_update_timer_.setInterval(prefs.tap_update_interval);
372
373     prefs_to_capture_opts();
374     prefs_apply_all();
375
376     emit preferencesChanged();
377     emit columnsChanged();
378     emit recentFilesRead();
379     emit filterExpressionsChanged();
380 //    macros_post_update();
381
382     /* Enable all protocols and disable from the disabled list */
383     proto_enable_all();
384     if (gdp_path == NULL && dp_path == NULL) {
385         set_disabled_protos_list();
386         set_disabled_heur_dissector_list();
387     }
388
389     /* Reload color filters */
390     color_filters_reload();
391
392 //    user_font_apply();
393
394     /* Update menus with new recent values */
395     //    menu_recent_read_finished();
396 }
397
398 const QString WiresharkApplication::windowTitleString(QStringList title_parts)
399 {
400     QMutableStringListIterator tii(title_parts);
401     while (tii.hasNext()) {
402         QString ti = tii.next();
403         if (ti.isEmpty()) tii.remove();
404     }
405     title_parts.prepend(applicationName());
406     return title_parts.join(window_title_separator_);
407 }
408
409 void WiresharkApplication::setLastOpenDir(const char *dir_name)
410 {
411     qint64 len;
412     gchar *new_last_open_dir;
413
414     if (dir_name && dir_name[0]) {
415         len = strlen(dir_name);
416         if (dir_name[len-1] == G_DIR_SEPARATOR) {
417             new_last_open_dir = g_strconcat(dir_name, (char *)NULL);
418         }
419         else {
420             new_last_open_dir = g_strconcat(dir_name,
421                                             G_DIR_SEPARATOR_S, (char *)NULL);
422         }
423
424         if (last_open_dir == NULL ||
425             strcmp(last_open_dir, new_last_open_dir) != 0)
426             updated_last_open_dir = TRUE;
427     }
428     else {
429         new_last_open_dir = NULL;
430         if (last_open_dir != NULL)
431             updated_last_open_dir = TRUE;
432     }
433
434     g_free(last_open_dir);
435     last_open_dir = new_last_open_dir;
436 }
437
438 bool WiresharkApplication::event(QEvent *event)
439 {
440     QString display_filter = NULL;
441     if (event->type() == QEvent::FileOpen) {
442         QFileOpenEvent *foe = static_cast<QFileOpenEvent *>(event);
443         if (foe && foe->file().length() > 0) {
444             QString cf_path(foe->file());
445             if (initialized_) {
446                 emit openCaptureFile(cf_path, display_filter, WTAP_TYPE_AUTO);
447             } else {
448                 pending_open_files_.append(cf_path);
449             }
450         }
451         return true;
452     }
453     return QApplication::event(event);
454 }
455
456 void WiresharkApplication::clearRecentItems() {
457     qDeleteAll(recent_items_.begin(), recent_items_.end());
458     recent_items_.clear();
459     emit updateRecentItemStatus(NULL, 0, false);
460 }
461
462 void WiresharkApplication::captureFileReadStarted()
463 {
464     // Doesn't appear to do anything. Logic probably needs to be in file.c.
465     QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL / 5, this, SLOT(updateTaps()));
466     QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL / 2, this, SLOT(updateTaps()));
467 }
468
469 void WiresharkApplication::cleanup()
470 {
471     software_update_cleanup();
472     // Write the user's recent file(s) to disk.
473     write_profile_recent();
474     write_recent();
475 }
476
477 void WiresharkApplication::itemStatusFinished(const QString filename, qint64 size, bool accessible) {
478     recent_item_status *ri;
479
480     foreach (ri, recent_items_) {
481         if (filename == ri->filename && (size != ri->size || accessible != ri->accessible)) {
482             ri->size = size;
483             ri->accessible = accessible;
484             ri->in_thread = false;
485
486             emit updateRecentItemStatus(filename, size, accessible);
487         }
488     }
489 }
490
491 WiresharkApplication::WiresharkApplication(int &argc,  char **argv) :
492     QApplication(argc, argv),
493     initialized_(false),
494     is_reloading_lua_(false),
495     if_notifier_(NULL),
496     active_captures_(0)
497 {
498     wsApp = this;
499     setApplicationName("Wireshark");
500
501     Q_INIT_RESOURCE(about);
502     Q_INIT_RESOURCE(i18n);
503     Q_INIT_RESOURCE(layout);
504     Q_INIT_RESOURCE(toolbar);
505     Q_INIT_RESOURCE(wsicon);
506     Q_INIT_RESOURCE(languages);
507
508 #ifdef Q_OS_WIN
509     /* RichEd20.DLL is needed for native file dialog filter entries. */
510     if (QLibrary::isLibrary("riched20.dll")) {
511         QLibrary riched20("riched20.dll");
512         riched20.load();
513         if (!riched20.isLoaded()) {
514             qDebug() << riched20.errorString();
515         }
516     }
517 #endif // Q_OS_WIN
518
519 #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
520     setAttribute(Qt::AA_UseHighDpiPixmaps);
521 #endif
522
523     QList<int> icon_sizes = QList<int>() << 16 << 24 << 32 << 48 << 64 << 128 << 256 << 512 << 1024;
524     foreach (int icon_size, icon_sizes) {
525         QString icon_path = QString(":/wsicon/wsicon%1.png").arg(icon_size);
526         normal_icon_.addFile(icon_path);
527         icon_path = QString(":/wsicon/wsiconcap%1.png").arg(icon_size);
528         capture_icon_.addFile(icon_path);
529     }
530
531     //
532     // XXX - this means we try to check for the existence of all files
533     // in the recent list every 2 seconds; that causes noticeable network
534     // traffic if any of them are stored on file servers.
535     //
536     // QFileSystemWatcher should allow us to watch for files being
537     // removed or renamed.  It uses kqueues and EVFILT_VNODE on FreeBSD,
538     // NetBSD, FSEvents on OS X, inotify on Linux if available, and
539     // FindFirstChagneNotification() on Windows.  On all other platforms,
540     // it just periodically polls, as we're doing now.
541     //
542     // For unmounts:
543     //
544     // OS X and FreeBSD deliver NOTE_REVOKE notes for EVFILT_VNODE, and
545     // QFileSystemWatcher delivers signals for them, just as it does for
546     // NOTE_DELETE and NOTE_RENAME.
547     //
548     // On Linux, inotify:
549     //
550     //    http://man7.org/linux/man-pages/man7/inotify.7.html
551     //
552     // appears to deliver "filesystem containing watched object was
553     // unmounted" events.  It looks as if Qt turns them into "changed"
554     // events.
555     //
556     // On Windows, it's not clearly documented what happens on a handle
557     // opened with FindFirstChangeNotification() if the volume on which
558     // the path handed to FindFirstChangeNotification() is removed, or
559     // ejected, or whatever the Windowsese is for "unmounted".  The
560     // handle obviously isn't valid any more, but whether it just hangs
561     // around and never delivers any notifications or delivers an
562     // event that turns into an error indication doesn't seem to be
563     // documented.  If it just hangs around, I think our main loop will
564     // receive a WM_DEVICECHANGE Windows message with DBT_DEVICEREMOVECOMPLETE
565     // if an unmount occurs - even for network devices.  If we need to watch
566     // for those, we can use the winEvent method of the QWidget for the
567     // top-level window to get Windows messages.
568     //
569     // Note also that remote file systems might not report file
570     // removal or renames if they're done on the server or done by
571     // another client.  At least on OS X, they *will* get reported
572     // if they're done on the machine running the program doing the
573     // kqueue stuff, and, at least in newer versions, should get
574     // reported on SMB-mounted (and AFP-mounted?) file systems
575     // even if done on the server or another client.
576     //
577     // But, when push comes to shove, the file manager(s) on the
578     // OSes in question probably use the same mechanisms to
579     // monitor folders in folder windows or open/save dialogs or...,
580     // so my inclination is just to use QFileSystemWatcher.
581     //
582     // However, that wouldn't catch files that become *re*-accessible
583     // by virtue of a file system being re-mounted.  The only way to
584     // catch *that* would be to watch for mounts and re-check all
585     // marked-as-inaccessible files.
586     //
587     // OS X and FreeBSD also support EVFILT_FS events, which notify you
588     // of file system mounts and unmounts.  We'd need to add our own
589     // kqueue for that, if we can check those with QSocketNotifier.
590     //
591     // On Linux, at least as of 2006, you're supposed to poll /proc/mounts:
592     //
593     //    https://lkml.org/lkml/2006/2/22/169
594     //
595     // to discover mounts.
596     //
597     // On Windows, you'd probably have to watch for WM_DEVICECHANGE events.
598     //
599     // Then again, with an automounter, a file system containing a
600     // recent capture might get unmounted automatically if you haven't
601     // referred to anything on that file system for a while, and get
602     // treated as inaccessible.  However, if you try to access it,
603     // the automounter will attempt to re-mount it, so the access *will*
604     // succeed if the automounter can remount the file.
605     //
606     // (Speaking of automounters, repeatedly polling recent files will
607     // keep the file system from being unmounted, for what that's worth.)
608     //
609     // At least on OS X, you can determine whether a file is on an
610     // automounted file system by calling statfs() on its path and
611     // checking whether MNT_AUTOMOUNTED is set in f_flags.  FreeBSD
612     // appears to support that flag as well, but no other *BSD appears
613     // to.
614     //
615     // I'm not sure what can be done on Linux.
616     //
617     recent_timer_.setParent(this);
618     connect(&recent_timer_, SIGNAL(timeout()), this, SLOT(refreshRecentFiles()));
619     recent_timer_.start(2000);
620
621     addr_resolv_timer_.setParent(this);
622     connect(&addr_resolv_timer_, SIGNAL(timeout()), this, SLOT(refreshAddressResolution()));
623     addr_resolv_timer_.start(1000);
624
625     tap_update_timer_.setParent(this);
626     tap_update_timer_.setInterval(TAP_UPDATE_DEFAULT_INTERVAL);
627     connect(this, SIGNAL(appInitialized()), &tap_update_timer_, SLOT(start()));
628     connect(&tap_update_timer_, SIGNAL(timeout()), this, SLOT(updateTaps()));
629
630     // Application-wide style sheet
631     QString app_style_sheet = qApp->styleSheet();
632 #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
633     // Qt uses the HITheme API to draw splitters. In recent versions of OS X
634     // this looks particularly bad: https://bugreports.qt.io/browse/QTBUG-43425
635     // This doesn't look native but it looks better than Yosemite's bit-rotten
636     // rendering of HIThemeSplitterDrawInfo.
637     app_style_sheet +=
638             "QSplitter::handle:vertical { height: 0px; }\n"
639             "QSplitter::handle:horizontal { width: 0px; }\n";
640 #endif
641     qApp->setStyleSheet(app_style_sheet);
642
643     connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
644 }
645
646 void WiresharkApplication::registerUpdate(register_action_e action, const char *message)
647 {
648     emit splashUpdate(action, message);
649 }
650
651 void WiresharkApplication::emitAppSignal(AppSignal signal)
652 {
653     switch (signal) {
654     case ColumnsChanged:
655         emit columnsChanged();
656         break;
657     case FilterExpressionsChanged:
658         emit filterExpressionsChanged();
659         break;
660     case PreferencesChanged:
661         emit preferencesChanged();
662         break;
663     case PacketDissectionChanged:
664         emit packetDissectionChanged();
665         break;
666     case RecentFilesRead:
667         emit recentFilesRead();
668         break;
669     case FieldsChanged:
670         emit fieldsChanged();
671         break;
672     default:
673         break;
674     }
675 }
676
677 // Flush any collected app signals.
678 //
679 // On OS X emitting PacketDissectionChanged from a dialog can
680 // render the application unusable:
681 // https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=11361
682 // https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=11448
683 // Work around the problem by queueing up app signals and emitting them
684 // after the dialog is closed.
685 //
686 // The following bugs might be related although they don't describe the
687 // exact behavior we're working around here:
688 // https://bugreports.qt.io/browse/QTBUG-38512
689 // https://bugreports.qt.io/browse/QTBUG-38600
690 void WiresharkApplication::flushAppSignals()
691 {
692     while (!app_signals_.isEmpty()) {
693         wsApp->emitAppSignal(app_signals_.takeFirst());
694     }
695 }
696
697 void WiresharkApplication::emitStatCommandSignal(const QString &menu_path, const char *arg, void *userdata)
698 {
699     emit openStatCommandDialog(menu_path, arg, userdata);
700 }
701
702 void WiresharkApplication::emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata)
703 {
704     emit openTapParameterDialog(cfg_abbr, arg, userdata);
705 }
706
707 // XXX Combine statistics and funnel routines into addGroupItem + groupItems?
708 void WiresharkApplication::addDynamicMenuGroupItem(int group, QAction *sg_action)
709 {
710     if (!dynamic_menu_groups_.contains(group)) {
711         dynamic_menu_groups_[group] = QList<QAction *>();
712     }
713     dynamic_menu_groups_[group] << sg_action;
714 }
715
716 void WiresharkApplication::appendDynamicMenuGroupItem(int group, QAction *sg_action)
717 {
718     if (!added_menu_groups_.contains(group)) {
719         added_menu_groups_[group] = QList<QAction *>();
720     }
721     added_menu_groups_[group] << sg_action;
722     addDynamicMenuGroupItem(group, sg_action);
723 }
724
725 void WiresharkApplication::removeDynamicMenuGroupItem(int group, QAction *sg_action)
726 {
727     if (!removed_menu_groups_.contains(group)) {
728         removed_menu_groups_[group] = QList<QAction *>();
729     }
730     removed_menu_groups_[group] << sg_action;
731     dynamic_menu_groups_[group].removeAll(sg_action);
732 }
733
734 QList<QAction *> WiresharkApplication::dynamicMenuGroupItems(int group)
735 {
736     if (!dynamic_menu_groups_.contains(group)) {
737         return QList<QAction *>();
738     }
739
740     QList<QAction *> sgi_list = dynamic_menu_groups_[group];
741     std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
742     return sgi_list;
743 }
744
745 QList<QAction *> WiresharkApplication::addedMenuGroupItems(int group)
746 {
747     if (!added_menu_groups_.contains(group)) {
748         return QList<QAction *>();
749     }
750
751     QList<QAction *> sgi_list = added_menu_groups_[group];
752     std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
753     return sgi_list;
754 }
755
756 QList<QAction *> WiresharkApplication::removedMenuGroupItems(int group)
757 {
758     if (!removed_menu_groups_.contains(group)) {
759         return QList<QAction *>();
760     }
761
762     QList<QAction *> sgi_list = removed_menu_groups_[group];
763     std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
764     return sgi_list;
765 }
766
767 void WiresharkApplication::clearAddedMenuGroupItems()
768 {
769     foreach (int group, added_menu_groups_.uniqueKeys()) {
770         added_menu_groups_[group].clear();
771     }
772 }
773
774 void WiresharkApplication::clearRemovedMenuGroupItems()
775 {
776     foreach (int group, removed_menu_groups_.uniqueKeys()) {
777         foreach (QAction *action, removed_menu_groups_[group]) {
778             delete action;
779         }
780         removed_menu_groups_[group].clear();
781     }
782 }
783
784 #ifdef HAVE_LIBPCAP
785
786 static void
787 iface_mon_event_cb(const char *iface, int up)
788 {
789     int present = 0;
790     guint ifs, j;
791     interface_t device;
792     interface_options interface_opts;
793
794     for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
795         device = g_array_index(global_capture_opts.all_ifaces, interface_t, ifs);
796         if (strcmp(device.name, iface) == 0) {
797             present = 1;
798             if (!up) {
799                 /*
800                  * Interface went down or disappeared; remove all instances
801                  * of it from the current list of interfaces selected
802                  * for capturing.
803                  */
804                 for (j = 0; j < global_capture_opts.ifaces->len; j++) {
805                     interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j);
806                     if (strcmp(interface_opts.name, device.name) == 0) {
807                         g_array_remove_index(global_capture_opts.ifaces, j);
808                 }
809              }
810           }
811         }
812     }
813
814     if (present != up) {
815         /*
816          * We've been told that there's a new interface or that an old
817          * interface is gone; reload the local interface list.
818          */
819         scan_local_interfaces(main_window_update);
820     }
821 }
822
823 #endif
824
825 void WiresharkApplication::ifChangeEventsAvailable()
826 {
827 #ifdef HAVE_LIBPCAP
828     /*
829      * Something's readable from the descriptor for interface
830      * monitoring.
831      *
832      * Have the interface-monitoring code Read whatever interface-change
833      * events are available, and call the callback for them.
834      */
835     iface_mon_event();
836
837     /*
838      * Now emit a signal to indicate that the list changed, so that all
839      * places displaying the list will get updated.
840      *
841      * XXX - only if it *did* change.
842      */
843     emit localInterfaceListChanged();
844 #endif
845 }
846
847 void WiresharkApplication::refreshLocalInterfaces()
848 {
849 #ifdef HAVE_LIBPCAP
850     /*
851      * Reload the local interface list.
852      */
853     scan_local_interfaces(main_window_update);
854
855     /*
856      * Now emit a signal to indicate that the list changed, so that all
857      * places displaying the list will get updated.
858      *
859      * XXX - only if it *did* change.
860      */
861     emit localInterfaceListChanged();
862 #endif
863 }
864
865 void WiresharkApplication::allSystemsGo()
866 {
867     QString display_filter = NULL;
868     initialized_ = true;
869     emit appInitialized();
870     while (pending_open_files_.length() > 0) {
871         emit openCaptureFile(pending_open_files_.front(), display_filter, WTAP_TYPE_AUTO);
872         pending_open_files_.pop_front();
873     }
874     software_update_init();
875
876 #ifdef HAVE_LIBPCAP
877     int err;
878     err = iface_mon_start(&iface_mon_event_cb);
879     if (err == 0) {
880         if_notifier_ = new QSocketNotifier(iface_mon_get_sock(),
881                                            QSocketNotifier::Read);
882         connect(if_notifier_, SIGNAL(activated(int)), SLOT(ifChangeEventsAvailable()));
883     }
884 #endif
885 }
886
887 _e_prefs *WiresharkApplication::readConfigurationFiles(char **gdp_path, char **dp_path)
888 {
889     int                  gpf_open_errno, gpf_read_errno;
890     int                  cf_open_errno, df_open_errno;
891     int                  gdp_open_errno, gdp_read_errno;
892     int                  dp_open_errno, dp_read_errno;
893     char                *gpf_path, *pf_path;
894     char                *cf_path, *df_path;
895     int                  pf_open_errno, pf_read_errno;
896     e_prefs             *prefs_p;
897
898     /* load the decode as entries of this profile */
899     load_decode_as_entries();
900
901     /* Read the preference files. */
902     prefs_p = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
903                          &pf_open_errno, &pf_read_errno, &pf_path);
904
905     if (gpf_path != NULL) {
906         if (gpf_open_errno != 0) {
907             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
908                           "Could not open global preferences file\n\"%s\": %s.", gpf_path,
909                           g_strerror(gpf_open_errno));
910         }
911         if (gpf_read_errno != 0) {
912             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
913                           "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
914                           g_strerror(gpf_read_errno));
915         }
916     }
917     if (pf_path != NULL) {
918         if (pf_open_errno != 0) {
919             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
920                           "Could not open your preferences file\n\"%s\": %s.", pf_path,
921                           g_strerror(pf_open_errno));
922         }
923         if (pf_read_errno != 0) {
924             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
925                           "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
926                           g_strerror(pf_read_errno));
927         }
928         g_free(pf_path);
929         pf_path = NULL;
930     }
931
932 #ifdef _WIN32
933     /* if the user wants a console to be always there, well, we should open one for him */
934     if (prefs_p->gui_console_open == console_open_always) {
935         create_console();
936     }
937 #endif
938
939     /* Read the capture filter file. */
940     read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
941     if (cf_path != NULL) {
942         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
943                       "Could not open your capture filter file\n\"%s\": %s.", cf_path,
944                       g_strerror(cf_open_errno));
945         g_free(cf_path);
946     }
947
948     /* Read the display filter file. */
949     read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
950     if (df_path != NULL) {
951         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
952                       "Could not open your display filter file\n\"%s\": %s.", df_path,
953                       g_strerror(df_open_errno));
954         g_free(df_path);
955     }
956
957     /* Read the disabled protocols file. */
958     read_disabled_protos_list(gdp_path, &gdp_open_errno, &gdp_read_errno,
959                               dp_path, &dp_open_errno, &dp_read_errno);
960     read_disabled_heur_dissector_list(gdp_path, &gdp_open_errno, &gdp_read_errno,
961                               dp_path, &dp_open_errno, &dp_read_errno);
962     if (*gdp_path != NULL) {
963         if (gdp_open_errno != 0) {
964             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
965                           "Could not open global disabled protocols file\n\"%s\": %s.",
966                           *gdp_path, g_strerror(gdp_open_errno));
967         }
968         if (gdp_read_errno != 0) {
969             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
970                           "I/O error reading global disabled protocols file\n\"%s\": %s.",
971                           *gdp_path, g_strerror(gdp_read_errno));
972         }
973         g_free(*gdp_path);
974         *gdp_path = NULL;
975     }
976     if (*dp_path != NULL) {
977         if (dp_open_errno != 0) {
978             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
979                           "Could not open your disabled protocols file\n\"%s\": %s.", *dp_path,
980                           g_strerror(dp_open_errno));
981         }
982         if (dp_read_errno != 0) {
983             simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
984                           "I/O error reading your disabled protocols file\n\"%s\": %s.", *dp_path,
985                           g_strerror(dp_read_errno));
986         }
987         g_free(*dp_path);
988         *dp_path = NULL;
989     }
990
991     return prefs_p;
992 }
993
994 QList<recent_item_status *> WiresharkApplication::recentItems() const {
995     return recent_items_;
996 }
997
998 void WiresharkApplication::addRecentItem(const QString filename, qint64 size, bool accessible) {
999     recent_item_status *ri = new(recent_item_status);
1000
1001     ri->filename = filename;
1002     ri->size = size;
1003     ri->accessible = accessible;
1004     ri->in_thread = false;
1005     recent_items_.prepend(ri);
1006
1007     itemStatusFinished(filename, size, accessible);
1008 }
1009
1010 static void switchTranslator(QTranslator& myTranslator, const QString& filename,
1011     const QString& searchPath)
1012 {
1013     wsApp->removeTranslator(&myTranslator);
1014
1015     if (myTranslator.load(filename, searchPath))
1016         wsApp->installTranslator(&myTranslator);
1017 }
1018
1019 void WiresharkApplication::loadLanguage(const QString& newLanguage)
1020 {
1021     QLocale locale;
1022     QString localeLanguage;
1023
1024     if (newLanguage.isEmpty() || newLanguage == "system") {
1025         localeLanguage = QLocale::system().name();
1026     } else {
1027         localeLanguage = newLanguage;
1028     }
1029
1030     locale = QLocale(localeLanguage);
1031     QLocale::setDefault(locale);
1032     switchTranslator(wsApp->translator,
1033             QString("wireshark_%1.qm").arg(localeLanguage), QString(":/i18n/"));
1034     if (QFile::exists(QString("%1/%2/wireshark_%3.qm")
1035             .arg(get_datafile_dir()).arg("languages").arg(localeLanguage)))
1036         switchTranslator(wsApp->translator,
1037                 QString("wireshark_%1.qm").arg(localeLanguage), QString(get_datafile_dir()) + QString("/languages"));
1038     if (QFile::exists(QString("%1/wireshark_%3.qm")
1039             .arg(gchar_free_to_qstring(get_persconffile_path("languages", FALSE))).arg(localeLanguage)))
1040         switchTranslator(wsApp->translator,
1041                 QString("wireshark_%1.qm").arg(localeLanguage), gchar_free_to_qstring(get_persconffile_path("languages", FALSE)));
1042     switchTranslator(wsApp->translatorQt,
1043             QString("qt_%1.qm").arg(localeLanguage),
1044             QLibraryInfo::location(QLibraryInfo::TranslationsPath));
1045 }
1046
1047 /*
1048  * Editor modelines
1049  *
1050  * Local Variables:
1051  * c-basic-offset: 4
1052  * tab-width: 8
1053  * indent-tabs-mode: nil
1054  * End:
1055  *
1056  * ex: set shiftwidth=4 tabstop=8 expandtab:
1057  * :indentSize=4:tabSize=8:noTabs=true:
1058  */