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 "main_window.h"
25 #include "ui_main_window.h"
29 #include <epan/filesystem.h>
30 #include <epan/prefs.h>
32 //#include <wiretap/wtap.h>
36 #include "capture-pcap-util.h"
37 #include "capture_ui_utils.h"
38 #include "capture_session.h"
41 #include "ui/alert_box.h"
42 #include "ui/main_statusbar.h"
43 #include "ui/capture_globals.h"
45 #include "wireshark_application.h"
46 #include "proto_tree.h"
47 #include "byte_view_tab.h"
48 #include "display_filter_edit.h"
49 #include "import_text_dialog.h"
50 #include "export_dissection_dialog.h"
52 #include "qt_ui_utils.h"
54 #include <QTreeWidget>
57 #include <QToolButton>
59 #include <QMetaObject>
60 #include <QMessageBox>
62 #ifdef QT_MACEXTRAS_LIB
63 #include <QtMacExtras/QMacNativeToolBar>
68 //menu_recent_file_write_all
70 // If we ever add support for multiple windows this will need to be replaced.
71 static MainWindow *gbl_cur_main_window = NULL;
73 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
75 gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
78 MainWindow::MainWindow(QWidget *parent) :
80 main_ui_(new Ui::MainWindow),
81 df_combo_box_(new DisplayFilterCombo()),
83 previous_focus_(NULL),
84 capture_stopping_(false),
91 gbl_cur_main_window = this;
92 main_ui_->setupUi(this);
93 setTitlebarForCaptureFile();
94 setMenusForCaptureFile();
95 setForCapturedPackets(false);
96 setMenusForSelectedPacket();
97 setMenusForSelectedTreeRow();
98 setForCaptureInProgress(false);
99 setMenusForFileSet(false);
100 interfaceSelectionChanged();
102 //To prevent users use features before initialization complete
103 //Otherwise unexpected problems may occur
104 setFeaturesEnabled(false);
105 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
107 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
110 connect(&summary_dialog_, SIGNAL(captureCommentChanged()), this, SLOT(updateForUnsavedChanges()));
112 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
113 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
114 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
115 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)), main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
116 connect(df_edit, SIGNAL(filterPackets(QString&,bool)), this, SLOT(filterPackets(QString&,bool)));
117 connect(df_edit, SIGNAL(addBookmark(QString)), this, SLOT(addDisplayFilterButton(QString)));
118 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
120 // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
121 // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
122 main_ui_->actionFileOpen->setIcon(
123 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
124 // main_ui_->actionFileSave set in main_window.ui
125 main_ui_->actionFileClose->setIcon(
126 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_DialogCloseButton)));
128 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
129 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
130 // https://bugreports.qt-project.org/browse/QTBUG-22433
131 // This property is obsolete in Qt5 so this issue may be fixed in that version.
132 main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
134 main_ui_->goToFrame->hide();
135 // XXX For some reason the cursor is drawn funny with an input mask set
136 // https://bugreports.qt-project.org/browse/QTBUG-7174
138 main_ui_->searchFrame->hide();
139 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(QString&)),
140 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
142 #if defined(Q_OS_MAC)
143 #ifdef QT_MACEXTRAS_LIB
144 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
145 ntb->setIconSize(QSize(24, 24));
146 #endif // QT_MACEXTRAS_LIB
148 foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
149 foreach (QAction *act, menu->actions()) {
150 act->setIconVisibleInMenu(false);
153 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
154 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
155 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
157 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
161 #ifdef HAVE_SOFTWARE_UPDATE
162 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
163 QAction *update_action = new QAction(tr("Check for Updates..."), main_ui_->menuHelp);
164 main_ui_->menuHelp->insertAction(update_sep, update_action);
165 connect(update_action, SIGNAL(triggered()), this, SLOT(on_actionHelpCheckForUpdates_triggered()));
168 packet_splitter_ = new QSplitter(main_ui_->mainStack);
169 packet_splitter_->setObjectName(QString::fromUtf8("splitterV"));
170 packet_splitter_->setOrientation(Qt::Vertical);
172 packet_list_ = new PacketList(packet_splitter_);
174 proto_tree_ = new ProtoTree(packet_splitter_);
175 proto_tree_->setHeaderHidden(true);
176 proto_tree_->installEventFilter(this);
178 ByteViewTab *byte_view_tab = new ByteViewTab(packet_splitter_);
179 byte_view_tab->setTabPosition(QTabWidget::South);
180 byte_view_tab->setDocumentMode(true);
182 packet_list_->setProtoTree(proto_tree_);
183 packet_list_->setByteViewTab(byte_view_tab);
184 packet_list_->installEventFilter(this);
186 packet_splitter_->addWidget(packet_list_);
187 packet_splitter_->addWidget(proto_tree_);
188 packet_splitter_->addWidget(byte_view_tab);
190 main_ui_->mainStack->addWidget(packet_splitter_);
192 main_welcome_ = main_ui_->welcomePage;
195 connect(wsApp, SIGNAL(captureCapturePrepared(capture_session *)),
196 this, SLOT(captureCapturePrepared(capture_session *)));
197 connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
198 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
199 connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
200 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
201 connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_session *)),
202 this, SLOT(captureCaptureFixedStarted(capture_session *)));
203 connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_session *)),
204 this, SLOT(captureCaptureFixedFinished(capture_session *)));
205 connect(wsApp, SIGNAL(captureCaptureStopping(capture_session *)),
206 this, SLOT(captureCaptureStopping(capture_session *)));
207 connect(wsApp, SIGNAL(captureCaptureFailed(capture_session *)),
208 this, SLOT(captureCaptureFailed(capture_session *)));
211 connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
212 this, SLOT(captureFileOpened(const capture_file*)));
213 connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
214 this, SLOT(captureFileReadStarted(const capture_file*)));
215 connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
216 this, SLOT(captureFileReadFinished(const capture_file*)));
217 connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
218 this, SLOT(captureFileClosing(const capture_file*)));
219 connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
220 this, SLOT(captureFileClosed(const capture_file*)));
221 connect(wsApp, SIGNAL(columnsChanged()),
222 this, SLOT(recreatePacketList()));
223 connect(wsApp, SIGNAL(packetDissectionChanged()),
224 this, SLOT(redissectPackets()));
225 connect(wsApp, SIGNAL(appInitialized()),
226 this, SLOT(filterExpressionsChanged()));
227 connect(wsApp, SIGNAL(filterExpressionsChanged()),
228 this, SLOT(filterExpressionsChanged()));
230 connect(main_welcome_, SIGNAL(startCapture()),
231 this, SLOT(startCapture()));
232 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
233 this, SLOT(openCaptureFile(QString&)));
235 connect(this, SIGNAL(setCaptureFile(capture_file*)),
236 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
237 connect(this, SIGNAL(setCaptureFile(capture_file*)),
238 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
239 connect(this, SIGNAL(setCaptureFile(capture_file*)),
240 packet_list_, SLOT(setCaptureFile(capture_file*)));
241 connect(this, SIGNAL(setCaptureFile(capture_file*)),
242 byte_view_tab, SLOT(setCaptureFile(capture_file*)));
244 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
245 packet_list_, SLOT(goNextPacket()));
246 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
247 packet_list_, SLOT(goPreviousPacket()));
248 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
249 packet_list_, SLOT(goFirstPacket()));
250 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
251 packet_list_, SLOT(goLastPacket()));
253 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
254 proto_tree_, SLOT(expandSubtrees()));
255 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
256 proto_tree_, SLOT(expandAll()));
257 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
258 proto_tree_, SLOT(collapseAll()));
260 connect(packet_list_->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
261 this, SLOT(setMenusForSelectedPacket()));
262 connect(packet_list_, SIGNAL(packetDissectionChanged()),
263 this, SLOT(redissectPackets()));
265 connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
266 main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
268 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
269 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
271 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
272 this, SLOT(openCaptureFile(QString&)));
274 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
276 connect(iface_tree, SIGNAL(itemSelectionChanged()),
277 this, SLOT(interfaceSelectionChanged()));
279 main_ui_->mainStack->setCurrentWidget(main_welcome_);
282 MainWindow::~MainWindow()
287 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
289 pipe_source_ = source;
290 pipe_child_process_ = child_process;
291 pipe_user_data_ = user_data;
292 pipe_input_cb_ = input_cb;
295 /* Tricky to use pipes in win9x, as no concept of wait. NT can
296 do this but that doesn't cover all win32 platforms. GTK can do
297 this but doesn't seem to work over processes. Attempt to do
298 something similar here, start a timer and check for data on every
300 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
303 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
307 pipe_timer_ = new QTimer(this);
308 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
309 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
310 pipe_timer_->start(200);
312 if (pipe_notifier_) {
313 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
314 delete pipe_notifier_;
317 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
318 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
319 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
320 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
325 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
327 // The user typed some text. Start filling in a filter.
328 // We may need to be more choosy here. We just need to catch events for the packet list,
329 // proto tree, and main welcome widgets.
330 if (event->type() == QEvent::KeyPress) {
331 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
332 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
333 df_combo_box_->lineEdit()->insert(kevt->text());
334 df_combo_box_->lineEdit()->setFocus();
339 return QObject::eventFilter(obj, event);
342 void MainWindow::keyPressEvent(QKeyEvent *event) {
344 // Explicitly focus on the display filter combo.
345 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
346 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
350 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
351 if (event->modifiers() == Qt::NoModifier) {
352 if (event->key() == Qt::Key_Escape) {
353 on_goToCancel_clicked();
354 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
358 return; // goToLineEdit didn't want it and we don't either.
361 // Move up & down the packet list.
362 if (event->key() == Qt::Key_F7) {
363 packet_list_->goPreviousPacket();
364 } else if (event->key() == Qt::Key_F8) {
365 packet_list_->goNextPacket();
368 // Move along, citizen.
369 QMainWindow::keyPressEvent(event);
372 void MainWindow::closeEvent(QCloseEvent *event) {
373 /* If we're in the middle of stopping a capture, don't do anything;
374 the user can try deleting the window after the capture stops. */
375 if (capture_stopping_) {
380 if(testCaptureFileClose(true)) {
381 /* QCoreApplication::quit() won't exit properly
382 * because when during initialization phase we are not in the event loop */
394 void MainWindow::mergeCaptureFile()
396 QString file_name = "";
397 QString display_filter = "";
398 dfilter_t *rfcode = NULL;
404 if (prefs.gui_ask_unsaved) {
405 if (cf_has_unsaved_data(cap_file_)) {
406 QMessageBox msg_dialog;
407 gchar *display_basename;
410 msg_dialog.setIcon(QMessageBox::Question);
411 /* This file has unsaved data; ask the user whether to save
413 if (cap_file_->is_tempfile) {
414 msg_dialog.setText(tr("Save packets before merging?"));
415 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
418 * Format the message.
420 display_basename = g_filename_display_basename(cap_file_->filename);
421 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
422 g_free(display_basename);
423 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
426 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
427 msg_dialog.setDefaultButton(QMessageBox::Save);
429 response = msg_dialog.exec();
433 case QMessageBox::Save:
434 /* Save the file but don't close it */
435 saveCaptureFile(cap_file_, FALSE);
438 case QMessageBox::Cancel:
440 /* Don't do the merge. */
447 CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
449 cf_status_t merge_status;
450 char *in_filenames[2];
453 switch (prefs.gui_fileopen_style) {
455 case FO_STYLE_LAST_OPENED:
456 /* The user has specified that we should start out in the last directory
457 we looked in. If we've already opened a file, use its containing
458 directory, if we could determine it, as the directory, otherwise
459 use the "last opened" directory saved in the preferences file if
461 /* This is now the default behaviour in file_selection_new() */
464 case FO_STYLE_SPECIFIED:
465 /* The user has specified that we should always start out in a
466 specified directory; if they've specified that directory,
467 start out by showing the files in that dir. */
468 if (prefs.gui_fileopen_dir[0] != '\0')
469 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
473 if (merge_dlg.merge(file_name)) {
474 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
475 cf_set_rfcode(cap_file_, rfcode);
477 /* Not valid. Tell the user, and go back and run the file
478 selection box again once they dismiss the alert. */
479 //bad_dfilter_alert_box(top_level, display_filter->str);
480 QMessageBox::warning(this, tr("Invalid Display Filter"),
481 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
489 file_type = cap_file_->cd_t;
491 /* Try to merge or append the two files */
493 if (merge_dlg.mergeType() == 0) {
494 /* chronological order */
495 in_filenames[0] = cap_file_->filename;
496 in_filenames[1] = file_name.toUtf8().data();
497 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
498 } else if (merge_dlg.mergeType() <= 0) {
500 in_filenames[0] = file_name.toUtf8().data();
501 in_filenames[1] = cap_file_->filename;
502 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
505 in_filenames[0] = cap_file_->filename;
506 in_filenames[1] = file_name.toUtf8().data();
507 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
510 if (merge_status != CF_OK) {
512 dfilter_free(rfcode);
519 /* Try to open the merged capture file. */
521 if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
522 /* We couldn't open it; fail. */
525 dfilter_free(rfcode);
530 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
531 it closed the previous capture file, and thus destroyed any
532 previous read filter attached to "cf"). */
533 cfile.rfcode = rfcode;
535 switch (cf_read(&cfile, FALSE)) {
539 /* Just because we got an error, that doesn't mean we were unable
540 to read any of the file; we handle what we could get from the
544 case CF_READ_ABORTED:
545 /* The user bailed out of re-reading the capture file; the
546 capture file has been closed - just free the capture file name
547 string and return (without changing the last containing
553 /* Save the name of the containing directory specified in the path name,
554 if any; we can write over cf_merged_name, which is a good thing, given that
555 "get_dirname()" does write over its argument. */
556 wsApp->setLastOpenDir(get_dirname(tmpname));
558 df_combo_box_->setEditText(display_filter);
559 main_ui_->statusBar->showExpert();
565 void MainWindow::importCaptureFile() {
566 ImportTextDialog import_dlg;
568 if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
573 if (import_dlg.result() != QDialog::Accepted) {
574 main_ui_->mainStack->setCurrentWidget(main_welcome_);
578 openCaptureFile(import_dlg.capfileName());
581 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
583 gboolean discard_comments;
585 if (cf->is_tempfile) {
586 /* This is a temporary capture file, so saving it means saving
587 it to a permanent file. Prompt the user for a location
588 to which to save it. Don't require that the file format
589 support comments - if it's a temporary capture file, it's
590 probably pcap-ng, which supports comments and, if it's
591 not pcap-ng, let the user decide what they want to do
592 if they've added comments. */
593 saveAsCaptureFile(cf, FALSE, stay_closed);
595 if (cf->unsaved_changes) {
596 cf_write_status_t status;
598 /* This is not a temporary capture file, but it has unsaved
599 changes, so saving it means doing a "safe save" on top
600 of the existing file, in the same format - no UI needed
601 unless the file has comments and the file's format doesn't
604 If the file has comments, does the file's format support them?
605 If not, ask the user whether they want to discard the comments
606 or choose a different format. */
607 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
610 /* The file can be saved in the specified format as is;
611 just drive on and save in the format they selected. */
612 discard_comments = FALSE;
615 case SAVE_WITHOUT_COMMENTS:
616 /* The file can't be saved in the specified format as is,
617 but it can be saved without the comments, and the user
618 said "OK, discard the comments", so save it in the
619 format they specified without the comments. */
620 discard_comments = TRUE;
623 case SAVE_IN_ANOTHER_FORMAT:
624 /* There are file formats in which we can save this that
625 support comments, and the user said not to delete the
626 comments. Do a "Save As" so the user can select
627 one of those formats and choose a file name. */
628 saveAsCaptureFile(cf, TRUE, stay_closed);
632 /* The user said "forget it". Just return. */
636 /* Squelch warnings that discard_comments is being used
638 g_assert_not_reached();
642 /* XXX - cf->filename might get freed out from under us, because
643 the code path through which cf_save_packets() goes currently
644 closes the current file and then opens and reloads the saved file,
645 so make a copy and free it later. */
646 file_name = cf->filename;
647 status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
648 discard_comments, stay_closed);
652 /* The save succeeded; we're done.
653 If we discarded comments, redraw the packet list to reflect
654 any packets that no longer have comments. */
655 if (discard_comments)
656 packet_list_queue_draw();
658 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
659 updateForUnsavedChanges(); // we update the title bar to remove the *
664 XXX - OK, what do we do now? Let them try a
665 "Save As", in case they want to try to save to a
666 different directory r file system? */
669 case CF_WRITE_ABORTED:
670 /* The write was aborted; just drive on. */
674 /* Otherwise just do nothing. */
678 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
679 QString file_name = "";
682 cf_write_status_t status;
684 gboolean discard_comments = FALSE;
691 CaptureFileDialog save_as_dlg(this, cf);
693 switch (prefs.gui_fileopen_style) {
695 case FO_STYLE_LAST_OPENED:
696 /* The user has specified that we should start out in the last directory
697 we looked in. If we've already opened a file, use its containing
698 directory, if we could determine it, as the directory, otherwise
699 use the "last opened" directory saved in the preferences file if
701 /* This is now the default behaviour in file_selection_new() */
704 case FO_STYLE_SPECIFIED:
705 /* The user has specified that we should always start out in a
706 specified directory; if they've specified that directory,
707 start out by showing the files in that dir. */
708 if (prefs.gui_fileopen_dir[0] != '\0')
709 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
713 /* If the file has comments, does the format the user selected
714 support them? If not, ask the user whether they want to
715 discard the comments or choose a different format. */
716 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
719 /* The file can be saved in the specified format as is;
720 just drive on and save in the format they selected. */
721 discard_comments = FALSE;
724 case SAVE_WITHOUT_COMMENTS:
725 /* The file can't be saved in the specified format as is,
726 but it can be saved without the comments, and the user
727 said "OK, discard the comments", so save it in the
728 format they specified without the comments. */
729 discard_comments = TRUE;
732 case SAVE_IN_ANOTHER_FORMAT:
733 /* There are file formats in which we can save this that
734 support comments, and the user said not to delete the
735 comments. The combo box of file formats has had the
736 formats that don't support comments trimmed from it,
737 so run the dialog again, to let the user decide
738 whether to save in one of those formats or give up. */
739 discard_comments = FALSE;
740 must_support_comments = TRUE;
744 /* The user said "forget it". Just get rid of the dialog box
748 file_type = save_as_dlg.selectedFileType();
749 compressed = save_as_dlg.isCompressed();
751 fileAddExtension(file_name, file_type, compressed);
754 // /* If the file exists and it's user-immutable or not writable,
755 // ask the user whether they want to override that. */
756 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
757 // /* They don't. Let them try another file name or cancel. */
762 /* Attempt to save the file */
763 status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
764 discard_comments, stay_closed);
768 /* The save succeeded; we're done. */
769 /* Save the directory name for future file dialogs. */
770 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
771 set_last_open_dir(dirname);
772 /* If we discarded comments, redraw the packet list to reflect
773 any packets that no longer have comments. */
774 if (discard_comments)
775 packet_list_queue_draw();
777 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
778 updateForUnsavedChanges(); // we update the title bar to remove the *
782 /* The save failed; let the user try again. */
785 case CF_WRITE_ABORTED:
786 /* The user aborted the save; just return. */
793 void MainWindow::exportSelectedPackets() {
794 QString file_name = "";
797 packet_range_t range;
798 cf_write_status_t status;
800 gboolean discard_comments = FALSE;
805 /* Init the packet range */
806 packet_range_init(&range, cap_file_);
807 range.process_filtered = TRUE;
808 range.include_dependents = TRUE;
811 CaptureFileDialog esp_dlg(this, cap_file_);
813 switch (prefs.gui_fileopen_style) {
815 case FO_STYLE_LAST_OPENED:
816 /* The user has specified that we should start out in the last directory
817 we looked in. If we've already opened a file, use its containing
818 directory, if we could determine it, as the directory, otherwise
819 use the "last opened" directory saved in the preferences file if
821 /* This is now the default behaviour in file_selection_new() */
824 case FO_STYLE_SPECIFIED:
825 /* The user has specified that we should always start out in a
826 specified directory; if they've specified that directory,
827 start out by showing the files in that dir. */
828 if (prefs.gui_fileopen_dir[0] != '\0')
829 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
833 /* If the file has comments, does the format the user selected
834 support them? If not, ask the user whether they want to
835 discard the comments or choose a different format. */
836 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
839 /* The file can be saved in the specified format as is;
840 just drive on and save in the format they selected. */
841 discard_comments = FALSE;
844 case SAVE_WITHOUT_COMMENTS:
845 /* The file can't be saved in the specified format as is,
846 but it can be saved without the comments, and the user
847 said "OK, discard the comments", so save it in the
848 format they specified without the comments. */
849 discard_comments = TRUE;
852 case SAVE_IN_ANOTHER_FORMAT:
853 /* There are file formats in which we can save this that
854 support comments, and the user said not to delete the
855 comments. The combo box of file formats has had the
856 formats that don't support comments trimmed from it,
857 so run the dialog again, to let the user decide
858 whether to save in one of those formats or give up. */
859 discard_comments = FALSE;
863 /* The user said "forget it". Just get rid of the dialog box
869 * Check that we're not going to save on top of the current
871 * We do it here so we catch all cases ...
872 * Unfortunately, the file requester gives us an absolute file
873 * name and the read file name may be relative (if supplied on
874 * the command line). From Joerg Mayer.
876 if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
878 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
880 msg_box.setIcon(QMessageBox::Critical);
881 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
882 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
883 msg_box.setStandardButtons(QMessageBox::Ok);
884 msg_box.setDefaultButton(QMessageBox::Ok);
886 g_free(display_basename);
890 file_type = esp_dlg.selectedFileType();
891 compressed = esp_dlg.isCompressed();
892 fileAddExtension(file_name, file_type, compressed);
895 // /* If the file exists and it's user-immutable or not writable,
896 // ask the user whether they want to override that. */
897 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
898 // /* They don't. Let them try another file name or cancel. */
903 /* Attempt to save the file */
904 status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
908 /* The save succeeded; we're done. */
909 /* Save the directory name for future file dialogs. */
910 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
911 set_last_open_dir(dirname);
912 /* If we discarded comments, redraw the packet list to reflect
913 any packets that no longer have comments. */
914 if (discard_comments)
915 packet_list_queue_draw();
919 /* The save failed; let the user try again. */
922 case CF_WRITE_ABORTED:
923 /* The user aborted the save; just return. */
930 void MainWindow::exportDissections(export_type_e export_type) {
931 ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
932 packet_range_t range;
937 /* Init the packet range */
938 packet_range_init(&range, cap_file_);
939 range.process_filtered = TRUE;
940 range.include_dependents = TRUE;
945 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
946 QString file_name_lower;
948 GSList *extensions_list;
949 gboolean add_extension;
952 * Append the default file extension if there's none given by
953 * the user or if they gave one that's not one of the valid
954 * extensions for the file type.
956 file_name_lower = file_name.toLower();
957 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
958 if (extensions_list != NULL) {
961 /* We have one or more extensions for this file type.
962 Start out assuming we need to add the default one. */
963 add_extension = TRUE;
965 /* OK, see if the file has one of those extensions. */
966 for (extension = extensions_list; extension != NULL;
967 extension = g_slist_next(extension)) {
968 file_suffix += tr(".") + (char *)extension->data;
969 if (file_name_lower.endsWith(file_suffix)) {
971 * The file name has one of the extensions for
974 add_extension = FALSE;
977 file_suffix += ".gz";
978 if (file_name_lower.endsWith(file_suffix)) {
980 * The file name has one of the extensions for
983 add_extension = FALSE;
988 /* We have no extensions for this file type. Don't add one. */
989 add_extension = FALSE;
992 if (wtap_default_file_extension(file_type) != NULL) {
993 file_name += tr(".") + wtap_default_file_extension(file_type);
1001 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1002 bool capture_in_progress = FALSE;
1004 if (!cap_file_ || cap_file_->state == FILE_CLOSED)
1005 return true; /* Already closed, nothing to do */
1008 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1009 /* This is true if we're reading a capture file *or* if we're doing
1010 a live capture. If we're reading a capture file, the main loop
1011 is busy reading packets, and only accepting input from the
1012 progress dialog, so we can't get here, so this means we're
1014 capture_in_progress = TRUE;
1018 if (prefs.gui_ask_unsaved) {
1019 if (cf_has_unsaved_data(cap_file_) || capture_in_progress) {
1020 QMessageBox msg_dialog;
1022 QPushButton *saveButton;
1023 QPushButton *discardButton;
1025 msg_dialog.setIcon(QMessageBox::Question);
1027 /* This file has unsaved data or there's a capture in
1028 progress; ask the user whether to save the data. */
1029 if (cap_file_->is_tempfile) {
1031 msg_dialog.setText(tr("You have unsaved packets"));
1032 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1034 if (capture_in_progress) {
1035 question.append(tr("Do you want to stop the capture and save the captured packets"));
1037 question.append(tr("Do you want to save the captured packets"));
1039 question.append(before_what).append(tr("?"));
1040 msg_dialog.setInformativeText(question);
1045 * Format the message.
1047 if (capture_in_progress) {
1048 question.append(tr("Do you want to stop the capture and save the captured packets"));
1049 question.append(before_what).append(tr("?"));
1050 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1052 gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1053 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1054 .arg(display_basename)
1057 g_free(display_basename);
1058 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1062 // XXX Text comes from ui/gtk/stock_icons.[ch]
1063 // Note that the button roles differ from the GTK+ version.
1064 // Cancel = RejectRole
1065 // Save = AcceptRole
1066 // Don't Save = DestructiveRole
1067 msg_dialog.addButton(QMessageBox::Cancel);
1069 if (capture_in_progress) {
1070 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1072 saveButton = msg_dialog.addButton(QMessageBox::Save);
1074 msg_dialog.setDefaultButton(saveButton);
1077 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1078 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1079 QMessageBox::DestructiveRole);
1081 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1082 QMessageBox::DestructiveRole);
1085 if (capture_in_progress) {
1086 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1087 QMessageBox::DestructiveRole);
1089 discardButton = msg_dialog.addButton(QMessageBox::Discard);
1094 /* According to the Qt doc:
1095 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1097 * Therefore we should use clickedButton() to determine which button was clicked. */
1099 if(msg_dialog.clickedButton() == saveButton)
1102 /* If there's a capture in progress, we have to stop the capture
1103 and then do the save. */
1104 if (capture_in_progress)
1107 /* Save the file and close it */
1108 saveCaptureFile(cap_file_, TRUE);
1110 else if(msg_dialog.clickedButton() == discardButton)
1114 * If there's a capture in progress; we have to stop the capture
1115 * and then do the close.
1117 if (capture_in_progress)
1120 /* Just close the file, discarding changes */
1121 cf_close(cap_file_);
1124 else //cancelButton or some other unspecified button
1130 /* Unchanged file, just close it */
1131 cf_close(cap_file_);
1134 /* User asked not to be bothered by those prompts, just close it.
1135 XXX - should that apply only to saving temporary files? */
1137 /* If there's a capture in progress, we have to stop the capture
1138 and then do the close. */
1139 if (capture_in_progress)
1142 cf_close(cap_file_);
1145 return true; /* File closed */
1148 void MainWindow::captureStop() {
1151 while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1152 WiresharkApplication::processEvents();
1157 void MainWindow::setTitlebarForCaptureFile()
1159 if (cap_file_ && cap_file_->filename) {
1160 setWindowModified(cf_has_unsaved_data(cap_file_));
1162 // Qt *REALLY* doesn't like windows that sometimes have a
1163 // title set with setWindowTitle() and other times have a
1164 // file path set; apparently, once you've set the title
1165 // with setWindowTitle(), it sticks, and setWindowFilePath()
1166 // has no effect. It appears to can clear the title with
1167 // setWindowTitle(NULL), but that clears the actual title in
1168 // the title bar, and setWindowFilePath() then, I guess, sees
1169 // that there's already a file path, and does nothing, leaving
1170 // the title bar empty. So you then have to clear the file path
1171 // with setWindowFilePath(NULL), and then set it.
1173 // Maybe there's a #include "you're holding it wrong" here.
1174 // However, I really don't want to hear from people who think
1175 // that a window can never be associated with something other
1176 // than a user file at time T1 and with a user file at time T2,
1177 // given that, in Wireshark, a window can be associated with a
1178 // live capture at time T1 and then, after you've saved the live
1179 // capture to a user file, associated with a user file at time T2.
1181 if (cap_file_->is_tempfile) {
1183 // For a temporary file, put the source of the data
1184 // in the window title, not whatever random pile
1185 // of characters is the last component of the path
1188 // XXX - on non-Mac platforms, put in the application
1192 setWindowFilePath(NULL);
1193 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1194 setWindowTitle(window_name);
1195 g_free(window_name);
1198 // For a user file, set the full path; that way,
1199 // for OS X, it'll set the "proxy icon". Qt
1200 // handles extracting the last component.
1202 // Sadly, some UN*Xes don't necessarily use UTF-8
1203 // for their file names, so we have to map the
1204 // file path to UTF-8. If that fails, we're somewhat
1207 char *utf8_filename = g_filename_to_utf8(cap_file_->filename,
1212 if (utf8_filename == NULL) {
1213 // So what the heck else can we do here?
1214 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1216 setWindowTitle(NULL);
1217 setWindowFilePath(NULL);
1218 setWindowFilePath(utf8_filename);
1219 g_free(utf8_filename);
1223 /* We have no capture file. */
1224 setWindowFilePath(NULL);
1225 setWindowTitle(tr("The Wireshark Network Analyzer"));
1229 void MainWindow::setTitlebarForSelectedTreeRow()
1231 setWindowTitle(tr("The Wireshark Network Analyzer"));
1235 void MainWindow::setTitlebarForCaptureInProgress()
1239 setWindowFilePath(NULL);
1241 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1242 setWindowTitle(window_name);
1243 g_free(window_name);
1245 /* We have no capture in progress. */
1246 setWindowTitle(tr("The Wireshark Network Analyzer"));
1252 /* Enable or disable menu items based on whether you have a capture file
1253 you've finished reading and, if you have one, whether it's been saved
1254 and whether it could be saved except by copying the raw packet data. */
1255 void MainWindow::setMenusForCaptureFile(bool force_disable)
1257 if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1258 /* We have no capture file or we're currently reading a file */
1259 main_ui_->actionFileMerge->setEnabled(false);
1260 main_ui_->actionFileClose->setEnabled(false);
1261 main_ui_->actionFileSave->setEnabled(false);
1262 main_ui_->actionFileSaveAs->setEnabled(false);
1263 main_ui_->actionSummary->setEnabled(false);
1264 main_ui_->actionFileExportPackets->setEnabled(false);
1265 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1266 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1267 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1268 main_ui_->menuFileExportObjects->setEnabled(false);
1269 main_ui_->actionViewReload->setEnabled(false);
1271 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1273 main_ui_->actionFileClose->setEnabled(true);
1274 main_ui_->actionFileSave->setEnabled(cf_can_save(cap_file_));
1275 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(cap_file_));
1276 main_ui_->actionSummary->setEnabled(true);
1278 * "Export Specified Packets..." should be available only if
1279 * we can write the file out in at least one format.
1281 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1282 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1283 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1284 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1285 main_ui_->menuFileExportObjects->setEnabled(true);
1286 main_ui_->actionViewReload->setEnabled(true);
1290 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1291 /* Either a capture was started or stopped; in either case, it's not
1292 in the process of stopping, so allow quitting. */
1294 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1295 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1296 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1297 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1298 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1299 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1300 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1301 main_ui_->actionFileQuit->setEnabled(true);
1303 main_ui_->actionSummary->setEnabled(capture_in_progress);
1305 qDebug() << "FIX: packet list heading menu sensitivity";
1306 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1307 // !capture_in_progress);
1308 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1309 // !capture_in_progress);
1310 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1311 // !capture_in_progress);
1314 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1315 main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1316 main_ui_->actionStartCapture->setChecked(capture_in_progress);
1317 main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1318 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1319 #endif /* HAVE_LIBPCAP */
1323 void MainWindow::setMenusForCaptureStopping() {
1324 main_ui_->actionFileQuit->setEnabled(false);
1325 main_ui_->actionSummary->setEnabled(false);
1327 main_ui_->actionStartCapture->setChecked(false);
1328 main_ui_->actionStopCapture->setEnabled(false);
1329 main_ui_->actionCaptureRestart->setEnabled(false);
1330 #endif /* HAVE_LIBPCAP */
1333 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1335 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1337 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1338 // have_captured_packets);
1340 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1341 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1342 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1343 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1344 // have_captured_packets);
1345 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1346 // have_captured_packets);
1347 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1348 // have_captured_packets);
1350 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1351 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1352 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1353 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1354 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1356 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1357 // have_captured_packets);
1358 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1359 // have_captured_packets);
1360 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1361 // have_captured_packets);
1362 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1363 // have_captured_packets);
1366 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1367 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1368 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1370 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1371 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1372 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1375 void MainWindow::updateForUnsavedChanges() {
1376 setTitlebarForCaptureFile();
1377 setMenusForCaptureFile();
1378 // set_toolbar_for_capture_file(cf);
1382 /* Update main window items based on whether there's a capture in progress. */
1383 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1385 setMenusForCaptureInProgress(capture_in_progress);
1387 //#ifdef HAVE_LIBPCAP
1388 // set_toolbar_for_capture_in_progress(capture_in_progress);
1390 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1400 * indent-tabs-mode: nil
1403 * ex: set shiftwidth=4 tabstop=8 expandtab:
1404 * :indentSize=4:tabSize=8:noTabs=true: