Make the bytes-written information from Wiretap a long, as we allow
[obnox/wireshark/wip.git] / gtk / capture_dlg.c
1 /* capture_dlg.c
2  * Routines for packet capture windows
3  *
4  * $Id: capture_dlg.c,v 1.50 2001/12/04 08:25:58 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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_LIBPCAP
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <gtk/gtk.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include <time.h>
41
42 #include <pcap.h>
43
44 #ifdef NEED_SNPRINTF_H
45 # include "snprintf.h"
46 #endif
47
48 #include "capture.h"
49 #include "globals.h"
50 #include "resolv.h"
51 #include "main.h"
52 #include "ui_util.h"
53 #include "capture_dlg.h"
54 #include "filter_prefs.h"
55 #include "simple_dialog.h"
56 #include "dlg_utils.h"
57 #include "pcap-util.h"
58 #include "prefs.h"
59 #include "ringbuffer.h"
60
61 #ifdef _WIN32
62 #include "capture-wpcap.h"
63 #endif
64
65 /* Capture callback data keys */
66 #define E_CAP_IFACE_KEY       "cap_iface"
67 #define E_CAP_FILT_KEY        "cap_filter_te"
68 #define E_CAP_FILE_TE_KEY     "cap_file_te"
69 #define E_CAP_COUNT_KEY       "cap_count"
70 #define E_CAP_SNAP_KEY        "cap_snap"
71 #define E_CAP_PROMISC_KEY     "cap_promisc"
72 #define E_CAP_SYNC_KEY        "cap_sync"
73 #define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
74 #define E_CAP_M_RESOLVE_KEY   "cap_m_resolve"
75 #define E_CAP_N_RESOLVE_KEY   "cap_n_resolve"
76 #define E_CAP_T_RESOLVE_KEY   "cap_t_resolve"
77 #define E_CAP_FILESIZE_KEY    "cap_filesize"
78 #define E_CAP_DURATION_KEY    "cap_duration"
79 #define E_CAP_RING_TB_KEY     "cap_ringbuffer_tb"
80 #define E_CAP_RING_SB_KEY     "cap_ringbuffer_sb"
81
82 #define E_FS_CALLER_PTR_KEY       "fs_caller_ptr"
83 #define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
84
85 static void
86 capture_prep_file_cb(GtkWidget *w, gpointer te);
87
88 static void
89 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data);
90
91 static void
92 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data);
93
94 static void
95 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data);
96
97 static void
98 capture_prep_sync_toggle_cb(GtkWidget *sync_cb, gpointer parent_w);
99
100 static void
101 capture_prep_ringbuffer_toggle_cb(GtkWidget *ringbuffer_tb, gpointer parent_w);
102
103 static void
104 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
105
106 static void
107 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w);
108
109 static void
110 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
111
112 void
113 capture_stop_cb(GtkWidget *w, gpointer d)
114 {
115     capture_stop();
116 }
117
118 /*
119  * Keep a static pointer to the current "Capture Preferences" window, if
120  * any, so that if somebody tries to do "Capture:Start" while there's
121  * already a "Capture Preferences" window up, we just pop up the existing
122  * one, rather than creating a new one.
123  */
124 static GtkWidget *cap_open_w;
125
126 void
127 capture_prep_cb(GtkWidget *w, gpointer d)
128 {
129   GtkWidget     *if_cb, *if_lb,
130                 *count_lb, *count_cb, *main_vb,
131                 *filter_bt, *filter_te,
132                 *file_bt, *file_te,
133                 *filesize_lb, *filesize_cb,
134                 *duration_lb, *duration_cb,
135                 *caplen_hb, *table,
136                 *bbox, *ok_bt, *cancel_bt, *snap_lb,
137                 *snap_sb, *promisc_cb, *sync_cb, *auto_scroll_cb,
138                 *m_resolv_cb, *n_resolv_cb, *t_resolv_cb,
139                 *ringbuffer_hb, *ringbuffer_on_tb, *ringbuffer_nbf_lb, *ringbuffer_nbf_sb;
140   GtkAccelGroup *accel_group;
141   GtkAdjustment *snap_adj, *ringbuffer_nbf_adj;
142   GList         *if_list, *count_list = NULL, *filesize_list = NULL, *duration_list = NULL;
143   gchar         *count_item1 = "0 (Infinite)", count_item2[16],
144                 *filesize_item1 = "0 (Infinite)", filesize_item2[16],
145                 *duration_item1 = "0 (Infinite)", duration_item2[16];
146   int           err;
147   char          err_str[PCAP_ERRBUF_SIZE];
148
149   if (cap_open_w != NULL) {
150     /* There's already a "Capture Preferences" dialog box; reactivate it. */
151     reactivate_window(cap_open_w);
152     return;
153   }
154
155 #ifdef _WIN32
156   /* Is WPcap loaded? */
157   if (!has_wpcap) {
158           simple_dialog(ESD_TYPE_CRIT, NULL,
159                   "Unable to load WinPcap (wpcap.dll); Ethereal will not be able\n"
160                   "to capture packets.\n\n"
161                   "In order to capture packets, WinPcap must be installed; see\n"
162                   "\n"
163                   "        http://netgroup-serv.polito.it/winpcap/\n"
164                   "\n"
165                   "or the mirror at\n"
166                   "\n"
167                   "        http://netgroup-mirror.ethereal.com/winpcap/\n"
168                   "\n"
169                   "or the mirror at\n"
170                   "\n"
171                   "        http://www.wiretapped.net/security/packet-capture/winpcap/default.htm\n"
172                   "\n"
173                   "for a downloadable version of WinPcap and for instructions\n"
174                   "on how to install WinPcap.");
175           return;
176   }
177 #endif
178
179   if_list = get_interface_list(&err, err_str);
180   if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
181     simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
182                         err_str);
183   }
184   
185   cap_open_w = dlg_window_new("Ethereal: Capture Preferences");
186   gtk_signal_connect(GTK_OBJECT(cap_open_w), "destroy",
187         GTK_SIGNAL_FUNC(capture_prep_destroy_cb), NULL);
188
189   /* Accelerator group for the accelerators (or, as they're called in
190      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
191      Ctrl+<key> is an accelerator). */
192   accel_group = gtk_accel_group_new();
193   gtk_window_add_accel_group(GTK_WINDOW(cap_open_w), accel_group);
194   
195   main_vb = gtk_vbox_new(FALSE, 3);
196   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
197   gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
198   gtk_widget_show(main_vb);
199   
200   /* Table : container of the first 6 rows */
201   table = gtk_table_new (6, 2, FALSE);
202   gtk_table_set_row_spacings(GTK_TABLE (table), 5);
203   gtk_table_set_col_spacings(GTK_TABLE (table), 5);
204   gtk_container_add(GTK_CONTAINER(main_vb), table);
205   gtk_widget_show(table);
206
207   /* Interface row */
208   
209   if_lb = gtk_label_new("Interface:");
210   gtk_table_attach_defaults(GTK_TABLE(table), if_lb, 0, 1, 0, 1);
211   gtk_widget_show(if_lb);
212   
213   if_cb = gtk_combo_new();
214   if (if_list != NULL)
215     gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
216   if (cfile.iface)
217     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cfile.iface);
218   else if (if_list)
219     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
220   gtk_table_attach_defaults(GTK_TABLE(table), if_cb, 1, 2, 0, 1);
221   gtk_widget_show(if_cb);
222   
223   free_interface_list(if_list);
224
225   /* Count row */
226   
227   count_lb = gtk_label_new("Count:");
228   gtk_table_attach_defaults(GTK_TABLE(table), count_lb, 0, 1, 1, 2);
229   gtk_widget_show(count_lb);
230   
231   count_list = g_list_append(count_list, count_item1);
232   if (cfile.count) {
233     snprintf(count_item2, 15, "%d", cfile.count);
234     count_list = g_list_append(count_list, count_item2);
235   }
236
237   count_cb = gtk_combo_new();
238   gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
239   gtk_table_attach_defaults(GTK_TABLE(table), count_cb, 1, 2, 1, 2);
240   gtk_widget_show(count_cb);
241
242   while (count_list)
243     count_list = g_list_remove_link(count_list, count_list);
244
245   /* Filesize row */
246   
247   filesize_lb = gtk_label_new("File size:");
248   gtk_table_attach_defaults(GTK_TABLE(table), filesize_lb, 0, 1, 2, 3);
249   gtk_widget_show(filesize_lb);
250   
251   filesize_list = g_list_append(filesize_list, filesize_item1);
252   if (cfile.autostop_filesize) {
253     snprintf(filesize_item2, 15, "%d", cfile.autostop_filesize);
254     filesize_list = g_list_append(filesize_list, filesize_item2);
255   }
256
257   filesize_cb = gtk_combo_new();
258   gtk_combo_set_popdown_strings(GTK_COMBO(filesize_cb), filesize_list);
259   gtk_table_attach_defaults(GTK_TABLE(table), filesize_cb, 1, 2, 2, 3);
260   gtk_widget_show(filesize_cb);
261
262   while (filesize_list)
263     filesize_list = g_list_remove_link(filesize_list, filesize_list);
264
265   /* Duration row */
266   
267   duration_lb = gtk_label_new("Duration:");
268   gtk_table_attach_defaults(GTK_TABLE(table), duration_lb, 0, 1, 3, 4);
269   gtk_widget_show(duration_lb);
270   
271   duration_list = g_list_append(duration_list, duration_item1);
272   if (cfile.autostop_duration) {
273     snprintf(duration_item2, 15, "%d", cfile.autostop_duration);
274     duration_list = g_list_append(duration_list, duration_item2);
275   }
276
277   duration_cb = gtk_combo_new();
278   gtk_combo_set_popdown_strings(GTK_COMBO(duration_cb), duration_list);
279   gtk_table_attach_defaults(GTK_TABLE(table), duration_cb, 1, 2, 3, 4);
280   gtk_widget_show(duration_cb);
281
282   while (duration_list)
283     duration_list = g_list_remove_link(duration_list, duration_list);
284
285   /* Filter row */
286   
287   filter_bt = gtk_button_new_with_label("Filter:");
288   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
289     GTK_SIGNAL_FUNC(capture_filter_construct_cb), NULL);
290   gtk_table_attach_defaults(GTK_TABLE(table), filter_bt, 0, 1, 4, 5);
291   gtk_widget_show(filter_bt);
292   
293   filter_te = gtk_entry_new();
294   if (cfile.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.cfilter);
295   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
296   gtk_table_attach_defaults(GTK_TABLE(table), filter_te, 1, 2, 4, 5);
297   gtk_widget_show(filter_te);
298   
299   /* File row */
300   
301   file_bt = gtk_button_new_with_label("File:");
302   gtk_table_attach_defaults(GTK_TABLE(table), file_bt, 0, 1, 5, 6);
303   gtk_widget_show(file_bt);
304   
305   file_te = gtk_entry_new();
306   gtk_table_attach_defaults(GTK_TABLE(table), file_te, 1, 2, 5, 6);
307   gtk_widget_show(file_te);
308
309   gtk_signal_connect(GTK_OBJECT(file_bt), "clicked",
310     GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_te));
311
312   /* Misc row: Snap spinbutton */
313   caplen_hb = gtk_hbox_new(FALSE, 3);
314   gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
315   gtk_widget_show(caplen_hb);
316
317   snap_lb = gtk_label_new("Capture length");
318   gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
319   gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
320   gtk_widget_show(snap_lb);
321
322   snap_adj = (GtkAdjustment *) gtk_adjustment_new((float) cfile.snap,
323     MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
324   snap_sb = gtk_spin_button_new (snap_adj, 0, 0);
325   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
326   gtk_widget_set_usize (snap_sb, 80, 0);
327   gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3); 
328   gtk_widget_show(snap_sb);
329   
330   promisc_cb = dlg_check_button_new_with_label_with_mnemonic(
331                 "Capture packets in _promiscuous mode", accel_group);
332   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(promisc_cb), prefs.capture_prom_mode);
333   gtk_container_add(GTK_CONTAINER(main_vb), promisc_cb);
334   gtk_widget_show(promisc_cb);
335
336   /* Misc row: Ringbuffer toggle button and Ringbuffer spinbutton */
337   ringbuffer_hb = gtk_hbox_new(FALSE, 3);
338   gtk_container_add(GTK_CONTAINER(main_vb), ringbuffer_hb);
339   gtk_widget_show(ringbuffer_hb);
340
341   ringbuffer_on_tb = dlg_check_button_new_with_label_with_mnemonic(
342     "Use _ringbuffer", accel_group);
343   if (prefs.capture_real_time == TRUE)
344     cfile.ringbuffer_on = FALSE;
345   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ringbuffer_on_tb),cfile.ringbuffer_on);
346   gtk_signal_connect(GTK_OBJECT(ringbuffer_on_tb), "toggled",
347     GTK_SIGNAL_FUNC(capture_prep_ringbuffer_toggle_cb), GTK_OBJECT(cap_open_w));
348   gtk_box_pack_start(GTK_BOX(ringbuffer_hb), ringbuffer_on_tb, FALSE, FALSE, 0);
349   gtk_widget_show(ringbuffer_on_tb);
350   
351   ringbuffer_nbf_lb = gtk_label_new("Number of files");
352   gtk_misc_set_alignment(GTK_MISC(ringbuffer_nbf_lb), 1, 0.5);
353   gtk_box_pack_start(GTK_BOX(ringbuffer_hb), ringbuffer_nbf_lb, FALSE, FALSE, 6);
354   gtk_widget_show(ringbuffer_nbf_lb);
355
356   ringbuffer_nbf_adj = (GtkAdjustment *) gtk_adjustment_new((float) cfile.ringbuffer_num_files,
357     RINGBUFFER_MIN_NUM_FILES, RINGBUFFER_MAX_NUM_FILES, 1.0, 10.0, 0.0);
358   ringbuffer_nbf_sb = gtk_spin_button_new (ringbuffer_nbf_adj, 0, 0);
359   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (ringbuffer_nbf_sb), TRUE);
360   gtk_widget_set_usize (ringbuffer_nbf_sb, 40, 0);
361   gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), cfile.ringbuffer_on);
362   gtk_box_pack_start (GTK_BOX(ringbuffer_hb), ringbuffer_nbf_sb, TRUE, TRUE, 0); 
363   gtk_widget_show(ringbuffer_nbf_sb);
364   
365   /* Misc row: Capture file checkboxes */
366   sync_cb = dlg_check_button_new_with_label_with_mnemonic(
367                 "_Update list of packets in real time", accel_group);
368   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sync_cb), prefs.capture_real_time);
369   gtk_signal_connect(GTK_OBJECT(sync_cb), "toggled",
370     GTK_SIGNAL_FUNC(capture_prep_sync_toggle_cb), GTK_OBJECT(cap_open_w));
371   gtk_container_add(GTK_CONTAINER(main_vb), sync_cb);
372   gtk_widget_show(sync_cb);
373
374   auto_scroll_cb = dlg_check_button_new_with_label_with_mnemonic(
375                 "_Automatic scrolling in live capture", accel_group);
376   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(auto_scroll_cb), prefs.capture_auto_scroll);
377   gtk_container_add(GTK_CONTAINER(main_vb), auto_scroll_cb);
378   gtk_widget_show(auto_scroll_cb);
379
380   m_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
381                 "Enable _MAC name resolution", accel_group);
382   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(m_resolv_cb),
383                 prefs.name_resolve & PREFS_RESOLV_MAC);
384   gtk_container_add(GTK_CONTAINER(main_vb), m_resolv_cb);
385   gtk_widget_show(m_resolv_cb);
386
387   n_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
388                 "Enable _network name resolution", accel_group);
389   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(n_resolv_cb),
390                 prefs.name_resolve & PREFS_RESOLV_NETWORK);
391   gtk_container_add(GTK_CONTAINER(main_vb), n_resolv_cb);
392   gtk_widget_show(n_resolv_cb);
393
394   t_resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
395                 "Enable _transport name resolution", accel_group);
396   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(t_resolv_cb),
397                 prefs.name_resolve & PREFS_RESOLV_TRANSPORT);
398   gtk_container_add(GTK_CONTAINER(main_vb), t_resolv_cb);
399   gtk_widget_show(t_resolv_cb);
400   
401   /* Button row: OK and cancel buttons */
402   bbox = gtk_hbutton_box_new();
403   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
404   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
405   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
406   gtk_widget_show(bbox);
407
408   ok_bt = gtk_button_new_with_label ("OK");
409   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
410     GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
411   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
412   gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
413   gtk_widget_grab_default(ok_bt);
414   gtk_widget_show(ok_bt);
415
416   cancel_bt = gtk_button_new_with_label ("Cancel");
417   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
418     GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
419   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
420   gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
421   gtk_widget_show(cancel_bt);
422
423   /* Attach pointers to needed widgets to the capture prefs window/object */
424   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
425   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY,  filter_te);
426   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY,  file_te);
427   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
428   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY,  snap_sb);
429   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_PROMISC_KEY, promisc_cb);
430   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SYNC_KEY,  sync_cb);
431   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
432   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_M_RESOLVE_KEY,  m_resolv_cb);
433   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_N_RESOLVE_KEY,  n_resolv_cb);
434   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_T_RESOLVE_KEY,  t_resolv_cb);
435   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILESIZE_KEY,  filesize_cb);
436   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_DURATION_KEY,  duration_cb);
437   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RING_TB_KEY,  ringbuffer_on_tb);
438   gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RING_SB_KEY,  ringbuffer_nbf_sb);
439
440   /* Catch the "activate" signal on the frame number and file name text
441      entries, so that if the user types Return there, we act as if the
442      "OK" button had been selected, as happens if Return is typed if some
443      widget that *doesn't* handle the Return key has the input focus. */
444   dlg_set_activate(filter_te, ok_bt);
445   dlg_set_activate(file_te, ok_bt);
446
447   /* Catch the "key_press_event" signal in the window, so that we can catch
448      the ESC key being pressed and act as if the "Cancel" button had
449      been selected. */
450   dlg_set_cancel(cap_open_w, cancel_bt);
451
452   /* XXX - why does not
453
454      gtk_widget_grab_focus(if_cb);
455
456     give the initial focus to the "Interface" combo box?
457
458     Or should I phrase that as "why does GTK+ continually frustrate
459     attempts to make GUIs driveable from the keyboard?"  We have to
460     go catch the activate signal on every single GtkEntry widget
461     (rather than having widgets whose activate signal is *not*
462     caught not catch the Return keystroke, so that it passes on,
463     ultimately, to the window, which can activate the default
464     widget, i.e. the "OK" button); we have to catch the "key_press_event"
465     signal and have the handler check for ESC, so that we can have ESC
466     activate the "Cancel" button; in order to support Alt+<key> mnemonics
467     for buttons and the like, we may have to construct an accelerator
468     group by hand and set up the accelerators by hand (if that even
469     works - I've not tried it yet); we have to do a "gtk_widget_grab_focus()"
470     to keep some container widget from getting the initial focus, so that
471     you don't have to tab into the first widget in order to start typing
472     in it; and it now appears that you simply *can't* make a combo box
473     get the initial focus, at least not in the obvious fashion. Sigh.... */
474
475   gtk_widget_show(cap_open_w);
476 }
477
478 static void
479 capture_prep_file_cb(GtkWidget *w, gpointer file_te)
480 {
481   GtkWidget *caller = gtk_widget_get_toplevel(w);
482   GtkWidget *fs;
483
484   /* Has a file selection dialog box already been opened for that top-level
485      widget? */
486   fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
487
488   if (fs != NULL) {
489     /* Yes.  Just re-activate that dialog box. */
490     reactivate_window(fs);
491     return;
492   }
493
494   fs = gtk_file_selection_new ("Ethereal: Capture File");
495
496   gtk_object_set_data(GTK_OBJECT(fs), E_CAP_FILE_TE_KEY, file_te);
497
498   /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
499   gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
500
501   /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
502   gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
503
504   /* Call a handler when the file selection box is destroyed, so we can inform
505      our caller, if any, that it's been destroyed. */
506   gtk_signal_connect(GTK_OBJECT(fs), "destroy",
507             GTK_SIGNAL_FUNC(cap_prep_fs_destroy_cb), NULL);
508
509   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
510     "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, fs);
511
512   /* Connect the cancel_button to destroy the widget */
513   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
514     "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, fs);
515
516   /* Catch the "key_press_event" signal in the window, so that we can catch
517      the ESC key being pressed and act as if the "Cancel" button had
518      been selected. */
519   dlg_set_cancel(fs, GTK_FILE_SELECTION(fs)->cancel_button);
520   
521   gtk_widget_show(fs);
522 }
523
524 static void
525 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data)
526 {
527   gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
528       E_CAP_FILE_TE_KEY)),
529       gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
530   gtk_widget_destroy(GTK_WIDGET(data));
531 }
532
533 static void
534 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data)
535 {
536   gtk_widget_destroy(GTK_WIDGET(data));
537 }  
538
539 static void
540 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data)
541 {
542   GtkWidget *caller;
543
544   /* Get the widget that requested that we be popped up.
545      (It should arrange to destroy us if it's destroyed, so
546      that we don't get a pointer to a non-existent window here.) */
547   caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
548
549   /* Tell it we no longer exist. */
550   gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
551
552   /* Now nuke this window. */
553   gtk_grab_remove(GTK_WIDGET(win));
554   gtk_widget_destroy(GTK_WIDGET(win));
555 }
556
557 static int
558 get_positive_int(const char *string, const char *name)
559 {
560   long number;
561   char *p;
562
563   number = strtol(string, &p, 10);
564   /*
565    * XXX - we allow extra stuff after 0, so that we don't have
566    * problems with the "(Infinite)" value.
567    */
568   if (p == string || (*p != '\0' && number != 0)) {
569     simple_dialog(ESD_TYPE_CRIT, NULL,
570         "The specified %s is not a decimal number.", name);
571     return -1;
572   }
573   if (number < 0) {
574     simple_dialog(ESD_TYPE_CRIT, NULL,
575         "The specified %s is a negative number.", name);
576     return -1;
577   }
578   if (number > INT_MAX) {
579     simple_dialog(ESD_TYPE_CRIT, NULL,
580         "The specified %s is too large (greater than %d).", name, INT_MAX);
581     return -1;
582   }
583   return number;
584 }
585
586 static void
587 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
588   GtkWidget *if_cb, *filter_te, *file_te, *count_cb, *snap_sb, *promisc_cb,
589             *sync_cb, *auto_scroll_cb, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb,
590             *filesize_cb, *duration_cb, *ringbuffer_on_tb, *ringbuffer_nbf_sb;
591   gchar *if_text;
592   gchar *if_name;
593   gchar *filter_text;
594   gchar *save_file;
595   int value;
596
597   if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
598   filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
599   file_te   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
600   count_cb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
601   snap_sb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
602   promisc_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_PROMISC_KEY);
603   sync_cb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
604   auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
605   m_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_M_RESOLVE_KEY);
606   n_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_N_RESOLVE_KEY);
607   t_resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_T_RESOLVE_KEY);
608   filesize_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILESIZE_KEY);
609   duration_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_DURATION_KEY);
610   ringbuffer_on_tb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_TB_KEY);
611   ringbuffer_nbf_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_SB_KEY);
612
613   if_text =
614     g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
615   if_name = strtok(if_text, " \t");
616   if (if_name == NULL) {
617     simple_dialog(ESD_TYPE_CRIT, NULL,
618       "You didn't specify an interface on which to capture packets.");
619     g_free(if_text);
620     return;
621   }
622   if (cfile.iface)
623     g_free(cfile.iface);
624   cfile.iface = g_strdup(if_name);
625   g_free(if_text);
626
627   /* XXX - don't try to get clever and set "cfile.filter" to NULL if the
628      filter string is empty, as an indication that we don't have a filter
629      and thus don't have to set a filter when capturing - the version of
630      libpcap in Red Hat Linux 6.1, and versions based on later patches
631      in that series, don't bind the AF_PACKET socket to an interface
632      until a filter is set, which means they aren't bound at all if
633      no filter is set, which means no packets arrive as input on that
634      socket, which means Ethereal never sees any packets. */
635   filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
636   if (cfile.cfilter)
637     g_free(cfile.cfilter);
638   g_assert(filter_text != NULL);
639   cfile.cfilter = g_strdup(filter_text); 
640
641   save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
642   if (save_file && save_file[0]) {
643     /* User specified a file to which the capture should be written. */
644     save_file = g_strdup(save_file);
645   } else {
646     /* User didn't specify a file; save to a temporary file. */
647     save_file = NULL;
648   }
649
650   cfile.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
651
652   value = get_positive_int(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(filesize_cb)->entry)),
653       "maximum capture file size");
654   if (value == -1)
655     return;     /* error */
656   cfile.autostop_filesize = value;
657
658   value = get_positive_int(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(duration_cb)->entry)),
659       "capture duration");
660   if (value == -1)
661     return;     /* error */
662   cfile.autostop_duration = value;
663
664   cfile.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
665   if (cfile.snap < 1)
666     cfile.snap = WTAP_MAX_PACKET_SIZE;
667   else if (cfile.snap < MIN_PACKET_SIZE)
668     cfile.snap = MIN_PACKET_SIZE;
669
670   prefs.capture_prom_mode = GTK_TOGGLE_BUTTON (promisc_cb)->active;
671
672   prefs.capture_real_time = GTK_TOGGLE_BUTTON (sync_cb)->active;
673
674   prefs.capture_auto_scroll = GTK_TOGGLE_BUTTON (auto_scroll_cb)->active;
675
676   prefs.name_resolve = PREFS_RESOLV_NONE;
677   prefs.name_resolve |= (GTK_TOGGLE_BUTTON (m_resolv_cb)->active ? PREFS_RESOLV_MAC : PREFS_RESOLV_NONE);
678   prefs.name_resolve |= (GTK_TOGGLE_BUTTON (n_resolv_cb)->active ? PREFS_RESOLV_NETWORK : PREFS_RESOLV_NONE);
679   prefs.name_resolve |= (GTK_TOGGLE_BUTTON (t_resolv_cb)->active ? PREFS_RESOLV_TRANSPORT : PREFS_RESOLV_NONE);
680
681   cfile.ringbuffer_on = GTK_TOGGLE_BUTTON (ringbuffer_on_tb)->active && !(prefs.capture_real_time);
682   if (cfile.ringbuffer_on == TRUE) {
683     if (save_file == NULL) {
684       simple_dialog(ESD_TYPE_CRIT, NULL,
685         "You must specify a save file if you want to use the ringbuffer.");
686       return;
687     } else if (cfile.autostop_filesize == 0) {
688       simple_dialog(ESD_TYPE_CRIT, NULL,
689         "You must specify a maximum save file size other \nthan 0 (infinite) if you want to use the ringbuffer.");
690       return;
691     }
692   }
693
694   cfile.ringbuffer_num_files = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ringbuffer_nbf_sb));
695   if (cfile.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
696     cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
697   else if (cfile.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
698     cfile.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
699
700   gtk_widget_destroy(GTK_WIDGET(parent_w));
701
702   do_capture(save_file);
703 }
704
705 static void
706 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
707 {
708   gtk_grab_remove(GTK_WIDGET(parent_w));
709   gtk_widget_destroy(GTK_WIDGET(parent_w));
710 }
711
712 static void
713 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data)
714 {
715   GtkWidget *capture_prep_filter_w;
716   GtkWidget *fs;
717
718   /* Is there a filter edit/selection dialog associated with this
719      Capture Preferences dialog? */
720   capture_prep_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
721
722   if (capture_prep_filter_w != NULL) {
723     /* Yes.  Destroy it. */
724     gtk_widget_destroy(capture_prep_filter_w);
725   }
726
727   /* Is there a file selection dialog associated with this
728      Print File dialog? */
729   fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
730
731   if (fs != NULL) {
732     /* Yes.  Destroy it. */
733     gtk_widget_destroy(fs);
734   }
735
736   /* Note that we no longer have a "Capture Preferences" dialog box. */
737   cap_open_w = NULL;
738 }
739
740 static void
741 capture_prep_ringbuffer_toggle_cb(GtkWidget *ringbuffer_tb, gpointer parent_w)
742 {
743   GtkWidget *ringbuffer_nbf_sb, *sync_cb;
744
745   ringbuffer_nbf_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_SB_KEY);
746   sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
747
748   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_tb)) == TRUE) {
749     gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), TRUE);
750     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb)) == TRUE) {
751       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sync_cb), FALSE);
752     }
753   } else {
754     gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), FALSE);
755   }
756 }
757
758 static void
759 capture_prep_sync_toggle_cb(GtkWidget *sync_cb, gpointer parent_w)
760 {
761   GtkWidget *ringbuffer_on_tb;
762
763   ringbuffer_on_tb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RING_TB_KEY);
764
765   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb)) == TRUE) {
766     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb)) == TRUE) {
767       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ringbuffer_on_tb), FALSE);
768     }
769   }
770 }
771
772 #endif /* HAVE_LIBPCAP */