1 /* wireshark_application.cpp
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "wireshark_application.h"
26 #include "wsutil/filesystem.h"
28 #include "epan/disabled_protos.h"
30 #include "epan/timestamp.h"
32 #include "ui/decode_as_utils.h"
33 #include "ui/preference_utils.h"
34 #include "ui/recent.h"
35 #include "ui/simple_dialog.h"
38 #include "qt_ui_utils.h"
41 #include "color_filters.h"
44 #include "recent_file_status.h"
46 #include "ui/capture_globals.h"
47 #include "ui/software_update.h"
48 #include "ui/last_open_dir.h"
49 #include "ui/recent_utils.h"
52 # include "ui/win32/console_win32.h"
55 #include <QDesktopServices>
58 #include <QFileOpenEvent>
59 #include <QFontMetrics>
68 WiresharkApplication *wsApp = NULL;
70 // XXX - Copied from ui/gtk/file_dlg.c
73 static char *last_open_dir = NULL;
74 static bool updated_last_open_dir = FALSE;
75 static QList<recent_item_status *> recent_items;
78 topic_action(topic_action_e action)
80 if (wsApp) wsApp->helpTopicAction(action);
84 get_last_open_dir(void)
90 set_last_open_dir(const char *dirname)
92 if (wsApp) wsApp->setLastOpenDir(dirname);
96 * Add the capture filename to the application-wide "Recent Files" list.
97 * Contrary to the name this isn't limited to the "recent" menu.
100 * XXX - We might want to call SHAddToRecentDocs under Windows 7:
101 * http://stackoverflow.com/questions/437212/how-do-you-register-a-most-recently-used-list-with-windows-in-preparation-for-win
104 add_menu_recent_capture_file(const gchar *cf_name) {
105 QString normalized_cf_name = QString::fromUtf8(cf_name);
108 // cf_path.setPath(normalized_cf_name);
109 // normalized_cf_name = cf_path.absolutePath();
110 normalized_cf_name = QDir::cleanPath(normalized_cf_name);
111 normalized_cf_name = QDir::toNativeSeparators(normalized_cf_name);
113 recent_item_status *ri;
115 /* Iterate through the recent items list, removing duplicate entries and every
116 * item above count_max
118 unsigned int cnt = 1;
119 foreach (ri, wsApp->recentItems()) {
120 /* if this element string is one of our special items (separator, ...) or
121 * already in the list or
122 * this element is above maximum count (too old), remove it
124 if (ri->filename.length() < 1 ||
126 /* do a case insensitive compare on win32 */
127 ri->filename.compare(normalized_cf_name, Qt::CaseInsensitive) == 0 ||
129 /* do a case sensitive compare on unix */
130 ri->filename.compare(normalized_cf_name) == 0 ||
132 cnt >= prefs.gui_recent_files_count_max) {
133 wsApp->recentItems().removeOne(ri);
139 wsApp->addRecentItem(normalized_cf_name, 0, false);
142 /* write all capture filenames of the menu to the user's recent file */
143 extern "C" void menu_recent_file_write_all(FILE *rf) {
145 /* we have to iterate backwards through the children's list,
146 * so we get the latest item last in the file.
148 QListIterator<recent_item_status *> rii(recent_items);
150 while (rii.hasPrevious()) {
152 /* get capture filename from the menu item label */
153 cf_name = rii.previous()->filename;
154 if (cf_name != NULL) {
156 // fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", u3_contract_device_path(cf_name));
158 fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", cf_name.toUtf8().constData());
163 void WiresharkApplication::refreshRecentFiles(void) {
164 recent_item_status *ri;
165 RecentFileStatus *rf_status;
168 foreach (ri, recent_items) {
173 rf_thread = new QThread;
174 rf_status = new RecentFileStatus(ri->filename);
176 rf_status->moveToThread(rf_thread);
178 connect(rf_thread, SIGNAL(started()), rf_status, SLOT(start()));
180 connect(rf_status, SIGNAL(statusFound(QString, qint64, bool)), this, SLOT(itemStatusFinished(QString, qint64, bool)));
181 connect(rf_status, SIGNAL(finished()), rf_thread, SLOT(quit()));
182 connect(rf_status, SIGNAL(finished()), rf_status, SLOT(deleteLater()));
188 void WiresharkApplication::updateTaps()
190 draw_tap_listeners(FALSE);
193 void WiresharkApplication::captureCallback(int event _U_, capture_session *cap_session _U_)
197 case(capture_cb_capture_prepared):
198 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture prepared");
199 emit captureCapturePrepared(cap_session);
201 case(capture_cb_capture_update_started):
202 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update started");
203 emit captureCaptureUpdateStarted(cap_session);
205 case(capture_cb_capture_update_continue):
206 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update continue");
207 emit captureCaptureUpdateContinue(cap_session);
209 case(capture_cb_capture_update_finished):
210 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update finished");
211 emit captureCaptureUpdateFinished(cap_session);
213 case(capture_cb_capture_fixed_started):
214 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed started");
215 emit captureCaptureFixedStarted(cap_session);
217 case(capture_cb_capture_fixed_continue):
218 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed continue");
220 case(capture_cb_capture_fixed_finished):
221 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed finished");
222 emit captureCaptureFixedFinished(cap_session);
224 case(capture_cb_capture_stopping):
225 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture stopping");
226 /* Beware: this state won't be called, if the capture child
227 * closes the capturing on it's own! */
228 emit captureCaptureStopping(cap_session);
230 case(capture_cb_capture_failed):
231 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture failed");
232 emit captureCaptureFailed(cap_session);
235 g_warning("main_capture_callback: event %u unknown", event);
236 g_assert_not_reached();
238 #endif // HAVE_LIBPCAP
241 void WiresharkApplication::captureFileCallback(int event, void * data)
243 capture_file *cf = (capture_file *) data;
247 case(cf_cb_file_opened):
248 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Opened");
249 emit captureFileOpened(cf);
251 case(cf_cb_file_closing):
252 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Closing");
253 emit captureFileClosing(cf);
255 case(cf_cb_file_closed):
256 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Closed");
257 emit captureFileClosed(cf);
259 case(cf_cb_file_read_started):
260 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Read started");
261 emit captureFileReadStarted(cf);
263 case(cf_cb_file_read_finished):
264 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Read finished");
265 emit captureFileReadFinished(cf);
267 case(cf_cb_file_reload_started):
268 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Reload started");
269 emit captureFileReadStarted(cf);
271 case(cf_cb_file_reload_finished):
272 g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Reload finished");
273 emit captureFileReadFinished(cf);
276 case(cf_cb_packet_selected):
277 case(cf_cb_packet_unselected):
278 case(cf_cb_field_unselected):
279 // Pure signals and slots
282 // case(cf_cb_file_save_started): // data = string
283 // g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save started");
285 // case(cf_cb_file_save_finished):
286 // g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save finished");
288 // case(cf_cb_file_save_failed):
289 // g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save failed");
292 g_log(NULL, G_LOG_LEVEL_DEBUG, "FIX: main_cf_callback %d %p", event, data);
293 // g_warning("main_cf_callback: event %u unknown", event);
294 // g_assert_not_reached();
298 QDir WiresharkApplication::lastOpenDir() {
299 return QDir(last_open_dir);
302 void WiresharkApplication::setLastOpenDir(QString *dir_str) {
303 setLastOpenDir(dir_str->toUtf8().constData());
306 void WiresharkApplication::helpTopicAction(topic_action_e action)
310 url = topic_action_url(action);
313 QDesktopServices::openUrl(QUrl(url));
318 void WiresharkApplication::setMonospaceFont(const char *font_string) {
320 if (font_string && strlen(font_string) > 0) {
321 mono_regular_font_.fromString(font_string);
322 mono_bold_font_ = QFont(mono_regular_font_);
323 mono_bold_font_.setBold(true);
327 // http://en.wikipedia.org/wiki/Category:Monospaced_typefaces
328 const char *win_default_font = "Consolas";
329 const char *win_alt_font = "Lucida Console";
330 const char *osx_default_font = "Menlo";
331 const char *osx_alt_font = "Monaco";
332 const char *x11_default_font = "Bitstream Vera Sans Mono";
333 const QStringList x11_alt_fonts = QStringList() << "Liberation Mono" << "DejaVu Sans Mono";
334 const QStringList fallback_fonts = QStringList() << "Lucida Sans Typewriter" << "Inconsolata" << "Droid Sans Mono" << "Andale Mono" << "Courier New" << "monospace";
335 QStringList substitutes;
336 int font_size_adjust = 0;
338 // Try to pick the latest, shiniest fixed-width font for our OS.
339 #if defined(Q_OS_WIN)
340 const char *default_font = win_default_font;
341 substitutes << win_alt_font << osx_default_font << osx_alt_font << x11_default_font << x11_alt_fonts << fallback_fonts;
342 font_size_adjust = 2;
343 #elif defined(Q_OS_MAC)
344 const char *default_font = osx_default_font;
345 substitutes << osx_alt_font << win_default_font << win_alt_font << x11_default_font << x11_alt_fonts << fallback_fonts;
347 const char *default_font = x11_default_font;
348 substitutes << x11_alt_fonts << win_default_font << win_alt_font << osx_default_font << osx_alt_font << fallback_fonts;
351 mono_regular_font_.setFamily(default_font);
352 mono_regular_font_.insertSubstitutions(default_font, substitutes);
353 mono_regular_font_.setPointSize(wsApp->font().pointSize() + font_size_adjust);
354 mono_regular_font_.setBold(false);
356 mono_bold_font_ = QFont(mono_regular_font_);
357 mono_bold_font_.setBold(true);
359 g_free(prefs.gui_qt_font_name);
360 prefs.gui_qt_font_name = g_strdup(mono_regular_font_.toString().toUtf8().constData());
363 int WiresharkApplication::monospaceTextSize(const char *str, bool bold)
368 fm = new QFontMetrics(mono_bold_font_);
370 fm = new QFontMetrics(mono_regular_font_);
372 return fm->width(str);
375 QFont WiresharkApplication::monospaceFont(bool bold)
377 return bold ? mono_bold_font_ : mono_regular_font_;
380 void WiresharkApplication::setConfigurationProfile(const gchar *profile_name)
382 char *gdp_path, *dp_path;
386 /* First check if profile exists */
387 if (!profile_exists(profile_name, FALSE)) {
388 if (profile_exists(profile_name, TRUE)) {
389 char *pf_dir_path, *pf_dir_path2, *pf_filename;
390 /* Copy from global profile */
391 if (create_persconffile_profile(profile_name, &pf_dir_path) == -1) {
392 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
393 "Can't create directory\n\"%s\":\n%s.",
394 pf_dir_path, g_strerror(errno));
399 if (copy_persconffile_profile(profile_name, profile_name, TRUE, &pf_filename,
400 &pf_dir_path, &pf_dir_path2) == -1) {
401 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
402 "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
403 pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
407 g_free(pf_dir_path2);
410 /* No personal and no global profile exists */
415 /* Then check if changing to another profile */
416 if (profile_name && strcmp (profile_name, get_profile_name()) == 0) {
420 /* Get the current geometry, before writing it to disk */
421 // main_save_window_geometry(top_level);
423 if (profile_exists(get_profile_name(), FALSE)) {
424 /* Write recent file for profile we are leaving, if it still exists */
425 write_profile_recent();
428 /* Set profile name and update the status bar */
429 set_profile_name (profile_name);
430 emit configurationProfileChanged(profile_name);
432 /* Reset current preferences and apply the new */
434 // menu_prefs_reset();
436 (void) readConfigurationFiles (&gdp_path, &dp_path);
438 recent_read_profile_static(&rf_path, &rf_open_errno);
439 if (rf_path != NULL && rf_open_errno != 0) {
440 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
441 "Could not open common recent file\n\"%s\": %s.",
442 rf_path, g_strerror(rf_open_errno));
444 if (recent.gui_fileopen_remembered_dir &&
445 test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
446 set_last_open_dir(recent.gui_fileopen_remembered_dir);
448 timestamp_set_type (recent.gui_time_format);
449 timestamp_set_seconds_type (recent.gui_seconds_format);
450 color_filters_enable(recent.packet_list_colorize);
451 tap_update_timer_.setInterval(prefs.tap_update_interval);
453 prefs_to_capture_opts();
455 emit filterExpressionsChanged();
456 // macros_post_update();
458 /* Enable all protocols and disable from the disabled list */
460 if (gdp_path == NULL && dp_path == NULL) {
461 set_disabled_protos_list();
464 /* Reload color filters */
465 color_filters_reload();
467 // user_font_apply();
469 /* Update menus with new recent values */
470 // menu_recent_read_finished();
473 void WiresharkApplication::setLastOpenDir(const char *dir_name)
476 gchar *new_last_open_dir;
479 len = strlen(dir_name);
480 if (dir_name[len-1] == G_DIR_SEPARATOR) {
481 new_last_open_dir = g_strconcat(dir_name, (char *)NULL);
484 new_last_open_dir = g_strconcat(dir_name,
485 G_DIR_SEPARATOR_S, (char *)NULL);
488 if (last_open_dir == NULL ||
489 strcmp(last_open_dir, new_last_open_dir) != 0)
490 updated_last_open_dir = TRUE;
493 new_last_open_dir = NULL;
494 if (last_open_dir != NULL)
495 updated_last_open_dir = TRUE;
498 g_free(last_open_dir);
499 last_open_dir = new_last_open_dir;
502 bool WiresharkApplication::event(QEvent *event)
504 if (event->type() == QEvent::FileOpen) {
505 QFileOpenEvent *foe = static_cast<QFileOpenEvent *>(event);
506 if (foe && foe->file().length() > 0) {
507 QString cf_path(foe->file());
509 emit openCaptureFile(cf_path);
511 pending_open_files_.append(cf_path);
516 return QApplication::event(event);
519 void WiresharkApplication::clearRecentItems() {
520 recent_item_status *ri;
522 foreach (ri, recent_items) {
523 recent_items.removeOne(ri);
526 emit updateRecentItemStatus(NULL, 0, false);
529 void WiresharkApplication::cleanup()
531 software_update_cleanup();
532 /* write user's recent file to disk
533 * It is no problem to write this file, even if we do not quit */
534 write_profile_recent();
538 void WiresharkApplication::itemStatusFinished(const QString &filename, qint64 size, bool accessible) {
539 recent_item_status *ri;
540 RecentFileStatus *rf_status = qobject_cast<RecentFileStatus *>(QObject::sender());
542 // g_log(NULL, G_LOG_LEVEL_DEBUG, "rf isf %d", recent_items.count());
543 foreach (ri, recent_items) {
544 if (filename == ri->filename && (size != ri->size || accessible != ri->accessible)) {
546 ri->accessible = accessible;
547 ri->in_thread = false;
549 // g_log(NULL, G_LOG_LEVEL_DEBUG, "rf update %s", filename.toUtf8().constData());
550 emit updateRecentItemStatus(filename, size, accessible);
559 WiresharkApplication::WiresharkApplication(int &argc, char **argv) :
560 QApplication(argc, argv),
565 Q_INIT_RESOURCE(about);
566 Q_INIT_RESOURCE(display_filter);
567 Q_INIT_RESOURCE(i18n);
568 Q_INIT_RESOURCE(layout);
569 Q_INIT_RESOURCE(status);
570 Q_INIT_RESOURCE(toolbar);
571 Q_INIT_RESOURCE(welcome);
574 /* RichEd20.DLL is needed for native file dialog filter entries. */
575 if (QLibrary::isLibrary("riched20.dll")) {
576 QLibrary riched20("riched20.dll");
578 if (!riched20.isLoaded()) {
579 qDebug() << riched20.errorString();
584 setAttribute(Qt::AA_DontShowIconsInMenus, true);
586 recent_timer_.setParent(this);
587 connect(&recent_timer_, SIGNAL(timeout()), this, SLOT(refreshRecentFiles()));
588 recent_timer_.start(2000);
590 tap_update_timer_.setParent(this);
591 tap_update_timer_.setInterval(TAP_UPDATE_DEFAULT_INTERVAL);
592 connect(this, SIGNAL(appInitialized()), &tap_update_timer_, SLOT(start()));
593 connect(&tap_update_timer_, SIGNAL(timeout()), this, SLOT(updateTaps()));
595 connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
598 void WiresharkApplication::registerUpdate(register_action_e action, const char *message)
600 emit splashUpdate(action, message);
603 void WiresharkApplication::emitAppSignal(AppSignal signal)
607 emit columnsChanged();
609 case FilterExpressionsChanged:
610 emit filterExpressionsChanged();
611 case PreferencesChanged:
612 emit preferencesChanged();
614 case PacketDissectionChanged:
615 emit packetDissectionChanged();
617 case StaticRecentFilesRead:
618 emit recentFilesRead();
625 void WiresharkApplication::allSystemsGo()
628 emit appInitialized();
629 while (pending_open_files_.length() > 0) {
630 emit openCaptureFile(pending_open_files_.front());
631 pending_open_files_.pop_front();
633 software_update_init();
636 e_prefs * WiresharkApplication::readConfigurationFiles(char **gdp_path, char **dp_path)
638 int gpf_open_errno, gpf_read_errno;
639 int cf_open_errno, df_open_errno;
640 int gdp_open_errno, gdp_read_errno;
641 int dp_open_errno, dp_read_errno;
642 char *gpf_path, *pf_path;
643 char *cf_path, *df_path;
644 int pf_open_errno, pf_read_errno;
647 /* load the decode as entries of this profile */
648 load_decode_as_entries();
650 /* Read the preference files. */
651 prefs_p = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
652 &pf_open_errno, &pf_read_errno, &pf_path);
654 if (gpf_path != NULL) {
655 if (gpf_open_errno != 0) {
656 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
657 "Could not open global preferences file\n\"%s\": %s.", gpf_path,
658 g_strerror(gpf_open_errno));
660 if (gpf_read_errno != 0) {
661 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
662 "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
663 g_strerror(gpf_read_errno));
666 if (pf_path != NULL) {
667 if (pf_open_errno != 0) {
668 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
669 "Could not open your preferences file\n\"%s\": %s.", pf_path,
670 g_strerror(pf_open_errno));
672 if (pf_read_errno != 0) {
673 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
674 "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
675 g_strerror(pf_read_errno));
682 /* if the user wants a console to be always there, well, we should open one for him */
683 if (prefs_p->gui_console_open == console_open_always) {
688 /* Read the capture filter file. */
689 read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
690 if (cf_path != NULL) {
691 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
692 "Could not open your capture filter file\n\"%s\": %s.", cf_path,
693 g_strerror(cf_open_errno));
697 /* Read the display filter file. */
698 read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
699 if (df_path != NULL) {
700 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
701 "Could not open your display filter file\n\"%s\": %s.", df_path,
702 g_strerror(df_open_errno));
706 /* Read the disabled protocols file. */
707 read_disabled_protos_list(gdp_path, &gdp_open_errno, &gdp_read_errno,
708 dp_path, &dp_open_errno, &dp_read_errno);
709 if (*gdp_path != NULL) {
710 if (gdp_open_errno != 0) {
711 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
712 "Could not open global disabled protocols file\n\"%s\": %s.",
713 *gdp_path, g_strerror(gdp_open_errno));
715 if (gdp_read_errno != 0) {
716 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
717 "I/O error reading global disabled protocols file\n\"%s\": %s.",
718 *gdp_path, g_strerror(gdp_read_errno));
723 if (*dp_path != NULL) {
724 if (dp_open_errno != 0) {
725 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
726 "Could not open your disabled protocols file\n\"%s\": %s.", *dp_path,
727 g_strerror(dp_open_errno));
729 if (dp_read_errno != 0) {
730 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
731 "I/O error reading your disabled protocols file\n\"%s\": %s.", *dp_path,
732 g_strerror(dp_read_errno));
741 QList<recent_item_status *> WiresharkApplication::recentItems() const {
745 void WiresharkApplication::addRecentItem(const QString &filename, qint64 size, bool accessible) {
746 recent_item_status *ri = new(recent_item_status);
748 ri->filename = filename;
750 ri->accessible = accessible;
751 ri->in_thread = false;
752 recent_items.prepend(ri);
754 itemStatusFinished(filename, size, accessible);
763 * indent-tabs-mode: nil
766 * ex: set shiftwidth=4 tabstop=8 expandtab:
767 * :indentSize=4:tabSize=8:noTabs=true: