2 * Routines for packet capture windows
4 * $Id: capture_dlg.c,v 1.37 2001/01/28 23:56:29 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46 #ifdef NEED_SNPRINTF_H
47 # include "snprintf.h"
55 #include "capture_dlg.h"
56 #include "filter_prefs.h"
57 #include "simple_dialog.h"
58 #include "dlg_utils.h"
61 /* Capture callback data keys */
62 #define E_CAP_IFACE_KEY "cap_iface"
63 #define E_CAP_FILT_KEY "cap_filter_te"
64 #define E_CAP_FILE_TE_KEY "cap_file_te"
65 #define E_CAP_COUNT_KEY "cap_count"
66 #define E_CAP_SNAP_KEY "cap_snap"
67 #define E_CAP_PROMISC_KEY "cap_promisc"
68 #define E_CAP_SYNC_KEY "cap_sync"
69 #define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
70 #define E_CAP_RESOLVE_KEY "cap_resolve"
72 #define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
73 #define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
76 capture_prep_file_cb(GtkWidget *w, gpointer te);
79 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data);
82 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data);
85 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data);
88 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
91 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w);
94 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
97 capture_stop_cb(GtkWidget *w, gpointer d)
103 * Keep a static pointer to the current "Capture Preferences" window, if
104 * any, so that if somebody tries to do "Capture:Start" while there's
105 * already a "Capture Preferences" window up, we just pop up the existing
106 * one, rather than creating a new one.
108 static GtkWidget *cap_open_w;
111 capture_prep_cb(GtkWidget *w, gpointer d)
113 GtkWidget *if_cb, *if_lb,
114 *count_lb, *count_cb, *main_vb,
115 *filter_bt, *filter_te,
118 *bbox, *ok_bt, *cancel_bt, *snap_lb,
119 *snap_sb, *promisc_cb, *sync_cb, *auto_scroll_cb, *resolv_cb;
120 GtkAccelGroup *accel_group;
122 GList *if_list, *count_list = NULL;
123 gchar *count_item1 = "0 (Infinite)", count_item2[16];
125 char err_str[PCAP_ERRBUF_SIZE];
127 if (cap_open_w != NULL) {
128 /* There's already a "Capture Preferences" dialog box; reactivate it. */
129 reactivate_window(cap_open_w);
133 if_list = get_interface_list(&err, err_str);
134 if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
135 simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
139 cap_open_w = dlg_window_new("Ethereal: Capture Preferences");
140 gtk_signal_connect(GTK_OBJECT(cap_open_w), "destroy",
141 GTK_SIGNAL_FUNC(capture_prep_destroy_cb), NULL);
143 /* Accelerator group for the accelerators (or, as they're called in
144 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
145 Ctrl+<key> is an accelerator). */
146 accel_group = gtk_accel_group_new();
147 gtk_window_add_accel_group(GTK_WINDOW(cap_open_w), accel_group);
149 main_vb = gtk_vbox_new(FALSE, 3);
150 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
151 gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
152 gtk_widget_show(main_vb);
154 /* Table : container of the first 4 rows */
155 table = gtk_table_new (4, 2, FALSE);
156 gtk_table_set_row_spacings(GTK_TABLE (table), 5);
157 gtk_table_set_col_spacings(GTK_TABLE (table), 5);
158 gtk_container_add(GTK_CONTAINER(main_vb), table);
159 gtk_widget_show(table);
163 if_lb = gtk_label_new("Interface:");
164 gtk_table_attach_defaults(GTK_TABLE(table), if_lb, 0, 1, 0, 1);
165 gtk_widget_show(if_lb);
167 if_cb = gtk_combo_new();
169 gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
171 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cfile.iface);
173 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
174 gtk_table_attach_defaults(GTK_TABLE(table), if_cb, 1, 2, 0, 1);
175 gtk_widget_show(if_cb);
177 free_interface_list(if_list);
181 count_lb = gtk_label_new("Count:");
182 gtk_table_attach_defaults(GTK_TABLE(table), count_lb, 0, 1, 1, 2);
183 gtk_widget_show(count_lb);
185 count_list = g_list_append(count_list, count_item1);
187 snprintf(count_item2, 15, "%d", cfile.count);
188 count_list = g_list_append(count_list, count_item2);
191 count_cb = gtk_combo_new();
192 gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
193 gtk_table_attach_defaults(GTK_TABLE(table), count_cb, 1, 2, 1, 2);
194 gtk_widget_show(count_cb);
197 count_list = g_list_remove_link(count_list, count_list);
201 filter_bt = gtk_button_new_with_label("Filter:");
202 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
203 GTK_SIGNAL_FUNC(capture_filter_construct_cb), NULL);
204 gtk_table_attach_defaults(GTK_TABLE(table), filter_bt, 0, 1, 2, 3);
205 gtk_widget_show(filter_bt);
207 filter_te = gtk_entry_new();
208 if (cfile.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cfile.cfilter);
209 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
210 gtk_table_attach_defaults(GTK_TABLE(table), filter_te, 1, 2, 2, 3);
211 gtk_widget_show(filter_te);
215 file_bt = gtk_button_new_with_label("File:");
216 gtk_table_attach_defaults(GTK_TABLE(table), file_bt, 0, 1, 3, 4);
217 gtk_widget_show(file_bt);
219 file_te = gtk_entry_new();
220 gtk_table_attach_defaults(GTK_TABLE(table), file_te, 1, 2, 3, 4);
221 gtk_widget_show(file_te);
223 gtk_signal_connect(GTK_OBJECT(file_bt), "clicked",
224 GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_te));
226 /* Misc row: Capture file checkbox and snap spinbutton */
227 caplen_hb = gtk_hbox_new(FALSE, 3);
228 gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
229 gtk_widget_show(caplen_hb);
231 snap_lb = gtk_label_new("Capture length");
232 gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
233 gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
234 gtk_widget_show(snap_lb);
236 adj = (GtkAdjustment *) gtk_adjustment_new((float) cfile.snap,
237 MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
238 snap_sb = gtk_spin_button_new (adj, 0, 0);
239 gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
240 gtk_widget_set_usize (snap_sb, 80, 0);
241 gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3);
242 gtk_widget_show(snap_sb);
244 promisc_cb = dlg_check_button_new_with_label_with_mnemonic(
245 "Capture packets in _promiscuous mode", accel_group);
246 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(promisc_cb), promisc_mode);
247 gtk_container_add(GTK_CONTAINER(main_vb), promisc_cb);
248 gtk_widget_show(promisc_cb);
250 sync_cb = dlg_check_button_new_with_label_with_mnemonic(
251 "_Update list of packets in real time", accel_group);
252 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sync_cb), sync_mode);
253 gtk_container_add(GTK_CONTAINER(main_vb), sync_cb);
254 gtk_widget_show(sync_cb);
256 auto_scroll_cb = dlg_check_button_new_with_label_with_mnemonic(
257 "_Automatic scrolling in live capture", accel_group);
258 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(auto_scroll_cb), auto_scroll_live);
259 gtk_container_add(GTK_CONTAINER(main_vb), auto_scroll_cb);
260 gtk_widget_show(auto_scroll_cb);
262 resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
263 "Enable _name resolution", accel_group);
264 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(resolv_cb), g_resolving_actif);
265 gtk_container_add(GTK_CONTAINER(main_vb), resolv_cb);
266 gtk_widget_show(resolv_cb);
268 /* Button row: OK and cancel buttons */
269 bbox = gtk_hbutton_box_new();
270 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
271 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
272 gtk_container_add(GTK_CONTAINER(main_vb), bbox);
273 gtk_widget_show(bbox);
275 ok_bt = gtk_button_new_with_label ("OK");
276 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
277 GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
278 GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
279 gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
280 gtk_widget_grab_default(ok_bt);
281 gtk_widget_show(ok_bt);
283 cancel_bt = gtk_button_new_with_label ("Cancel");
284 gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
285 GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
286 GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
287 gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
288 gtk_widget_show(cancel_bt);
290 /* Attach pointers to needed widgets to the capture prefs window/object */
291 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
292 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY, filter_te);
293 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY, file_te);
294 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
295 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY, snap_sb);
296 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_PROMISC_KEY, promisc_cb);
297 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SYNC_KEY, sync_cb);
298 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
299 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RESOLVE_KEY, resolv_cb);
301 /* Catch the "activate" signal on the frame number and file name text
302 entries, so that if the user types Return there, we act as if the
303 "OK" button had been selected, as happens if Return is typed if some
304 widget that *doesn't* handle the Return key has the input focus. */
305 dlg_set_activate(filter_te, ok_bt);
306 dlg_set_activate(file_te, ok_bt);
308 /* Catch the "key_press_event" signal in the window, so that we can catch
309 the ESC key being pressed and act as if the "Cancel" button had
311 dlg_set_cancel(cap_open_w, cancel_bt);
313 /* XXX - why does not
315 gtk_widget_grab_focus(if_cb);
317 give the initial focus to the "Interface" combo box?
319 Or should I phrase that as "why does GTK+ continually frustrate
320 attempts to make GUIs driveable from the keyboard?" We have to
321 go catch the activate signal on every single GtkEntry widget
322 (rather than having widgets whose activate signal is *not*
323 caught not catch the Return keystroke, so that it passes on,
324 ultimately, to the window, which can activate the default
325 widget, i.e. the "OK" button); we have to catch the "key_press_event"
326 signal and have the handler check for ESC, so that we can have ESC
327 activate the "Cancel" button; in order to support Alt+<key> mnemonics
328 for buttons and the like, we may have to construct an accelerator
329 group by hand and set up the accelerators by hand (if that even
330 works - I've not tried it yet); we have to do a "gtk_widget_grab_focus()"
331 to keep some container widget from getting the initial focus, so that
332 you don't have to tab into the first widget in order to start typing
333 in it; and it now appears that you simply *can't* make a combo box
334 get the initial focus, at least not in the obvious fashion. Sigh.... */
336 gtk_widget_show(cap_open_w);
340 capture_prep_file_cb(GtkWidget *w, gpointer file_te)
342 GtkWidget *caller = gtk_widget_get_toplevel(w);
345 /* Has a file selection dialog box already been opened for that top-level
347 fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
350 /* Yes. Just re-activate that dialog box. */
351 reactivate_window(fs);
355 fs = gtk_file_selection_new ("Ethereal: Capture File");
357 gtk_object_set_data(GTK_OBJECT(fs), E_CAP_FILE_TE_KEY, file_te);
359 /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
360 gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
362 /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
363 gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
365 /* Call a handler when the file selection box is destroyed, so we can inform
366 our caller, if any, that it's been destroyed. */
367 gtk_signal_connect(GTK_OBJECT(fs), "destroy",
368 GTK_SIGNAL_FUNC(cap_prep_fs_destroy_cb), NULL);
370 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
371 "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, fs);
373 /* Connect the cancel_button to destroy the widget */
374 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
375 "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, fs);
377 /* Catch the "key_press_event" signal in the window, so that we can catch
378 the ESC key being pressed and act as if the "Cancel" button had
380 dlg_set_cancel(fs, GTK_FILE_SELECTION(fs)->cancel_button);
386 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data)
388 gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
390 gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
391 gtk_widget_destroy(GTK_WIDGET(data));
395 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data)
397 gtk_widget_destroy(GTK_WIDGET(data));
401 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data)
405 /* Get the widget that requested that we be popped up.
406 (It should arrange to destroy us if it's destroyed, so
407 that we don't get a pointer to a non-existent window here.) */
408 caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
410 /* Tell it we no longer exist. */
411 gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
413 /* Now nuke this window. */
414 gtk_grab_remove(GTK_WIDGET(win));
415 gtk_widget_destroy(GTK_WIDGET(win));
419 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
420 GtkWidget *if_cb, *filter_te, *file_te, *count_cb, *snap_sb, *promisc_cb,
421 *sync_cb, *auto_scroll_cb, *resolv_cb;
427 if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
428 filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
429 file_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
430 count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
431 snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
432 promisc_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_PROMISC_KEY);
433 sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
434 auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
435 resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RESOLVE_KEY);
438 g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
439 if_name = strtok(if_text, " \t");
440 if (if_name == NULL) {
441 simple_dialog(ESD_TYPE_CRIT, NULL,
442 "You didn't specify an interface on which to capture packets.");
448 cfile.iface = g_strdup(if_name);
451 /* XXX - don't try to get clever and set "cfile.filter" to NULL if the
452 filter string is empty, as an indication that we don't have a filter
453 and thus don't have to set a filter when capturing - the version of
454 libpcap in Red Hat Linux 6.1, and versions based on later patches
455 in that series, don't bind the AF_PACKET socket to an interface
456 until a filter is set, which means they aren't bound at all if
457 no filter is set, which means no packets arrive as input on that
458 socket, which means Ethereal never sees any packets. */
459 filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
461 g_free(cfile.cfilter);
462 g_assert(filter_text != NULL);
463 cfile.cfilter = g_strdup(filter_text);
465 save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
466 if (save_file && save_file[0]) {
467 /* User specified a file to which the capture should be written. */
468 save_file = g_strdup(save_file);
470 /* User didn't specify a file; save to a temporary file. */
474 cfile.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
476 cfile.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
478 cfile.snap = WTAP_MAX_PACKET_SIZE;
479 else if (cfile.snap < MIN_PACKET_SIZE)
480 cfile.snap = MIN_PACKET_SIZE;
482 promisc_mode = GTK_TOGGLE_BUTTON (promisc_cb)->active;
484 sync_mode = GTK_TOGGLE_BUTTON (sync_cb)->active;
486 auto_scroll_live = GTK_TOGGLE_BUTTON (auto_scroll_cb)->active;
488 g_resolving_actif = GTK_TOGGLE_BUTTON (resolv_cb)->active;
490 gtk_widget_destroy(GTK_WIDGET(parent_w));
492 do_capture(save_file);
496 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
498 gtk_grab_remove(GTK_WIDGET(parent_w));
499 gtk_widget_destroy(GTK_WIDGET(parent_w));
503 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data)
505 GtkWidget *capture_prep_filter_w;
508 /* Is there a filter edit/selection dialog associated with this
509 Capture Preferences dialog? */
510 capture_prep_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
512 if (capture_prep_filter_w != NULL) {
513 /* Yes. Destroy it. */
514 gtk_widget_destroy(capture_prep_filter_w);
517 /* Is there a file selection dialog associated with this
518 Print File dialog? */
519 fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
522 /* Yes. Destroy it. */
523 gtk_widget_destroy(fs);
526 /* Note that we no longer have a "Capture Preferences" dialog box. */
530 #endif /* HAVE_LIBPCAP */