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"
40 #include "ui/alert_box.h"
41 #include "ui/main_statusbar.h"
42 #include "ui/capture_globals.h"
44 #include "wireshark_application.h"
45 #include "proto_tree.h"
46 #include "byte_view_tab.h"
47 #include "display_filter_edit.h"
48 #include "import_text_dialog.h"
49 #include "export_dissection_dialog.h"
51 #include "qt_ui_utils.h"
53 #include <QTreeWidget>
56 #include <QToolButton>
58 #include <QMetaObject>
59 #include <QMessageBox>
61 //menu_recent_file_write_all
63 // If we ever add support for multiple windows this will need to be replaced.
64 static MainWindow *gbl_cur_main_window = NULL;
66 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
68 gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
71 MainWindow::MainWindow(QWidget *parent) :
73 main_ui_(new Ui::MainWindow),
74 df_combo_box_(new DisplayFilterCombo()),
76 previous_focus_(NULL),
77 capture_stopping_(false),
84 QMargins go_to_margins;
86 gbl_cur_main_window = this;
87 main_ui_->setupUi(this);
88 setMenusForCaptureFile();
89 setForCapturedPackets(false);
90 setMenusForSelectedTreeRow();
91 setForCaptureInProgress(false);
92 setMenusForFileSet(false);
93 interfaceSelectionChanged();
95 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
98 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
99 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
100 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
101 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)), main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
103 // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
104 // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
105 main_ui_->actionFileOpen->setIcon(
106 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
107 // main_ui_->actionFileSave set in main_window.ui
108 main_ui_->actionFileClose->setIcon(
109 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_BrowserStop)));
111 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
112 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
113 // https://bugreports.qt-project.org/browse/QTBUG-22433
114 // This property is obsolete in Qt5 so this issue may be fixed in that version.
115 main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
117 main_ui_->goToFrame->hide();
118 go_to_margins = main_ui_->goToHB->contentsMargins();
119 // go_to_margins.setTop(0);
120 // go_to_margins.setBottom(0);
121 main_ui_->goToHB->setContentsMargins(go_to_margins);
122 // XXX For some reason the cursor is drawn funny with an input mask set
123 // https://bugreports.qt-project.org/browse/QTBUG-7174
124 main_ui_->goToFrame->setStyleSheet(
126 " background: palette(window);"
127 " padding-top: 0.1em;"
128 " padding-bottom: 0.1em;"
129 " border-bottom: 1px solid palette(shadow);"
136 #if defined(Q_WS_MAC)
137 foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
138 foreach (QAction *act, menu->actions()) {
139 act->setIconVisibleInMenu(false);
142 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
143 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
144 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
147 packet_splitter_ = new QSplitter(main_ui_->mainStack);
148 packet_splitter_->setObjectName(QString::fromUtf8("splitterV"));
149 packet_splitter_->setOrientation(Qt::Vertical);
151 packet_list_ = new PacketList(packet_splitter_);
153 ProtoTree *proto_tree = new ProtoTree(packet_splitter_);
154 proto_tree->setHeaderHidden(true);
155 proto_tree->installEventFilter(this);
157 ByteViewTab *byte_view_tab = new ByteViewTab(packet_splitter_);
158 byte_view_tab->setTabPosition(QTabWidget::South);
159 byte_view_tab->setDocumentMode(true);
161 packet_list_->setProtoTree(proto_tree);
162 packet_list_->setByteViewTab(byte_view_tab);
163 packet_list_->installEventFilter(this);
165 packet_splitter_->addWidget(packet_list_);
166 packet_splitter_->addWidget(proto_tree);
167 packet_splitter_->addWidget(byte_view_tab);
169 main_ui_->mainStack->addWidget(packet_splitter_);
171 main_welcome_ = main_ui_->welcomePage;
174 connect(wsApp, SIGNAL(captureCapturePrepared(capture_options *)),
175 this, SLOT(captureCapturePrepared(capture_options *)));
176 connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_options *)),
177 this, SLOT(captureCaptureUpdateStarted(capture_options *)));
178 connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_options *)),
179 this, SLOT(captureCaptureUpdateFinished(capture_options *)));
180 connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_options *)),
181 this, SLOT(captureCaptureFixedStarted(capture_options *)));
182 connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_options *)),
183 this, SLOT(captureCaptureFixedFinished(capture_options *)));
184 connect(wsApp, SIGNAL(captureCaptureStopping(capture_options *)),
185 this, SLOT(captureCaptureStopping(capture_options *)));
186 connect(wsApp, SIGNAL(captureCaptureFailed(capture_options *)),
187 this, SLOT(captureCaptureFailed(capture_options *)));
190 connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
191 this, SLOT(captureFileOpened(const capture_file*)));
192 connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
193 this, SLOT(captureFileReadStarted(const capture_file*)));
194 connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
195 this, SLOT(captureFileReadFinished(const capture_file*)));
196 connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
197 this, SLOT(captureFileClosing(const capture_file*)));
198 connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
199 this, SLOT(captureFileClosed(const capture_file*)));
201 connect(main_welcome_, SIGNAL(startCapture()),
202 this, SLOT(startCapture()));
203 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
204 this, SLOT(openCaptureFile(QString&)));
206 connect(this, SIGNAL(setCaptureFile(capture_file*)),
207 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
208 connect(this, SIGNAL(setCaptureFile(capture_file*)),
209 packet_list_, SLOT(setCaptureFile(capture_file*)));
210 connect(this, SIGNAL(setCaptureFile(capture_file*)),
211 byte_view_tab, SLOT(setCaptureFile(capture_file*)));
213 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
214 packet_list_, SLOT(goNextPacket()));
215 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
216 packet_list_, SLOT(goPreviousPacket()));
217 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
218 packet_list_, SLOT(goFirstPacket()));
219 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
220 packet_list_, SLOT(goLastPacket()));
222 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
223 proto_tree, SLOT(expandSubtrees()));
224 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
225 proto_tree, SLOT(expandAll()));
226 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
227 proto_tree, SLOT(collapseAll()));
229 connect(proto_tree, SIGNAL(protoItemSelected(QString&)),
230 main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
232 connect(proto_tree, SIGNAL(protoItemSelected(field_info *)),
233 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
235 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
236 this, SLOT(openCaptureFile(QString&)));
238 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
240 connect(iface_tree, SIGNAL(itemSelectionChanged()),
241 this, SLOT(interfaceSelectionChanged()));
243 main_ui_->mainStack->setCurrentWidget(main_welcome_);
246 MainWindow::~MainWindow()
252 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
254 pipe_source_ = source;
255 pipe_child_process_ = child_process;
256 pipe_user_data_ = user_data;
257 pipe_input_cb_ = input_cb;
260 /* Tricky to use pipes in win9x, as no concept of wait. NT can
261 do this but that doesn't cover all win32 platforms. GTK can do
262 this but doesn't seem to work over processes. Attempt to do
263 something similar here, start a timer and check for data on every
265 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
268 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
272 pipe_timer_ = new QTimer(this);
273 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
274 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
275 pipe_timer_->start(200);
277 if (pipe_notifier_) {
278 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
279 delete pipe_notifier_;
282 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
283 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
284 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
285 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
290 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
292 // The user typed some text. Start filling in a filter.
293 // We may need to be more choosy here. We just need to catch events for the packet list,
294 // proto tree, and main welcome widgets.
295 if (event->type() == QEvent::KeyPress) {
296 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
297 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
298 df_combo_box_->lineEdit()->insert(kevt->text());
299 df_combo_box_->lineEdit()->setFocus();
304 return QObject::eventFilter(obj, event);
307 void MainWindow::keyPressEvent(QKeyEvent *event) {
309 // Explicitly focus on the display filter combo.
310 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
311 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
315 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
316 if (event->modifiers() == Qt::NoModifier) {
317 if (event->key() == Qt::Key_Escape) {
318 on_goToCancel_clicked();
319 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
323 return; // goToLineEdit didn't want it and we don't either.
326 // Move up & down the packet list.
327 if (event->key() == Qt::Key_F7) {
328 packet_list_->goPreviousPacket();
329 } else if (event->key() == Qt::Key_F8) {
330 packet_list_->goNextPacket();
333 // Move along, citizen.
334 QMainWindow::keyPressEvent(event);
337 void MainWindow::closeEvent(QCloseEvent *event) {
338 /* If we're in the middle of stopping a capture, don't do anything;
339 the user can try deleting the window after the capture stops. */
340 if (capture_stopping_) {
346 void MainWindow::mergeCaptureFile()
348 QString file_name = "";
349 QString display_filter = "";
350 dfilter_t *rfcode = NULL;
356 if (prefs.gui_ask_unsaved) {
357 if (cap_file_->is_tempfile || cap_file_->unsaved_changes) {
358 QMessageBox msg_dialog;
359 gchar *display_basename;
362 msg_dialog.setIcon(QMessageBox::Question);
363 /* This is a temporary capture file or has unsaved changes; ask the
364 user whether to save the capture. */
365 if (cap_file_->is_tempfile) {
366 msg_dialog.setText(tr("Save packets before merging?"));
367 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
370 * Format the message.
372 display_basename = g_filename_display_basename(cap_file_->filename);
373 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
374 g_free(display_basename);
375 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
378 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
379 msg_dialog.setDefaultButton(QMessageBox::Save);
381 response = msg_dialog.exec();
385 case QMessageBox::Save:
386 /* Save the file but don't close it */
387 saveCaptureFile(cap_file_, FALSE);
390 case QMessageBox::Cancel:
392 /* Don't do the merge. */
399 CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
401 cf_status_t merge_status;
402 char *in_filenames[2];
405 switch (prefs.gui_fileopen_style) {
407 case FO_STYLE_LAST_OPENED:
408 /* The user has specified that we should start out in the last directory
409 we looked in. If we've already opened a file, use its containing
410 directory, if we could determine it, as the directory, otherwise
411 use the "last opened" directory saved in the preferences file if
413 /* This is now the default behaviour in file_selection_new() */
416 case FO_STYLE_SPECIFIED:
417 /* The user has specified that we should always start out in a
418 specified directory; if they've specified that directory,
419 start out by showing the files in that dir. */
420 if (prefs.gui_fileopen_dir[0] != '\0')
421 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
425 if (merge_dlg.merge(file_name)) {
426 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
427 cf_set_rfcode(cap_file_, rfcode);
429 /* Not valid. Tell the user, and go back and run the file
430 selection box again once they dismiss the alert. */
431 //bad_dfilter_alert_box(top_level, display_filter->str);
432 QMessageBox::warning(this, tr("Invalid Display Filter"),
433 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
441 file_type = cap_file_->cd_t;
443 /* Try to merge or append the two files */
445 if (merge_dlg.mergeType() == 0) {
446 /* chronological order */
447 in_filenames[0] = cap_file_->filename;
448 in_filenames[1] = file_name.toUtf8().data();
449 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
450 } else if (merge_dlg.mergeType() <= 0) {
452 in_filenames[0] = file_name.toUtf8().data();
453 in_filenames[1] = cap_file_->filename;
454 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
457 in_filenames[0] = cap_file_->filename;
458 in_filenames[1] = file_name.toUtf8().data();
459 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
462 if (merge_status != CF_OK) {
464 dfilter_free(rfcode);
471 /* Try to open the merged capture file. */
473 if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
474 /* We couldn't open it; fail. */
477 dfilter_free(rfcode);
482 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
483 it closed the previous capture file, and thus destroyed any
484 previous read filter attached to "cf"). */
485 cfile.rfcode = rfcode;
487 switch (cf_read(&cfile, FALSE)) {
491 /* Just because we got an error, that doesn't mean we were unable
492 to read any of the file; we handle what we could get from the
496 case CF_READ_ABORTED:
497 /* The user bailed out of re-reading the capture file; the
498 capture file has been closed - just free the capture file name
499 string and return (without changing the last containing
505 /* Save the name of the containing directory specified in the path name,
506 if any; we can write over cf_merged_name, which is a good thing, given that
507 "get_dirname()" does write over its argument. */
508 wsApp->setLastOpenDir(get_dirname(tmpname));
510 df_combo_box_->setEditText(display_filter);
511 main_ui_->statusBar->showExpert();
517 void MainWindow::importCaptureFile() {
518 ImportTextDialog import_dlg;
520 if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
525 if (import_dlg.result() != QDialog::Accepted) {
526 main_ui_->mainStack->setCurrentWidget(main_welcome_);
530 openCaptureFile(import_dlg.capfileName());
533 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
535 gboolean discard_comments;
537 if (cf->is_tempfile) {
538 /* This is a temporary capture file, so saving it means saving
539 it to a permanent file. Prompt the user for a location
540 to which to save it. Don't require that the file format
541 support comments - if it's a temporary capture file, it's
542 probably pcap-ng, which supports comments and, if it's
543 not pcap-ng, let the user decide what they want to do
544 if they've added comments. */
545 saveAsCaptureFile(cf, FALSE, stay_closed);
547 if (cf->unsaved_changes) {
548 cf_write_status_t status;
550 /* This is not a temporary capture file, but it has unsaved
551 changes, so saving it means doing a "safe save" on top
552 of the existing file, in the same format - no UI needed
553 unless the file has comments and the file's format doesn't
556 If the file has comments, does the file's format support them?
557 If not, ask the user whether they want to discard the comments
558 or choose a different format. */
559 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
562 /* The file can be saved in the specified format as is;
563 just drive on and save in the format they selected. */
564 discard_comments = FALSE;
567 case SAVE_WITHOUT_COMMENTS:
568 /* The file can't be saved in the specified format as is,
569 but it can be saved without the comments, and the user
570 said "OK, discard the comments", so save it in the
571 format they specified without the comments. */
572 discard_comments = TRUE;
575 case SAVE_IN_ANOTHER_FORMAT:
576 /* There are file formats in which we can save this that
577 support comments, and the user said not to delete the
578 comments. Do a "Save As" so the user can select
579 one of those formats and choose a file name. */
580 saveAsCaptureFile(cf, TRUE, stay_closed);
584 /* The user said "forget it". Just return. */
588 /* Squelch warnings that discard_comments is being used
590 g_assert_not_reached();
594 /* XXX - cf->filename might get freed out from under us, because
595 the code path through which cf_save_packets() goes currently
596 closes the current file and then opens and reloads the saved file,
597 so make a copy and free it later. */
598 file_name = cf->filename;
599 status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
600 discard_comments, stay_closed);
604 /* The save succeeded; we're done.
605 If we discarded comments, redraw the packet list to reflect
606 any packets that no longer have comments. */
607 if (discard_comments)
608 packet_list_queue_draw();
613 XXX - OK, what do we do now? Let them try a
614 "Save As", in case they want to try to save to a
615 different directory r file system? */
618 case CF_WRITE_ABORTED:
619 /* The write was aborted; just drive on. */
623 /* Otherwise just do nothing. */
627 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
628 QString file_name = "";
631 cf_write_status_t status;
633 gboolean discard_comments = FALSE;
640 CaptureFileDialog save_as_dlg(this, cf);
642 switch (prefs.gui_fileopen_style) {
644 case FO_STYLE_LAST_OPENED:
645 /* The user has specified that we should start out in the last directory
646 we looked in. If we've already opened a file, use its containing
647 directory, if we could determine it, as the directory, otherwise
648 use the "last opened" directory saved in the preferences file if
650 /* This is now the default behaviour in file_selection_new() */
653 case FO_STYLE_SPECIFIED:
654 /* The user has specified that we should always start out in a
655 specified directory; if they've specified that directory,
656 start out by showing the files in that dir. */
657 if (prefs.gui_fileopen_dir[0] != '\0')
658 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
662 /* If the file has comments, does the format the user selected
663 support them? If not, ask the user whether they want to
664 discard the comments or choose a different format. */
665 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
668 /* The file can be saved in the specified format as is;
669 just drive on and save in the format they selected. */
670 discard_comments = FALSE;
673 case SAVE_WITHOUT_COMMENTS:
674 /* The file can't be saved in the specified format as is,
675 but it can be saved without the comments, and the user
676 said "OK, discard the comments", so save it in the
677 format they specified without the comments. */
678 discard_comments = TRUE;
681 case SAVE_IN_ANOTHER_FORMAT:
682 /* There are file formats in which we can save this that
683 support comments, and the user said not to delete the
684 comments. The combo box of file formats has had the
685 formats that don't support comments trimmed from it,
686 so run the dialog again, to let the user decide
687 whether to save in one of those formats or give up. */
688 discard_comments = FALSE;
689 must_support_comments = TRUE;
693 /* The user said "forget it". Just get rid of the dialog box
697 file_type = save_as_dlg.selectedFileType();
698 compressed = save_as_dlg.isCompressed();
700 fileAddExtension(file_name, file_type, compressed);
703 // /* If the file exists and it's user-immutable or not writable,
704 // ask the user whether they want to override that. */
705 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
706 // /* They don't. Let them try another file name or cancel. */
711 /* Attempt to save the file */
712 status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
713 discard_comments, stay_closed);
717 /* The save succeeded; we're done. */
718 /* Save the directory name for future file dialogs. */
719 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
720 set_last_open_dir(dirname);
721 /* If we discarded comments, redraw the packet list to reflect
722 any packets that no longer have comments. */
723 if (discard_comments)
724 packet_list_queue_draw();
728 /* The save failed; let the user try again. */
731 case CF_WRITE_ABORTED:
732 /* The user aborted the save; just return. */
739 void MainWindow::exportSelectedPackets() {
740 QString file_name = "";
743 packet_range_t range;
744 cf_write_status_t status;
746 gboolean discard_comments = FALSE;
751 /* Init the packet range */
752 packet_range_init(&range, cap_file_);
753 range.process_filtered = TRUE;
754 range.include_dependents = TRUE;
757 CaptureFileDialog esp_dlg(this, cap_file_);
759 switch (prefs.gui_fileopen_style) {
761 case FO_STYLE_LAST_OPENED:
762 /* The user has specified that we should start out in the last directory
763 we looked in. If we've already opened a file, use its containing
764 directory, if we could determine it, as the directory, otherwise
765 use the "last opened" directory saved in the preferences file if
767 /* This is now the default behaviour in file_selection_new() */
770 case FO_STYLE_SPECIFIED:
771 /* The user has specified that we should always start out in a
772 specified directory; if they've specified that directory,
773 start out by showing the files in that dir. */
774 if (prefs.gui_fileopen_dir[0] != '\0')
775 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
779 /* If the file has comments, does the format the user selected
780 support them? If not, ask the user whether they want to
781 discard the comments or choose a different format. */
782 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
785 /* The file can be saved in the specified format as is;
786 just drive on and save in the format they selected. */
787 discard_comments = FALSE;
790 case SAVE_WITHOUT_COMMENTS:
791 /* The file can't be saved in the specified format as is,
792 but it can be saved without the comments, and the user
793 said "OK, discard the comments", so save it in the
794 format they specified without the comments. */
795 discard_comments = TRUE;
798 case SAVE_IN_ANOTHER_FORMAT:
799 /* There are file formats in which we can save this that
800 support comments, and the user said not to delete the
801 comments. The combo box of file formats has had the
802 formats that don't support comments trimmed from it,
803 so run the dialog again, to let the user decide
804 whether to save in one of those formats or give up. */
805 discard_comments = FALSE;
809 /* The user said "forget it". Just get rid of the dialog box
815 * Check that we're not going to save on top of the current
817 * We do it here so we catch all cases ...
818 * Unfortunately, the file requester gives us an absolute file
819 * name and the read file name may be relative (if supplied on
820 * the command line). From Joerg Mayer.
822 if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
824 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
826 msg_box.setIcon(QMessageBox::Critical);
827 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
828 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
829 msg_box.setStandardButtons(QMessageBox::Ok);
830 msg_box.setDefaultButton(QMessageBox::Ok);
832 g_free(display_basename);
836 file_type = esp_dlg.selectedFileType();
837 compressed = esp_dlg.isCompressed();
838 fileAddExtension(file_name, file_type, compressed);
841 // /* If the file exists and it's user-immutable or not writable,
842 // ask the user whether they want to override that. */
843 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
844 // /* They don't. Let them try another file name or cancel. */
849 /* Attempt to save the file */
850 status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
854 /* The save succeeded; we're done. */
855 /* Save the directory name for future file dialogs. */
856 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
857 set_last_open_dir(dirname);
858 /* If we discarded comments, redraw the packet list to reflect
859 any packets that no longer have comments. */
860 if (discard_comments)
861 packet_list_queue_draw();
865 /* The save failed; let the user try again. */
868 case CF_WRITE_ABORTED:
869 /* The user aborted the save; just return. */
876 void MainWindow::exportDissections(export_type_e export_type) {
877 ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
878 packet_range_t range;
883 /* Init the packet range */
884 packet_range_init(&range, cap_file_);
885 range.process_filtered = TRUE;
886 range.include_dependents = TRUE;
891 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
892 QString file_name_lower;
894 GSList *extensions_list;
895 gboolean add_extension;
898 * Append the default file extension if there's none given by
899 * the user or if they gave one that's not one of the valid
900 * extensions for the file type.
902 file_name_lower = file_name.toLower();
903 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
904 if (extensions_list != NULL) {
907 /* We have one or more extensions for this file type.
908 Start out assuming we need to add the default one. */
909 add_extension = TRUE;
911 /* OK, see if the file has one of those extensions. */
912 for (extension = extensions_list; extension != NULL;
913 extension = g_slist_next(extension)) {
914 file_suffix += tr(".") + (char *)extension->data;
915 if (file_name_lower.endsWith(file_suffix)) {
917 * The file name has one of the extensions for
920 add_extension = FALSE;
923 file_suffix += ".gz";
924 if (file_name_lower.endsWith(file_suffix)) {
926 * The file name has one of the extensions for
929 add_extension = FALSE;
934 /* We have no extensions for this file type. Don't add one. */
935 add_extension = FALSE;
938 if (wtap_default_file_extension(file_type) != NULL) {
939 file_name += tr(".") + wtap_default_file_extension(file_type);
947 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
948 bool capture_in_progress = FALSE;
950 if (!cap_file_ || cap_file_->state == FILE_CLOSED)
951 return true; /* Already closed, nothing to do */
954 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
955 /* This is true if we're reading a capture file *or* if we're doing
956 a live capture. If we're reading a capture file, the main loop
957 is busy reading packets, and only accepting input from the
958 progress dialog, so we can't get here, so this means we're
960 capture_in_progress = TRUE;
964 if (prefs.gui_ask_unsaved) {
965 if (cap_file_->is_tempfile || capture_in_progress || cap_file_->unsaved_changes) {
966 QMessageBox msg_dialog;
968 QPushButton *default_button;
971 msg_dialog.setIcon(QMessageBox::Question);
973 /* This is a temporary capture file, or there's a capture in
974 progress, or the file has unsaved changes; ask the user whether
976 if (cap_file_->is_tempfile) {
978 msg_dialog.setText(tr("You have unsaved packets"));
979 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
981 if (capture_in_progress) {
982 question.append(tr("Do you want to stop the capture and save the captured packets"));
984 question.append(tr("Do you want to save the captured packets"));
986 question.append(before_what).append(tr("?"));
987 msg_dialog.setInformativeText(question);
992 * Format the message.
994 if (capture_in_progress) {
995 question.append(tr("Do you want to stop the capture and save the captured packets"));
996 question.append(before_what).append(tr("?"));
997 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
999 gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1000 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1001 .arg(display_basename)
1004 g_free(display_basename);
1005 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1009 // XXX Text comes from ui/gtk/stock_icons.[ch]
1010 // Note that the button roles differ from the GTK+ version.
1011 // Cancel = RejectRole
1012 // Save = AcceptRole
1013 // Don't Save = DestructiveRole
1014 msg_dialog.setStandardButtons(QMessageBox::Cancel);
1016 if (capture_in_progress) {
1017 default_button = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1019 default_button = msg_dialog.addButton(QMessageBox::Save);
1021 msg_dialog.setDefaultButton(default_button);
1024 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1025 msg_dialog.addButton(tr("Stop and Quit without Saving"), QMessageBox::DestructiveRole);
1027 msg_dialog.addButton(tr("Quit without Saving"), QMessageBox::DestructiveRole);
1030 if (capture_in_progress) {
1031 msg_dialog.addButton(tr("Stop and Continue without Saving"), QMessageBox::DestructiveRole);
1033 msg_dialog.addButton(QMessageBox::Discard);
1037 response = msg_dialog.exec();
1041 case QMessageBox::Save:
1043 /* If there's a capture in progress, we have to stop the capture
1044 and then do the save. */
1045 if (capture_in_progress)
1048 /* Save the file and close it */
1049 saveCaptureFile(cap_file_, TRUE);
1052 case QMessageBox::Discard:
1055 * If there's a capture in progress; we have to stop the capture
1056 * and then do the close.
1058 if (capture_in_progress)
1061 /* Just close the file, discarding changes */
1062 cf_close(cap_file_);
1066 case QMessageBox::Cancel:
1068 /* Don't close the file (and don't stop any capture in progress). */
1069 return false; /* file not closed */
1073 /* Unchanged file, just close it */
1074 cf_close(cap_file_);
1077 /* User asked not to be bothered by those prompts, just close it.
1078 XXX - should that apply only to saving temporary files? */
1080 /* If there's a capture in progress, we have to stop the capture
1081 and then do the close. */
1082 if (capture_in_progress)
1085 cf_close(cap_file_);
1088 return true; /* File closed */
1091 void MainWindow::captureStop() {
1094 while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1095 WiresharkApplication::processEvents();
1101 /* Enable or disable menu items based on whether you have a capture file
1102 you've finished reading and, if you have one, whether it's been saved
1103 and whether it could be saved except by copying the raw packet data. */
1104 void MainWindow::setMenusForCaptureFile(bool force_disable)
1106 if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1107 /* We have no capture file or we're currently reading a file */
1108 main_ui_->actionFileMerge->setEnabled(false);
1109 main_ui_->actionFileClose->setEnabled(false);
1110 main_ui_->actionFileSave->setEnabled(false);
1111 main_ui_->actionFileSaveAs->setEnabled(false);
1112 main_ui_->actionFileExportPackets->setEnabled(false);
1113 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1114 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1115 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1116 main_ui_->menuFileExportObjects->setEnabled(false);
1117 main_ui_->actionViewReload->setEnabled(false);
1119 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1121 main_ui_->actionFileClose->setEnabled(true);
1123 * "Save" should be available only if:
1125 * the file has unsaved changes, and we can save it in some
1126 * format through Wiretap
1130 * the file is a temporary file and has no unsaved changes (so
1131 * that "saving" it just means copying it).
1133 main_ui_->actionFileSave->setEnabled(
1134 (cap_file_->unsaved_changes && cf_can_write_with_wiretap(cap_file_)) ||
1135 (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1137 * "Save As..." should be available only if:
1139 * we can save it in some format through Wiretap
1143 * the file is a temporary file and has no unsaved changes (so
1144 * that "saving" it just means copying it).
1146 main_ui_->actionFileSaveAs->setEnabled(
1147 cf_can_write_with_wiretap(cap_file_) ||
1148 (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1150 * "Export Specified Packets..." should be available only if
1151 * we can write the file out in at least one format.
1153 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1154 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1155 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1156 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1157 main_ui_->menuFileExportObjects->setEnabled(true);
1158 main_ui_->actionViewReload->setEnabled(true);
1162 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1163 /* Either a capture was started or stopped; in either case, it's not
1164 in the process of stopping, so allow quitting. */
1166 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1167 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1168 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1169 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1170 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1171 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1172 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1173 main_ui_->actionFileQuit->setEnabled(true);
1175 qDebug() << "FIX: packet list heading menu sensitivity";
1176 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1177 // !capture_in_progress);
1178 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1179 // !capture_in_progress);
1180 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1181 // !capture_in_progress);
1184 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1185 main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1186 main_ui_->actionStartCapture->setChecked(capture_in_progress);
1187 main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1188 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1189 #endif /* HAVE_LIBPCAP */
1193 void MainWindow::setMenusForCaptureStopping() {
1194 main_ui_->actionFileQuit->setEnabled(false);
1196 main_ui_->actionStartCapture->setChecked(false);
1197 main_ui_->actionStopCapture->setEnabled(false);
1198 main_ui_->actionCaptureRestart->setEnabled(false);
1199 #endif /* HAVE_LIBPCAP */
1202 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1204 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1206 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1207 // have_captured_packets);
1209 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPacket",
1210 // have_captured_packets);
1211 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNext",
1212 // have_captured_packets);
1213 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPrevious",
1214 // have_captured_packets);
1215 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1216 // have_captured_packets);
1217 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1218 // have_captured_packets);
1219 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1220 // have_captured_packets);
1222 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1223 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1224 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1225 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1226 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1228 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1229 // have_captured_packets);
1230 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1231 // have_captured_packets);
1232 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1233 // have_captured_packets);
1234 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1235 // have_captured_packets);
1238 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1239 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1240 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1242 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1243 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1244 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1247 void MainWindow::updateForUnsavedChanges() {
1248 // set_display_filename(cf);
1249 setMenusForCaptureFile();
1250 // set_toolbar_for_capture_file(cf);
1254 /* Update main window items based on whether there's a capture in progress. */
1255 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1257 setMenusForCaptureInProgress(capture_in_progress);
1259 //#ifdef HAVE_LIBPCAP
1260 // set_toolbar_for_capture_in_progress(capture_in_progress);
1262 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1272 * indent-tabs-mode: nil
1275 * ex: set shiftwidth=4 tabstop=8 expandtab:
1276 * :indentSize=4:tabSize=8:noTabs=true: