a5586f171c2e0228883b85781b999c43a5162c78
[obnox/wireshark/wip.git] / gtk / capture_file_dlg.c
1 /* capture_file_dlg.c
2  * Dialog boxes for handling capture files
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36
37 #include "packet-range.h"
38 #include <epan/filesystem.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/prefs.h>
41
42 #include "../globals.h"
43 #include "../alert_box.h"
44 #include "../simple_dialog.h"
45 #include "../color.h"
46 #include "../ui_util.h"
47 #include "../color_filters.h"
48 #include "../merge.h"
49 #include "../util.h"
50 #include <wsutil/file_util.h>
51
52 #include "gtk/gtkglobals.h"
53 #include "gtk/keys.h"
54 #include "gtk/filter_dlg.h"
55 #include "gtk/gui_utils.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/file_dlg.h"
58 #include "gtk/capture_file_dlg.h"
59 #include "gtk/drag_and_drop.h"
60 #include "gtk/main.h"
61 #include "gtk/main_menu.h"
62 #include "gtk/recent.h"
63 #include "gtk/color_dlg.h"
64 #ifdef HAVE_LIBPCAP
65 #include "gtk/capture_dlg.h"
66 #endif
67 #include "gtk/stock_icons.h"
68 #include "gtk/range_utils.h"
69
70 #if _WIN32
71 #include <gdk/gdkwin32.h>
72 #include <windows.h>
73 #include "gtk/file_dlg_win32.h"
74 #endif
75
76
77 static void file_open_ok_cb(GtkWidget *w, gpointer fs);
78 static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
79 static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
80 static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
81 static void select_file_type_cb(GtkWidget *w, gpointer data);
82 static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
83 static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
84 static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
85 static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
86 static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
87 static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
88 static void set_file_type_list(GtkWidget *option_menu);
89
90 #define E_FILE_M_RESOLVE_KEY      "file_dlg_mac_resolve_key"
91 #define E_FILE_N_RESOLVE_KEY      "file_dlg_network_resolve_key"
92 #define E_FILE_T_RESOLVE_KEY      "file_dlg_transport_resolve_key"
93
94 #define E_MERGE_PREPEND_KEY       "merge_dlg_prepend_key"
95 #define E_MERGE_CHRONO_KEY            "merge_dlg_chrono_key"
96 #define E_MERGE_APPEND_KEY            "merge_dlg_append_key"
97
98
99 #define PREVIEW_TABLE_KEY       "preview_table_key"
100 #define PREVIEW_FILENAME_KEY    "preview_filename_key"
101 #define PREVIEW_FORMAT_KEY      "preview_format_key"
102 #define PREVIEW_SIZE_KEY        "preview_size_key"
103 #define PREVIEW_ELAPSED_KEY     "preview_elapsed_key"
104 #define PREVIEW_PACKETS_KEY     "preview_packets_key"
105 #define PREVIEW_FIRST_KEY       "preview_first_key"
106
107
108 /*
109  * Keep a static pointer to the current "Save Capture File As" window, if
110  * any, so that if somebody tries to do "File:Save" or "File:Save As"
111  * while there's already a "Save Capture File As" window up, we just pop
112  * up the existing one, rather than creating a new one.
113  */
114 static GtkWidget *file_save_as_w;
115
116 /* XXX - can we make these not be static? */
117 static packet_range_t range;
118 static gboolean color_selected;
119 static int filetype;
120 static GtkWidget *cfselect_cb;
121 static GtkWidget *ft_om;
122 static GtkWidget *range_tb;
123
124 #define PREVIEW_STR_MAX         200
125
126
127 /* set a new filename for the preview widget */
128 static wtap *
129 preview_set_filename(GtkWidget *prev, const gchar *cf_name)
130 {
131     GtkWidget  *label;
132     wtap       *wth;
133     int         err = 0;
134     gchar      *err_info;
135     gchar       string_buff[PREVIEW_STR_MAX];
136     gint64      filesize;
137
138
139     /* init preview labels */
140     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
141     gtk_label_set_text(GTK_LABEL(label), "-");
142     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
143     gtk_label_set_text(GTK_LABEL(label), "-");
144     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
145     gtk_label_set_text(GTK_LABEL(label), "-");
146     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
147     gtk_label_set_text(GTK_LABEL(label), "-");
148     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
149     gtk_label_set_text(GTK_LABEL(label), "-");
150     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
151     gtk_label_set_text(GTK_LABEL(label), "-");
152
153     if(!cf_name) {
154         return NULL;
155     }
156
157     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
158     gtk_label_set_text(GTK_LABEL(label), get_basename(cf_name));
159
160     if (test_for_directory(cf_name) == EISDIR) {
161         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
162         gtk_label_set_text(GTK_LABEL(label), "directory");
163         return NULL;
164     }
165
166     wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
167     if (wth == NULL) {
168         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
169         if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
170             gtk_label_set_text(GTK_LABEL(label), "unknown file format");
171         } else {
172             gtk_label_set_text(GTK_LABEL(label), "error opening file");
173         }
174         return NULL;
175     }
176
177     /* Find the size of the file. */
178     filesize = wtap_file_size(wth, &err);
179     if (filesize == -1) {
180         gtk_label_set_text(GTK_LABEL(label), "error getting file size");
181         wtap_close(wth);
182         return NULL;
183     }
184     g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
185     label = g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
186     gtk_label_set_text(GTK_LABEL(label), string_buff);
187
188     /* type */
189     g_snprintf(string_buff, PREVIEW_STR_MAX, "%s", wtap_file_type_string(wtap_file_type(wth)));
190     label = g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
191     gtk_label_set_text(GTK_LABEL(label), string_buff);
192
193     return wth;
194 }
195
196
197 /* do a preview run on the currently selected capture file */
198 static void
199 preview_do(GtkWidget *prev, wtap *wth)
200 {
201     GtkWidget  *label;
202     unsigned int elapsed_time;
203     time_t      time_preview;
204     time_t      time_current;
205     int         err = 0;
206     gchar      *err_info;
207     gint64      data_offset;
208     const struct wtap_pkthdr *phdr;
209     double      start_time = 0; /* seconds, with nsec resolution */
210     double      stop_time = 0;  /* seconds, with nsec resolution */
211     double      cur_time;
212     unsigned int packets = 0;
213     gboolean    is_breaked = FALSE;
214     gchar       string_buff[PREVIEW_STR_MAX];
215     time_t      ti_time;
216     struct tm  *ti_tm;
217
218
219     time(&time_preview);
220     while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
221         phdr = wtap_phdr(wth);
222         cur_time = wtap_nstime_to_sec(&phdr->ts);
223         if(packets == 0) {
224             start_time  = cur_time;
225             stop_time = cur_time;
226         }
227         if (cur_time < start_time) {
228             start_time = cur_time;
229         }
230         if (cur_time > stop_time){
231             stop_time = cur_time;
232         }
233
234         packets++;
235         if(packets%1000) {
236             /* do we have a timeout? */
237             time(&time_current);
238             if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
239                 is_breaked = TRUE;
240                 break;
241             }
242         }
243     }
244
245     if(err != 0) {
246         g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
247         label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
248         gtk_label_set_text(GTK_LABEL(label), string_buff);
249         wtap_close(wth);
250         return;
251     }
252
253     /* packet count */
254     if(is_breaked) {
255         g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
256     } else {
257         g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
258     }
259     label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
260     gtk_label_set_text(GTK_LABEL(label), string_buff);
261
262     /* first packet */
263     ti_time = (long)start_time;
264     ti_tm = localtime( &ti_time );
265         if(ti_tm) {
266                 g_snprintf(string_buff, PREVIEW_STR_MAX,
267                                  "%04d-%02d-%02d %02d:%02d:%02d",
268                                  ti_tm->tm_year + 1900,
269                                  ti_tm->tm_mon + 1,
270                                  ti_tm->tm_mday,
271                                  ti_tm->tm_hour,
272                                  ti_tm->tm_min,
273                                  ti_tm->tm_sec);
274         } else {
275                 g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
276         }
277         label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
278     gtk_label_set_text(GTK_LABEL(label), string_buff);
279
280     /* elapsed time */
281     elapsed_time = (unsigned int)(stop_time-start_time);
282     if(elapsed_time/86400) {
283       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
284         elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
285     } else {
286       g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
287         elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
288     }
289     if(is_breaked) {
290       g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
291     }
292     label = g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
293     gtk_label_set_text(GTK_LABEL(label), string_buff);
294
295     wtap_close(wth);
296 }
297
298 #if 0
299 /* as the dialog layout will look very ugly when using the file chooser preview mechanism,
300    simply use the same layout as in GTK2.0 */
301 static void
302 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
303 {
304     GtkWidget *prev = GTK_WIDGET (data);
305     char *cf_name;
306     gboolean have_preview;
307
308     cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
309
310     have_preview = preview_set_filename(prev, cf_name);
311
312     g_free (cf_name);
313
314     have_preview = TRUE;
315     gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
316 }
317 #endif
318
319
320 /* the filename text entry changed */
321 static void
322 file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
323 {
324     GtkWidget *prev = g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
325     const gchar* cf_name;
326     gboolean have_preview;
327     wtap       *wth;
328
329     /* get the filename */
330     cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
331
332     /* set the filename to the preview */
333     wth = preview_set_filename(prev, cf_name);
334     have_preview = (wth != NULL);
335
336     /* make the preview widget sensitive */
337     gtk_widget_set_sensitive(prev, have_preview);
338
339     /* make the open/save/... dialog button sensitive */
340
341     gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
342
343     /* do the actual preview */
344     if(have_preview)
345         preview_do(prev, wth);
346 }
347
348
349 /* copied from summary_dlg.c */
350 static GtkWidget *
351 add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
352 {
353     GtkWidget *label;
354     gchar     *indent;
355
356     if(strlen(value) != 0) {
357         indent = g_strdup_printf("   %s", title);
358     } else {
359         indent = g_strdup(title);
360     }
361     label = gtk_label_new(indent);
362     g_free(indent);
363     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
364     gtk_widget_set_sensitive(label, sensitive);
365     gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
366
367     label = gtk_label_new(value);
368     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
369     gtk_widget_set_sensitive(label, sensitive);
370     gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
371
372     *row = *row + 1;
373
374     return label;
375 }
376
377 static GtkWidget *
378 add_string_to_table(GtkWidget *list, guint *row, const gchar *title, const gchar *value)
379 {
380     return add_string_to_table_sensitive(list, row, title, value, TRUE);
381 }
382
383
384
385 static GtkWidget *
386 preview_new(void)
387 {
388     GtkWidget *table, *label;
389     guint         row;
390
391     table = gtk_table_new(1, 2, FALSE);
392     gtk_table_set_col_spacings(GTK_TABLE(table), 6);
393     gtk_table_set_row_spacings(GTK_TABLE(table), 3);
394     row = 0;
395
396     label = add_string_to_table(table, &row, "Filename:", "-");
397     gtk_widget_set_size_request(label, DEF_WIDTH/3, -1);
398     g_object_set_data(G_OBJECT(table), PREVIEW_FILENAME_KEY, label);
399     label = add_string_to_table(table, &row, "Format:", "-");
400     g_object_set_data(G_OBJECT(table), PREVIEW_FORMAT_KEY, label);
401     label = add_string_to_table(table, &row, "Size:", "-");
402     g_object_set_data(G_OBJECT(table), PREVIEW_SIZE_KEY, label);
403     label = add_string_to_table(table, &row, "Packets:", "-");
404     g_object_set_data(G_OBJECT(table), PREVIEW_PACKETS_KEY, label);
405     label = add_string_to_table(table, &row, "First Packet:", "-");
406     g_object_set_data(G_OBJECT(table), PREVIEW_FIRST_KEY, label);
407     label = add_string_to_table(table, &row, "Elapsed time:", "-");
408     g_object_set_data(G_OBJECT(table), PREVIEW_ELAPSED_KEY, label);
409
410     return table;
411 }
412
413 /*
414  * Keep a static pointer to the current "Open Capture File" window, if
415  * any, so that if somebody tries to do "File:Open" while there's already
416  * an "Open Capture File" window up, we just pop up the existing one,
417  * rather than creating a new one.
418  */
419 static GtkWidget *file_open_w;
420
421 /* Open a file */
422 static void
423 file_open_cmd(GtkWidget *w)
424 {
425 #if _WIN32
426   win32_open_file(GDK_WINDOW_HWND(top_level->window));
427 #else /* _WIN32 */
428   GtkWidget     *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
429                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
430   GtkTooltips *tooltips = gtk_tooltips_new();
431   /* No Apply button, and "OK" just sets our text widget, it doesn't
432      activate it (i.e., it doesn't cause us to try to open the file). */
433   static construct_args_t args = {
434         "Wireshark: Read Filter",
435         FALSE,
436         FALSE,
437     TRUE
438   };
439
440   if (file_open_w != NULL) {
441     /* There's already an "Open Capture File" dialog box; reactivate it. */
442     reactivate_window(file_open_w);
443     return;
444   }
445
446   file_open_w = file_selection_new("Wireshark: Open Capture File",
447                                    FILE_SELECTION_OPEN);
448   /* it's annoying, that the file chooser dialog is already shown here,
449      so we cannot use the correct gtk_window_set_default_size() to resize it */
450   gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
451
452   switch (prefs.gui_fileopen_style) {
453
454   case FO_STYLE_LAST_OPENED:
455     /* The user has specified that we should start out in the last directory
456        we looked in.  If we've already opened a file, use its containing
457        directory, if we could determine it, as the directory, otherwise
458        use the "last opened" directory saved in the preferences file if
459        there was one. */
460     /* This is now the default behaviour in file_selection_new() */
461     break;
462
463   case FO_STYLE_SPECIFIED:
464     /* The user has specified that we should always start out in a
465        specified directory; if they've specified that directory,
466        start out by showing the files in that dir. */
467     if (prefs.gui_fileopen_dir[0] != '\0')
468       file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
469     break;
470   }
471
472
473   main_hb = gtk_hbox_new(FALSE, 3);
474   file_selection_set_extra_widget(file_open_w, main_hb);
475   gtk_widget_show(main_hb);
476
477   /* Container for each row of widgets */
478   main_vb = gtk_vbox_new(FALSE, 3);
479   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
480   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
481   gtk_widget_show(main_vb);
482
483   /* filter row */
484   filter_hbox = gtk_hbox_new(FALSE, 1);
485   gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
486   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
487   gtk_widget_show(filter_hbox);
488
489   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
490   g_signal_connect(filter_bt, "clicked",
491                    G_CALLBACK(display_filter_construct_cb), &args);
492   g_signal_connect(filter_bt, "destroy",
493                    G_CALLBACK(filter_button_destroy_cb), NULL);
494   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
495   gtk_widget_show(filter_bt);
496   gtk_tooltips_set_tip(tooltips, filter_bt,
497     "Open the \"Display Filter\" dialog, to edit/apply filters", NULL);
498
499   filter_te = gtk_entry_new();
500   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
501   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
502   g_signal_connect(filter_te, "changed",
503                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
504   gtk_widget_show(filter_te);
505   gtk_tooltips_set_tip(tooltips, filter_te, "Enter a display filter.", NULL);
506
507   g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
508
509   /* resolve buttons */
510   m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
511   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
512         g_resolv_flags & RESOLV_MAC);
513   gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
514   g_object_set_data(G_OBJECT(file_open_w),
515                   E_FILE_M_RESOLVE_KEY, m_resolv_cb);
516   gtk_widget_show(m_resolv_cb);
517
518   n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
519   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
520         g_resolv_flags & RESOLV_NETWORK);
521   gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
522   gtk_widget_show(n_resolv_cb);
523   g_object_set_data(G_OBJECT(file_open_w), E_FILE_N_RESOLVE_KEY, n_resolv_cb);
524   t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
525   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
526         g_resolv_flags & RESOLV_TRANSPORT);
527   gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
528   gtk_widget_show(t_resolv_cb);
529   g_object_set_data(G_OBJECT(file_open_w), E_FILE_T_RESOLVE_KEY, t_resolv_cb);
530
531   g_signal_connect(file_open_w, "destroy",
532                    G_CALLBACK(file_open_destroy_cb), NULL);
533
534   /* preview widget */
535   prev = preview_new();
536   g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
537   gtk_widget_show_all(prev);
538   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
539
540   g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
541                    G_CALLBACK(file_open_entry_changed), file_open_w);
542   file_open_entry_changed(file_open_w, file_open_w);
543
544   g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
545                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
546   if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
547   {
548     file_open_ok_cb(file_open_w, file_open_w);
549   }
550   else window_destroy(file_open_w);
551 #endif /* _WIN32 */
552 }
553
554 static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
555 {
556     switch(btn) {
557     case(ESD_BTN_SAVE):
558         /* save file first */
559         file_save_as_cmd(after_save_open_dialog, data);
560         break;
561     case(ESD_BTN_DONT_SAVE):
562         cf_close(&cfile);
563         file_open_cmd(data);
564         break;
565     case(ESD_BTN_CANCEL):
566         break;
567     default:
568         g_assert_not_reached();
569     }
570 }
571
572 void
573 file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
574   gpointer  dialog;
575
576   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
577     /* user didn't saved his current file, ask him */
578     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
579                 "%sSave capture file before opening a new one?%s\n\n"
580                 "If you open a new capture file without saving, your capture data will be discarded.",
581                 simple_dialog_primary_start(), simple_dialog_primary_end());
582     simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
583   } else {
584     /* unchanged file, just open a new one */
585     file_open_cmd(widget);
586   }
587 }
588
589 /* user pressed "open" button */
590 static void
591 file_open_ok_cb(GtkWidget *w, gpointer fs) {
592   gchar       *cf_name, *s;
593   const gchar *rfilter;
594   GtkWidget   *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
595   dfilter_t   *rfcode = NULL;
596   int          err;
597
598   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
599   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
600   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
601   if (!dfilter_compile(rfilter, &rfcode)) {
602     bad_dfilter_alert_box(rfilter);
603     g_free(cf_name);
604     return;
605   }
606
607   /* Perhaps the user specified a directory instead of a file.
608      Check whether they did. */
609   if (test_for_directory(cf_name) == EISDIR) {
610         /* It's a directory - set the file selection box to display that
611            directory, don't try to open the directory as a capture file. */
612         set_last_open_dir(cf_name);
613         g_free(cf_name);
614         file_selection_set_current_folder(fs, get_last_open_dir());
615         return;
616   }
617
618   /* Try to open the capture file. */
619   if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
620     /* We couldn't open it; don't dismiss the open dialog box,
621        just leave it around so that the user can, after they
622        dismiss the alert box popped up for the open error,
623        try again. */
624     if (rfcode != NULL)
625       dfilter_free(rfcode);
626     g_free(cf_name);
627
628     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
629      * as this will prevent the user from closing the now existing error
630      * message, simply close the dialog (this is the best we can do here). */
631     if (file_open_w)
632       window_destroy(file_open_w);
633
634     return;
635   }
636
637   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
638      it closed the previous capture file, and thus destroyed any
639      previous read filter attached to "cf"). */
640   cfile.rfcode = rfcode;
641
642   /* Set the global resolving variable */
643   g_resolv_flags = prefs.name_resolve;
644   m_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_M_RESOLVE_KEY);
645   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
646     g_resolv_flags |= RESOLV_MAC;
647   else
648     g_resolv_flags &= ~RESOLV_MAC;
649   n_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_N_RESOLVE_KEY);
650   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
651     g_resolv_flags |= RESOLV_NETWORK;
652   else
653     g_resolv_flags &= ~RESOLV_NETWORK;
654   t_resolv_cb = g_object_get_data(G_OBJECT(w), E_FILE_T_RESOLVE_KEY);
655   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
656     g_resolv_flags |= RESOLV_TRANSPORT;
657   else
658     g_resolv_flags &= ~RESOLV_TRANSPORT;
659
660   /* We've crossed the Rubicon; get rid of the file selection box. */
661   window_destroy(GTK_WIDGET (fs));
662
663   switch (cf_read(&cfile)) {
664
665   case CF_READ_OK:
666   case CF_READ_ERROR:
667     /* Just because we got an error, that doesn't mean we were unable
668        to read any of the file; we handle what we could get from the
669        file. */
670     break;
671
672   case CF_READ_ABORTED:
673     /* The user bailed out of re-reading the capture file; the
674        capture file has been closed - just free the capture file name
675        string and return (without changing the last containing
676        directory). */
677     g_free(cf_name);
678     return;
679   }
680
681   /* Save the name of the containing directory specified in the path name,
682      if any; we can write over cf_name, which is a good thing, given that
683      "get_dirname()" does write over its argument. */
684   s = get_dirname(cf_name);
685   set_last_open_dir(s);
686   gtk_widget_grab_focus(packet_list);
687
688   g_free(cf_name);
689 }
690
691 static void
692 file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
693 {
694   /* Note that we no longer have a "Open Capture File" dialog box. */
695   file_open_w = NULL;
696 }
697
698 /*
699  * Keep a static pointer to the current "Merge Capture File" window, if
700  * any, so that if somebody tries to do "File:Merge" while there's already
701  * an "Merge Capture File" window up, we just pop up the existing one,
702  * rather than creating a new one.
703  */
704 static GtkWidget *file_merge_w;
705
706 /* Merge existing with another file */
707 static void
708 file_merge_cmd(GtkWidget *w)
709 {
710 #if _WIN32
711   win32_merge_file(GDK_WINDOW_HWND(top_level->window));
712   packet_list_freeze();
713   packet_list_thaw();
714 #else /* _WIN32 */
715   GtkWidget     *main_hb, *main_vb, *ft_hb, *ft_lb, *filter_hbox,
716                 *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
717                 *append_rb, *prev;
718   GtkTooltips *tooltips = gtk_tooltips_new();
719   /* No Apply button, and "OK" just sets our text widget, it doesn't
720      activate it (i.e., it doesn't cause us to try to open the file). */
721   static construct_args_t args = {
722         "Wireshark: Read Filter",
723         FALSE,
724         FALSE,
725     TRUE
726   };
727
728   if (file_merge_w != NULL) {
729     /* There's already an "Merge Capture File" dialog box; reactivate it. */
730     reactivate_window(file_merge_w);
731     return;
732   }
733
734   /* Default to saving all packets, in the file's current format. */
735   filetype = cfile.cd_t;
736
737   file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
738                                    FILE_SELECTION_OPEN);
739   /* it's annoying, that the file chooser dialog is already shown here,
740      so we cannot use the correct gtk_window_set_default_size() to resize it */
741   gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
742
743   switch (prefs.gui_fileopen_style) {
744
745   case FO_STYLE_LAST_OPENED:
746     /* The user has specified that we should start out in the last directory
747        we looked in.  If we've already opened a file, use its containing
748        directory, if we could determine it, as the directory, otherwise
749        use the "last opened" directory saved in the preferences file if
750        there was one. */
751     /* This is now the default behaviour in file_selection_new() */
752     break;
753
754   case FO_STYLE_SPECIFIED:
755     /* The user has specified that we should always start out in a
756        specified directory; if they've specified that directory,
757        start out by showing the files in that dir. */
758     if (prefs.gui_fileopen_dir[0] != '\0')
759       file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
760     break;
761   }
762
763   main_hb = gtk_hbox_new(FALSE, 3);
764   file_selection_set_extra_widget(file_merge_w, main_hb);
765   gtk_widget_show(main_hb);
766
767   /* Container for each row of widgets */
768   main_vb = gtk_vbox_new(FALSE, 3);
769   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
770   gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
771   gtk_widget_show(main_vb);
772
773   /* File type row */
774   range_tb = NULL;
775   ft_hb = gtk_hbox_new(FALSE, 3);
776   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
777   gtk_widget_show(ft_hb);
778
779   ft_lb = gtk_label_new("Merged output file type:");
780   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
781   gtk_widget_show(ft_lb);
782
783   ft_om = gtk_option_menu_new();
784
785   /* Generate the list of file types we can save. */
786   set_file_type_list(ft_om);
787   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
788   gtk_widget_show(ft_om);
789
790   filter_hbox = gtk_hbox_new(FALSE, 1);
791   gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
792   gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
793   gtk_widget_show(filter_hbox);
794
795   filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
796   g_signal_connect(filter_bt, "clicked",
797                    G_CALLBACK(display_filter_construct_cb), &args);
798   g_signal_connect(filter_bt, "destroy",
799                    G_CALLBACK(filter_button_destroy_cb), NULL);
800   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
801   gtk_widget_show(filter_bt);
802   gtk_tooltips_set_tip(tooltips, filter_bt,
803     "Open the \"Display Filter\" dialog, to edit/apply filters", NULL);
804
805   filter_te = gtk_entry_new();
806   g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
807   gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
808   g_signal_connect(filter_te, "changed",
809                    G_CALLBACK(filter_te_syntax_check_cb), NULL);
810   gtk_widget_show(filter_te);
811   gtk_tooltips_set_tip(tooltips, filter_te, "Enter a display filter.", NULL);
812
813   g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
814
815   prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
816       "Prepend packets to existing file");
817   gtk_tooltips_set_tip(tooltips, prepend_rb,
818       "The resulting file contains the packets from the selected, followed by the packets from the currently loaded file,"
819       " the packet timestamps will be ignored.", NULL);
820   gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
821   g_object_set_data(G_OBJECT(file_merge_w),
822                   E_MERGE_PREPEND_KEY, prepend_rb);
823   gtk_widget_show(prepend_rb);
824
825   chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge packets chronologically");
826   gtk_tooltips_set_tip(tooltips, chrono_rb,
827       "The resulting file contains all the packets from the currently loaded and the selected file,"
828       " sorted by the packet timestamps.", NULL);
829   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
830   gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
831   gtk_widget_show(chrono_rb);
832   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_CHRONO_KEY, chrono_rb);
833
834   append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets to existing file");
835   gtk_tooltips_set_tip(tooltips, append_rb,
836       "The resulting file contains the packets from the currently loaded, followed by the packets from the selected file,"
837       " the packet timestamps will be ignored.", NULL);
838   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(append_rb),
839         g_resolv_flags & RESOLV_TRANSPORT);
840   gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
841   gtk_widget_show(append_rb);
842   g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_APPEND_KEY, append_rb);
843
844   g_signal_connect(file_merge_w, "destroy",
845                    G_CALLBACK(file_merge_destroy_cb), NULL);
846
847   /* preview widget */
848   prev = preview_new();
849   g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
850   gtk_widget_show_all(prev);
851   gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
852
853   g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
854                    G_CALLBACK(file_open_entry_changed), file_merge_w);
855   file_open_entry_changed(file_merge_w, file_merge_w);
856
857   g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
858                     g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
859   if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
860   {
861     file_merge_ok_cb(file_merge_w, file_merge_w);
862   }
863   else window_destroy(file_merge_w);
864 #endif /* _WIN32 */
865 }
866
867 static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
868 {
869     switch(btn) {
870     case(ESD_BTN_OK):
871         /* save file first */
872         file_save_as_cmd(after_save_merge_dialog, data);
873         break;
874     case(ESD_BTN_CANCEL):
875         break;
876     default:
877         g_assert_not_reached();
878     }
879 }
880
881 void
882 file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
883   gpointer  dialog;
884
885   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
886     /* user didn't saved his current file, ask him */
887     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
888                 "%sSave the capture file before merging to another one?%s\n\n"
889                 "A temporary capture file can't be merged.",
890                 simple_dialog_primary_start(), simple_dialog_primary_end());
891     simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
892   } else {
893     /* unchanged file, just start to merge */
894     file_merge_cmd(widget);
895   }
896 }
897
898
899 static void
900 file_merge_ok_cb(GtkWidget *w, gpointer fs) {
901   gchar       *cf_name, *s;
902   const gchar *rfilter;
903   GtkWidget   *filter_te, *rb;
904   dfilter_t   *rfcode = NULL;
905   int          err;
906   cf_status_t  merge_status;
907   char        *in_filenames[2];
908   char        *tmpname;
909
910   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
911   filter_te = g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
912   rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
913   if (!dfilter_compile(rfilter, &rfcode)) {
914     bad_dfilter_alert_box(rfilter);
915     g_free(cf_name);
916     return;
917   }
918
919   /* Perhaps the user specified a directory instead of a file.
920      Check whether they did. */
921   if (test_for_directory(cf_name) == EISDIR) {
922         /* It's a directory - set the file selection box to display that
923            directory, don't try to open the directory as a capture file. */
924         set_last_open_dir(cf_name);
925         g_free(cf_name);
926         file_selection_set_current_folder(fs, get_last_open_dir());
927         return;
928   }
929
930   /* merge or append the two files */
931   rb = g_object_get_data(G_OBJECT(w), E_MERGE_CHRONO_KEY);
932   tmpname = NULL;
933   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
934       /* chronological order */
935       in_filenames[0] = cfile.filename;
936       in_filenames[1] = cf_name;
937       merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype, FALSE);
938   } else {
939       rb = g_object_get_data(G_OBJECT(w), E_MERGE_PREPEND_KEY);
940       if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
941           /* prepend file */
942           in_filenames[0] = cf_name;
943           in_filenames[1] = cfile.filename;
944           merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype,
945                                         TRUE);
946       } else {
947           /* append file */
948           in_filenames[0] = cfile.filename;
949           in_filenames[1] = cf_name;
950           merge_status = cf_merge_files(&tmpname, 2, in_filenames, filetype,
951                                         TRUE);
952       }
953   }
954
955   g_free(cf_name);
956
957   if (merge_status != CF_OK) {
958     if (rfcode != NULL)
959       dfilter_free(rfcode);
960     g_free(tmpname);
961     return;
962   }
963
964   cf_close(&cfile);
965
966   /* We've crossed the Rubicon; get rid of the file selection box. */
967   window_destroy(GTK_WIDGET (fs));
968
969   /* Try to open the merged capture file. */
970   if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
971     /* We couldn't open it; don't dismiss the open dialog box,
972        just leave it around so that the user can, after they
973        dismiss the alert box popped up for the open error,
974        try again. */
975     if (rfcode != NULL)
976       dfilter_free(rfcode);
977     g_free(tmpname);
978     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
979      * as this will prevent the user from closing the now existing error
980      * message, simply close the dialog (this is the best we can do here). */
981     if (file_open_w)
982       window_destroy(file_open_w);
983     return;
984   }
985   g_free(tmpname);
986
987   /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
988      it closed the previous capture file, and thus destroyed any
989      previous read filter attached to "cf"). */
990   cfile.rfcode = rfcode;
991
992   switch (cf_read(&cfile)) {
993
994   case CF_READ_OK:
995   case CF_READ_ERROR:
996     /* Just because we got an error, that doesn't mean we were unable
997        to read any of the file; we handle what we could get from the
998        file. */
999     break;
1000
1001   case CF_READ_ABORTED:
1002     /* The user bailed out of re-reading the capture file; the
1003        capture file has been closed - just free the capture file name
1004        string and return (without changing the last containing
1005        directory). */
1006     return;
1007   }
1008
1009   /* Save the name of the containing directory specified in the path name,
1010      if any; we can write over cf_merged_name, which is a good thing, given that
1011      "get_dirname()" does write over its argument. */
1012   s = get_dirname(tmpname);
1013   set_last_open_dir(s);
1014   gtk_widget_grab_focus(packet_list);
1015 }
1016
1017 static void
1018 file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1019 {
1020   /* Note that we no longer have a "Merge Capture File" dialog box. */
1021   file_merge_w = NULL;
1022 }
1023
1024
1025 static void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1026 {
1027     switch(btn) {
1028     case(ESD_BTN_SAVE):
1029         /* save file first */
1030         file_save_as_cmd(after_save_close_file, NULL);
1031         break;
1032     case(ESD_BTN_DONT_SAVE):
1033         cf_close(&cfile);
1034         break;
1035     case(ESD_BTN_CANCEL):
1036         break;
1037     default:
1038         g_assert_not_reached();
1039     }
1040 }
1041
1042 /* Close a file */
1043 void
1044 file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
1045   gpointer  dialog;
1046
1047   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1048     /* user didn't saved his current file, ask him */
1049     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
1050                 "%sSave capture file before closing it?%s\n\n"
1051                 "If you close without saving, your capture data will be discarded.",
1052                 simple_dialog_primary_start(), simple_dialog_primary_end());
1053
1054     simple_dialog_set_cb(dialog, file_close_answered_cb, NULL);
1055   } else {
1056     /* unchanged file, just close it */
1057     cf_close(&cfile);
1058   }
1059 }
1060
1061 void
1062 file_save_cmd_cb(GtkWidget *w, gpointer data) {
1063   /* If the file's already been saved, do nothing.  */
1064   if (cfile.user_saved)
1065     return;
1066
1067   /* Do a "Save As". */
1068   file_save_as_cmd_cb(w, data);
1069 }
1070
1071 static gboolean
1072 can_save_with_wiretap(int ft)
1073 {
1074   /* To save a file with Wiretap, Wiretap has to handle that format,
1075      and its code to handle that format must be able to write a file
1076      with this file's encapsulation type. */
1077   return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
1078 }
1079
1080
1081 /* Generate a list of the file types we can save this file as, by
1082    checking what Wiretap supports. */
1083 static void
1084 set_file_type_list(GtkWidget *option_menu)
1085 {
1086   GtkWidget *ft_menu, *ft_menu_item;
1087   int ft;
1088   guint index;
1089   gint item_to_select;
1090
1091   /* Default to the first supported file type, if the file's current
1092      type isn't supported. */
1093   item_to_select = -1;
1094
1095   ft_menu = gtk_menu_new();
1096
1097   /* Check all file types. */
1098   index = 0;
1099   for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
1100     if (can_save_with_wiretap(ft)) {
1101       /* OK, we can write it out in this type. */
1102       ft_menu_item = gtk_menu_item_new_with_label(wtap_file_type_string(ft));
1103       if (ft == filetype) {
1104         /* Default to the same format as the file, if it's supported. */
1105         item_to_select = index;
1106       }
1107       g_signal_connect(ft_menu_item, "activate", G_CALLBACK(select_file_type_cb),
1108                      GINT_TO_POINTER(ft));
1109       gtk_menu_append(GTK_MENU(ft_menu), ft_menu_item);
1110       gtk_widget_show(ft_menu_item);
1111       index++;
1112     }
1113   }
1114
1115   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), ft_menu);
1116   if (item_to_select >= 0) {
1117           /* Select the current File format in the menu */
1118           gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), item_to_select);
1119           select_file_type_cb(NULL, GINT_TO_POINTER(filetype));
1120   } else {
1121
1122           /*
1123            * Manually call the signal handler to activate the first menu item
1124            * since gtk_option_menu_set_history() doesn't do it for us. The first two
1125            * entries in the wiretap file types are placeholders so we start at #2, which
1126            * is the normal libpcap format.
1127            */
1128           gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), 0);
1129           select_file_type_cb(NULL, GINT_TO_POINTER(WTAP_FILE_PCAP));
1130   }
1131 }
1132
1133 static void
1134 select_file_type_cb(GtkWidget *w _U_, gpointer data)
1135 {
1136   int new_filetype = GPOINTER_TO_INT(data);
1137   GtkWidget *compressed_cb;
1138
1139   if (filetype != new_filetype) {
1140     filetype = new_filetype;
1141     compressed_cb = g_object_get_data(G_OBJECT(file_save_as_w), "compressed");
1142     if(compressed_cb)
1143             gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(new_filetype));
1144   }
1145 }
1146
1147
1148 /*
1149  * Update various dynamic parts of the range controls; called from outside
1150  * the file dialog code whenever the packet counts change.
1151  */
1152 void
1153 file_save_update_dynamics(void)
1154 {
1155   if (file_save_as_w == NULL) {
1156     /* We don't currently have a "Save As..." dialog box up. */
1157     return;
1158   }
1159
1160   range_update_dynamics(range_tb);
1161 }
1162
1163
1164 action_after_save_e action_after_save_g;
1165 gpointer            action_after_save_data_g;
1166
1167
1168 void
1169 file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data)
1170 {
1171 #if _WIN32
1172   win32_save_as_file(GDK_WINDOW_HWND(top_level->window), action_after_save, action_after_save_data);
1173 #else /* _WIN32 */
1174   GtkWidget     *main_vb, *ft_hb, *ft_lb, *range_fr, *compressed_cb;
1175   GtkTooltips   *tooltips;
1176
1177   if (file_save_as_w != NULL) {
1178     /* There's already an "Save Capture File As" dialog box; reactivate it. */
1179     reactivate_window(file_save_as_w);
1180     return;
1181   }
1182
1183   /* Default to saving all packets, in the file's current format. */
1184   filetype = cfile.cd_t;
1185
1186   /* init the packet range */
1187   packet_range_init(&range);
1188
1189   /* Enable tooltips */
1190   tooltips = gtk_tooltips_new();
1191
1192   /* build the file selection */
1193   file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
1194                                        FILE_SELECTION_SAVE);
1195
1196   /* as the dialog might already be gone, when using this values, we cannot
1197    * set data to the dialog object, but keep global values */
1198   action_after_save_g       = action_after_save;
1199   action_after_save_data_g  = action_after_save_data;
1200
1201   /* Container for each row of widgets */
1202
1203   main_vb = gtk_vbox_new(FALSE, 5);
1204   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1205   file_selection_set_extra_widget(file_save_as_w, main_vb);
1206   gtk_widget_show(main_vb);
1207
1208   /*** Packet Range frame ***/
1209   range_fr = gtk_frame_new("Packet Range");
1210   gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
1211   gtk_widget_show(range_fr);
1212
1213   /* range table */
1214   range_tb = range_new(&range);
1215   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
1216   gtk_widget_show(range_tb);
1217
1218   /* File type row */
1219   ft_hb = gtk_hbox_new(FALSE, 3);
1220   gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
1221   gtk_widget_show(ft_hb);
1222
1223   ft_lb = gtk_label_new("File type:");
1224   gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
1225   gtk_widget_show(ft_lb);
1226
1227   ft_om = gtk_option_menu_new();
1228
1229   /* Generate the list of file types we can save. */
1230   set_file_type_list(ft_om);
1231   gtk_box_pack_start(GTK_BOX(ft_hb), ft_om, FALSE, FALSE, 0);
1232   gtk_widget_show(ft_om);
1233
1234   /* dynamic values in the range frame */
1235   range_update_dynamics(range_tb);
1236
1237   /* compressed */
1238   compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
1239   gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
1240   /* XXX - disable output compression for now, as this doesn't work with the
1241    * current optimization to simply copy a capture file if it's using the same
1242    * encapsulation ... */
1243   /* the rest of the implementation is just working fine :-( */
1244   /*gtk_widget_show(compressed_cb);*/
1245   g_object_set_data(G_OBJECT(file_save_as_w), "compressed", compressed_cb);
1246   gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(cfile.cd_t));
1247
1248   g_signal_connect(file_save_as_w, "destroy",
1249                    G_CALLBACK(file_save_as_destroy_cb), NULL);
1250
1251   if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) == GTK_RESPONSE_ACCEPT) {
1252     file_save_as_ok_cb(file_save_as_w, file_save_as_w);
1253   } else {
1254     window_destroy(file_save_as_w);
1255   }
1256 #endif /* _WIN32 */
1257 }
1258
1259 void
1260 file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
1261 {
1262   file_save_as_cmd(after_save_no_action, NULL);
1263 }
1264
1265
1266 /* all tests ok, we only have to save the file */
1267 /* (and probably continue with a pending operation) */
1268 static void
1269 file_save_as_cb(GtkWidget *w _U_, gpointer fs) {
1270   gchar *cf_name;
1271   gchar *dirname;
1272   GtkWidget *compressed_cb;
1273
1274
1275   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1276
1277   compressed_cb = g_object_get_data(G_OBJECT(file_save_as_w), "compressed");
1278
1279   /* XXX - if the user requests to save to an already existing filename, */
1280   /* ask in a dialog if that's intended */
1281   /* currently, cf_save() will simply deny it */
1282
1283   /* Write out the packets (all, or only the ones from the current
1284      range) to the file with the specified name. */
1285   if (cf_save(&cfile, cf_name, &range, filetype,
1286           gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
1287     /* The write failed; don't dismiss the open dialog box,
1288        just leave it around so that the user can, after they
1289        dismiss the alert box popped up for the error, try again. */
1290     g_free(cf_name);
1291     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1292      * as this will prevent the user from closing the now existing error
1293      * message, simply close the dialog (this is the best we can do here). */
1294     if (file_save_as_w)
1295       window_destroy(GTK_WIDGET (fs));
1296     return;
1297   }
1298
1299   /* The write succeeded; get rid of the file selection box. */
1300   /* cf_save() might already closed our dialog! */
1301   if (file_save_as_w)
1302     window_destroy(GTK_WIDGET (fs));
1303
1304   /* Save the directory name for future file dialogs. */
1305   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1306   set_last_open_dir(dirname);
1307   g_free(cf_name);
1308
1309   /* we have finished saving, do we have pending things to do? */
1310   switch(action_after_save_g) {
1311   case(after_save_no_action):
1312       break;
1313   case(after_save_open_dialog):
1314       file_open_cmd(action_after_save_data_g);
1315       break;
1316   case(after_save_open_recent_file):
1317       menu_open_recent_file_cmd(action_after_save_data_g);
1318       break;
1319   case(after_save_open_dnd_file):
1320       dnd_open_file_cmd(action_after_save_data_g);
1321       break;
1322   case(after_save_merge_dialog):
1323       file_merge_cmd(action_after_save_data_g);
1324       break;
1325 #ifdef HAVE_LIBPCAP
1326   case(after_save_capture_dialog):
1327       capture_start_confirmed();
1328       break;
1329 #endif
1330   case(after_save_close_file):
1331       cf_close(&cfile);
1332       break;
1333   case(after_save_exit):
1334       main_do_quit();
1335       break;
1336   default:
1337       g_assert_not_reached();
1338   }
1339
1340   action_after_save_g = after_save_no_action;
1341 }
1342
1343
1344 static void file_save_as_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
1345 {
1346     gchar       *cf_name;
1347
1348     cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data)));
1349
1350     switch(btn) {
1351     case(ESD_BTN_OK):
1352         /* save file */
1353         ws_unlink(cf_name);
1354         file_save_as_cb(NULL, data);
1355         break;
1356     case(ESD_BTN_CANCEL):
1357         /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1358          * as this will prevent the user from closing the now existing error
1359          * message, simply close the dialog (this is the best we can do here). */
1360         if (file_save_as_w)
1361             window_destroy(file_save_as_w);
1362         break;
1363     default:
1364         g_assert_not_reached();
1365     }
1366 }
1367
1368
1369 /* user pressed "Save" dialog "Ok" button */
1370 static void
1371 file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
1372   gchar *cf_name;
1373   gpointer  dialog;
1374
1375   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1376
1377   /* Perhaps the user specified a directory instead of a file.
1378      Check whether they did. */
1379   if (test_for_directory(cf_name) == EISDIR) {
1380         /* It's a directory - set the file selection box to display that
1381            directory, and leave the selection box displayed. */
1382         set_last_open_dir(cf_name);
1383         g_free(cf_name);
1384         file_selection_set_current_folder(fs, get_last_open_dir());
1385         return;
1386   }
1387
1388   /* Check whether the range is valid. */
1389   if (!range_check_validity(&range)) {
1390     /* The range isn't valid; don't dismiss the open dialog box,
1391        just leave it around so that the user can, after they
1392        dismiss the alert box popped up for the error, try again. */
1393     g_free(cf_name);
1394     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1395      * as this will prevent the user from closing the now existing error
1396      * message, simply close the dialog (this is the best we can do here). */
1397     if (file_save_as_w)
1398       window_destroy(GTK_WIDGET (fs));
1399
1400     return;
1401   }
1402
1403   /*
1404    * Check that the from file is not the same as to file
1405    * We do it here so we catch all cases ...
1406    * Unfortunately, the file requester gives us an absolute file
1407    * name and the read file name may be relative (if supplied on
1408    * the command line). From Joerg Mayer.
1409    */
1410   if (files_identical(cfile.filename, cf_name)) {
1411     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1412       "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
1413       "Please choose a different filename.",
1414       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1415     g_free(cf_name);
1416     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1417      * as this will prevent the user from closing the now existing error
1418      * message, simply close the dialog (this is the best we can do here). */
1419     if (file_save_as_w)
1420       window_destroy(GTK_WIDGET (fs));
1421
1422     return;
1423   }
1424
1425   /* don't show the dialog while saving (or asking) */
1426   gtk_widget_hide(GTK_WIDGET (fs));
1427
1428   /* it the file doesn't exist, simply try to save it */
1429   if (!file_exists(cf_name)) {
1430     file_save_as_cb(NULL, fs);
1431     g_free(cf_name);
1432     return;
1433   }
1434
1435   /* the file exists, ask the user to remove it first */
1436   dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
1437       "%sA file named \"%s\" already exists.%s\n\n"
1438       "Do you want to replace it with the capture you are saving?",
1439       simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
1440   simple_dialog_set_cb(dialog, file_save_as_exists_answered_cb, fs);
1441
1442   g_free(cf_name);
1443 }
1444
1445 void
1446 file_save_as_destroy(void)
1447 {
1448   if (file_save_as_w)
1449     window_destroy(file_save_as_w);
1450 }
1451
1452 static void
1453 file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1454 {
1455   /* Note that we no longer have a "Save Capture File As" dialog box. */
1456   file_save_as_w = NULL;
1457 }
1458
1459 /* Reload a file using the current read and display filters */
1460 void
1461 file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
1462   cf_reload(&cfile);
1463 }
1464
1465 /******************** Color Filters *********************************/
1466 /*
1467  * Keep a static pointer to the current "Color Export" window, if
1468  * any, so that if somebody tries to do "Export"
1469  * while there's already a "Color Export" window up, we just pop
1470  * up the existing one, rather than creating a new one.
1471  */
1472 static GtkWidget *file_color_import_w;
1473
1474 /* sets the file path to the global color filter file.
1475    WARNING: called by both the import and the export dialog.
1476 */
1477 static void
1478 color_global_cb(GtkWidget *widget _U_, gpointer data)
1479 {
1480   GtkWidget *fs_widget = data;
1481   gchar *path;
1482
1483   /* decide what file to open (from dfilter code) */
1484   path = get_datafile_path("colorfilters");
1485
1486   gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
1487
1488   g_free((gchar *)path);
1489 }
1490
1491 /* Import color filters */
1492 void
1493 file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
1494 {
1495 #if _WIN32
1496   win32_import_color_file(GDK_WINDOW_HWND(top_level->window), color_filters);
1497 #else /* _WIN32 */
1498   GtkWidget     *main_vb, *cfglobal_but;
1499
1500   /* No Apply button, and "OK" just sets our text widget, it doesn't
1501      activate it (i.e., it doesn't cause us to try to open the file). */
1502
1503   if (file_color_import_w != NULL) {
1504     /* There's already an "Import Color Filters" dialog box; reactivate it. */
1505     reactivate_window(file_color_import_w);
1506     return;
1507   }
1508
1509   file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
1510                                            FILE_SELECTION_OPEN);
1511
1512   /* Container for each row of widgets */
1513   main_vb = gtk_vbox_new(FALSE, 3);
1514   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1515   file_selection_set_extra_widget(file_color_import_w, main_vb);
1516   gtk_widget_show(main_vb);
1517
1518
1519   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1520   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1521   g_signal_connect(cfglobal_but, "clicked",
1522                    G_CALLBACK(color_global_cb), file_color_import_w);
1523   gtk_widget_show(cfglobal_but);
1524
1525   g_signal_connect(file_color_import_w, "destroy",
1526                    G_CALLBACK(file_color_import_destroy_cb), NULL);
1527
1528
1529   if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
1530   {
1531       file_color_import_ok_cb(file_color_import_w, color_filters);
1532   }
1533   else window_destroy(file_color_import_w);
1534 #endif /* _WIN32 */
1535 }
1536
1537 static void
1538 file_color_import_ok_cb(GtkWidget *w, gpointer color_filters) {
1539   gchar     *cf_name, *s;
1540   GtkWidget *fs = gtk_widget_get_toplevel(w);
1541
1542   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1543
1544   /* Perhaps the user specified a directory instead of a file.
1545      Check whether they did. */
1546   if (test_for_directory(cf_name) == EISDIR) {
1547         /* It's a directory - set the file selection box to display that
1548            directory, don't try to open the directory as a color filter file. */
1549         set_last_open_dir(cf_name);
1550         g_free(cf_name);
1551         file_selection_set_current_folder(fs, get_last_open_dir());
1552         return;
1553   }
1554
1555   /* Try to open the color filter file. */
1556
1557   if (!color_filters_import(cf_name, color_filters)) {
1558     /* We couldn't open it; don't dismiss the open dialog box,
1559        just leave it around so that the user can, after they
1560        dismiss the alert box popped up for the open error,
1561        try again. */
1562     g_free(cf_name);
1563     /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1564      * as this will prevent the user from closing the now existing error
1565      * message, simply close the dialog (this is the best we can do here). */
1566     if (file_save_as_w)
1567       window_destroy(GTK_WIDGET (fs));
1568
1569     return;
1570   }
1571
1572   /* We've crossed the Rubicon; get rid of the file selection box. */
1573   window_destroy(GTK_WIDGET (fs));
1574
1575   /* Save the name of the containing directory specified in the path name,
1576      if any; we can write over cf_name, which is a good thing, given that
1577      "get_dirname()" does write over its argument. */
1578   s = get_dirname(cf_name);
1579   set_last_open_dir(s);
1580   gtk_widget_grab_focus(packet_list);
1581
1582   g_free(cf_name);
1583 }
1584
1585 static void
1586 file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1587 {
1588   /* Note that we no longer have a "Open Capture File" dialog box. */
1589   file_color_import_w = NULL;
1590 }
1591
1592 static GtkWidget *file_color_export_w;
1593 /*
1594  * Set the "Export only selected filters" toggle button as appropriate for
1595  * the current output file type and count of selected filters.
1596  *
1597  * Called when the "Export" dialog box is created and when the selected
1598  * count changes.
1599  */
1600 static void
1601 color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
1602 {
1603   if (file_color_export_w == NULL) {
1604     /* We don't currently have an "Export" dialog box up. */
1605     return;
1606   }
1607
1608   /* We can request that only the selected filters be saved only if
1609         there *are* selected filters. */
1610   if (color_selected_count() != 0)
1611     gtk_widget_set_sensitive(cfselect_cb, TRUE);
1612   else {
1613     /* Force the "Export only selected filters" toggle to "false", turn
1614        off the flag it controls. */
1615     color_selected = FALSE;
1616     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1617     gtk_widget_set_sensitive(cfselect_cb, FALSE);
1618   }
1619 }
1620
1621 static void
1622 color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
1623 {
1624   color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1625 }
1626
1627 void
1628 file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
1629 {
1630 #if _WIN32
1631   win32_export_color_file(GDK_WINDOW_HWND(top_level->window), filter_list);
1632 #else /* _WIN32 */
1633   GtkWidget *main_vb, *cfglobal_but;
1634
1635   if (file_color_export_w != NULL) {
1636     /* There's already an "Color Filter Export" dialog box; reactivate it. */
1637     reactivate_window(file_color_export_w);
1638     return;
1639   }
1640
1641   /* Default to saving all packets, in the file's current format. */
1642   color_selected   = FALSE;
1643   filetype = cfile.cd_t;
1644
1645   file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
1646                                            FILE_SELECTION_SAVE);
1647
1648   /* Container for each row of widgets */
1649   main_vb = gtk_vbox_new(FALSE, 3);
1650   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1651   file_selection_set_extra_widget(file_color_export_w, main_vb);
1652   gtk_widget_show(main_vb);
1653
1654   cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
1655   gtk_container_add(GTK_CONTAINER(main_vb), cfselect_cb);
1656   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
1657   g_signal_connect(cfselect_cb, "toggled",
1658                    G_CALLBACK(color_toggle_selected_cb), NULL);
1659   gtk_widget_show(cfselect_cb);
1660   color_set_export_selected_sensitive(cfselect_cb);
1661
1662   cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
1663   gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
1664   g_signal_connect(cfglobal_but, "clicked",
1665                    G_CALLBACK(color_global_cb), file_color_export_w);
1666   gtk_widget_show(cfglobal_but);
1667
1668   g_signal_connect(file_color_export_w, "destroy",
1669                    G_CALLBACK(file_color_export_destroy_cb), NULL);
1670
1671   if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
1672   {
1673       file_color_export_ok_cb(file_color_export_w, filter_list);
1674   }
1675   else window_destroy(file_color_export_w);
1676 #endif /* _WIN32 */
1677 }
1678
1679 static void
1680 file_color_export_ok_cb(GtkWidget *w, gpointer filter_list) {
1681   gchar *cf_name;
1682   gchar *dirname;
1683   GtkWidget *fs = gtk_widget_get_toplevel(w);
1684
1685   cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1686
1687   /* Perhaps the user specified a directory instead of a file.
1688      Check whether they did. */
1689   if (test_for_directory(cf_name) == EISDIR) {
1690         /* It's a directory - set the file selection box to display that
1691            directory, and leave the selection box displayed. */
1692         set_last_open_dir(cf_name);
1693         g_free(cf_name);
1694         file_selection_set_current_folder(fs, get_last_open_dir());
1695         return;
1696   }
1697
1698   /* Write out the filters (all, or only the ones that are currently
1699      displayed or selected) to the file with the specified name. */
1700
1701    if (!color_filters_export(cf_name, filter_list, color_selected))
1702    {
1703     /* The write failed; don't dismiss the open dialog box,
1704        just leave it around so that the user can, after they
1705        dismiss the alert box popped up for the error, try again. */
1706        g_free(cf_name);
1707
1708       /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
1709        * as this will prevent the user from closing the now existing error
1710        * message, simply close the dialog (this is the best we can do here). */
1711       if (file_save_as_w)
1712         window_destroy(GTK_WIDGET (fs));
1713
1714        return;
1715    }
1716
1717   /* The write succeeded; get rid of the file selection box. */
1718   window_destroy(GTK_WIDGET (fs));
1719
1720   /* Save the directory name for future file dialogs. */
1721   dirname = get_dirname(cf_name);  /* Overwrites cf_name */
1722   set_last_open_dir(dirname);
1723   g_free(cf_name);
1724 }
1725
1726 static void
1727 file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
1728 {
1729   file_color_export_w = NULL;
1730 }