2 * Routines for packet capture windows
4 * $Id: capture_dlg.c,v 1.26 2000/06/15 08:02:42 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.
44 #ifdef NEED_SNPRINTF_H
50 # include "snprintf.h"
57 #include "capture_dlg.h"
58 #include "filter_prefs.h"
59 #include "simple_dialog.h"
60 #include "dlg_utils.h"
63 /* Capture callback data keys */
64 #define E_CAP_IFACE_KEY "cap_iface"
65 #define E_CAP_FILT_KEY "cap_filter_te"
66 #define E_CAP_FILE_TE_KEY "cap_file_te"
67 #define E_CAP_COUNT_KEY "cap_count"
68 #define E_CAP_SNAP_KEY "cap_snap"
69 #define E_CAP_SYNC_KEY "cap_sync"
70 #define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
71 #define E_CAP_RESOLVE_KEY "cap_resolve"
73 #define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
74 #define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
77 capture_prep_file_cb(GtkWidget *w, gpointer te);
80 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data);
83 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data);
86 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data);
89 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
92 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w);
95 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
98 * Keep a static pointer to the current "Capture Preferences" window, if
99 * any, so that if somebody tries to do "Capture:Start" while there's
100 * already a "Capture Preferences" window up, we just pop up the existing
101 * one, rather than creating a new one.
103 static GtkWidget *cap_open_w;
106 capture_prep_cb(GtkWidget *w, gpointer d)
108 GtkWidget *if_cb, *if_lb,
109 *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
110 *filter_hb, *filter_bt, *filter_te,
111 *file_hb, *file_bt, *file_te,
113 *bbox, *ok_bt, *cancel_bt, *snap_lb,
114 *snap_sb, *sync_cb, *auto_scroll_cb, *resolv_cb;
115 GtkAccelGroup *accel_group;
117 GList *if_list, *count_list = NULL;
118 gchar *count_item1 = "0 (Infinite)", count_item2[16];
120 char err_str[PCAP_ERRBUF_SIZE];
122 if (cap_open_w != NULL) {
123 /* There's already a "Capture Preferences" dialog box; reactivate it. */
124 reactivate_window(cap_open_w);
128 if_list = get_interface_list(&err, err_str);
129 if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
130 simple_dialog(ESD_TYPE_WARN, NULL, "Can't get list of interfaces: %s",
134 cap_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
135 gtk_window_set_title(GTK_WINDOW(cap_open_w), "Ethereal: Capture Preferences");
136 gtk_signal_connect(GTK_OBJECT(cap_open_w), "destroy",
137 GTK_SIGNAL_FUNC(capture_prep_destroy_cb), NULL);
139 /* Accelerator group for the accelerators (or, as they're called in
140 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
141 Ctrl+<key> is an accelerator). */
142 accel_group = gtk_accel_group_new();
143 gtk_window_add_accel_group(GTK_WINDOW(cap_open_w), accel_group);
145 /* Container for each row of widgets */
146 main_vb = gtk_vbox_new(FALSE, 3);
147 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
148 gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
149 gtk_widget_show(main_vb);
152 if_hb = gtk_hbox_new(FALSE, 3);
153 gtk_container_add(GTK_CONTAINER(main_vb), if_hb);
154 gtk_widget_show(if_hb);
156 if_lb = gtk_label_new("Interface:");
157 gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 0);
158 gtk_widget_show(if_lb);
160 if_cb = gtk_combo_new();
162 gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
164 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cf.iface);
166 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
167 gtk_box_pack_start(GTK_BOX(if_hb), if_cb, FALSE, FALSE, 0);
168 gtk_widget_show(if_cb);
170 free_interface_list(if_list);
173 count_hb = gtk_hbox_new(FALSE, 3);
174 gtk_container_add(GTK_CONTAINER(main_vb), count_hb);
175 gtk_widget_show(count_hb);
177 count_lb = gtk_label_new("Count:");
178 gtk_box_pack_start(GTK_BOX(count_hb), count_lb, FALSE, FALSE, 0);
179 gtk_widget_show(count_lb);
181 count_list = g_list_append(count_list, count_item1);
183 snprintf(count_item2, 15, "%d", cf.count);
184 count_list = g_list_append(count_list, count_item2);
187 count_cb = gtk_combo_new();
188 gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
189 gtk_box_pack_start(GTK_BOX(count_hb), count_cb, FALSE, FALSE, 0);
190 gtk_widget_show(count_cb);
193 count_list = g_list_remove_link(count_list, count_list);
196 filter_hb = gtk_hbox_new(FALSE, 3);
197 gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
198 gtk_widget_show(filter_hb);
200 filter_bt = gtk_button_new_with_label("Filter:");
201 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
202 GTK_SIGNAL_FUNC(filter_browse_cb), NULL);
203 gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
204 gtk_widget_show(filter_bt);
206 filter_te = gtk_entry_new();
207 if (cf.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cf.cfilter);
208 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
209 gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
210 gtk_widget_show(filter_te);
213 file_hb = gtk_hbox_new(FALSE, 1);
214 gtk_container_add(GTK_CONTAINER(main_vb), file_hb);
215 gtk_widget_show(file_hb);
217 file_bt = gtk_button_new_with_label("File:");
218 gtk_box_pack_start(GTK_BOX(file_hb), file_bt, FALSE, FALSE, 3);
219 gtk_widget_show(file_bt);
221 file_te = gtk_entry_new();
222 gtk_box_pack_start(GTK_BOX(file_hb), file_te, TRUE, TRUE, 3);
223 gtk_widget_show(file_te);
225 gtk_signal_connect(GTK_OBJECT(file_bt), "clicked",
226 GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_te));
228 /* Misc row: Capture file checkbox and snap spinbutton */
229 caplen_hb = gtk_hbox_new(FALSE, 3);
230 gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
231 gtk_widget_show(caplen_hb);
233 snap_lb = gtk_label_new("Capture length");
234 gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
235 gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
236 gtk_widget_show(snap_lb);
238 adj = (GtkAdjustment *) gtk_adjustment_new((float) cf.snap,
239 MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
240 snap_sb = gtk_spin_button_new (adj, 0, 0);
241 gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
242 gtk_widget_set_usize (snap_sb, 80, 0);
243 gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3);
244 gtk_widget_show(snap_sb);
246 sync_cb = dlg_check_button_new_with_label_with_mnemonic(
247 "_Update list of packets in real time", accel_group);
248 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sync_cb), sync_mode);
249 gtk_container_add(GTK_CONTAINER(main_vb), sync_cb);
250 gtk_widget_show(sync_cb);
252 auto_scroll_cb = dlg_check_button_new_with_label_with_mnemonic(
253 "_Automatic scrolling in live capture", accel_group);
254 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(auto_scroll_cb), auto_scroll_live);
255 gtk_container_add(GTK_CONTAINER(main_vb), auto_scroll_cb);
256 gtk_widget_show(auto_scroll_cb);
258 resolv_cb = dlg_check_button_new_with_label_with_mnemonic(
259 "Enable _name resolution", accel_group);
260 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(resolv_cb), g_resolving_actif);
261 gtk_container_add(GTK_CONTAINER(main_vb), resolv_cb);
262 gtk_widget_show(resolv_cb);
264 /* Button row: OK and cancel buttons */
265 bbox = gtk_hbutton_box_new();
266 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
267 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
268 gtk_container_add(GTK_CONTAINER(main_vb), bbox);
269 gtk_widget_show(bbox);
271 ok_bt = gtk_button_new_with_label ("OK");
272 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
273 GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
274 GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
275 gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
276 gtk_widget_grab_default(ok_bt);
277 gtk_widget_show(ok_bt);
279 cancel_bt = gtk_button_new_with_label ("Cancel");
280 gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
281 GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
282 GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
283 gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
284 gtk_widget_show(cancel_bt);
286 /* Attach pointers to needed widgets to the capture prefs window/object */
287 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
288 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY, filter_te);
289 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY, file_te);
290 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
291 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY, snap_sb);
292 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SYNC_KEY, sync_cb);
293 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
294 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_RESOLVE_KEY, resolv_cb);
296 /* Catch the "activate" signal on the frame number and file name text
297 entries, so that if the user types Return there, we act as if the
298 "OK" button had been selected, as happens if Return is typed if some
299 widget that *doesn't* handle the Return key has the input focus. */
300 dlg_set_activate(filter_te, ok_bt);
301 dlg_set_activate(file_te, ok_bt);
303 /* Catch the "key_press_event" signal in the window, so that we can catch
304 the ESC key being pressed and act as if the "Cancel" button had
306 dlg_set_cancel(cap_open_w, cancel_bt);
308 /* XXX - why does not
310 gtk_widget_grab_focus(if_cb);
312 give the initial focus to the "Interface" combo box?
314 Or should I phrase that as "why does GTK+ continually frustrate
315 attempts to make GUIs driveable from the keyboard?" We have to
316 go catch the activate signal on every single GtkEntry widget
317 (rather than having widgets whose activate signal is *not*
318 caught not catch the Return keystroke, so that it passes on,
319 ultimately, to the window, which can activate the default
320 widget, i.e. the "OK" button); we have to catch the "key_press_event"
321 signal and have the handler check for ESC, so that we can have ESC
322 activate the "Cancel" button; in order to support Alt+<key> mnemonics
323 for buttons and the like, we may have to construct an accelerator
324 group by hand and set up the accelerators by hand (if that even
325 works - I've not tried it yet); we have to do a "gtk_widget_grab_focus()"
326 to keep some container widget from getting the initial focus, so that
327 you don't have to tab into the first widget in order to start typing
328 in it; and it now appears that you simply *can't* make a combo box
329 get the initial focus, at least not in the obvious fashion. Sigh.... */
331 gtk_widget_show(cap_open_w);
335 capture_prep_file_cb(GtkWidget *w, gpointer file_te)
337 GtkWidget *caller = gtk_widget_get_toplevel(w);
340 /* Has a file selection dialog box already been opened for that top-level
342 fs = gtk_object_get_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
345 /* Yes. Just re-activate that dialog box. */
346 reactivate_window(fs);
350 fs = gtk_file_selection_new ("Ethereal: Capture File");
352 gtk_object_set_data(GTK_OBJECT(fs), E_CAP_FILE_TE_KEY, file_te);
354 /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
355 gtk_object_set_data(GTK_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
357 /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
358 gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
360 /* Call a handler when the file selection box is destroyed, so we can inform
361 our caller, if any, that it's been destroyed. */
362 gtk_signal_connect(GTK_OBJECT(fs), "destroy",
363 GTK_SIGNAL_FUNC(cap_prep_fs_destroy_cb), NULL);
365 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
366 "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, fs);
368 /* Connect the cancel_button to destroy the widget */
369 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
370 "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, fs);
372 /* Catch the "key_press_event" signal in the window, so that we can catch
373 the ESC key being pressed and act as if the "Cancel" button had
375 dlg_set_cancel(fs, GTK_FILE_SELECTION(fs)->cancel_button);
381 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data)
383 gtk_entry_set_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(data),
385 gtk_file_selection_get_filename (GTK_FILE_SELECTION(data)));
386 gtk_widget_destroy(GTK_WIDGET(data));
390 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data)
392 gtk_widget_destroy(GTK_WIDGET(data));
396 cap_prep_fs_destroy_cb(GtkWidget *win, gpointer data)
400 /* Get the widget that requested that we be popped up.
401 (It should arrange to destroy us if it's destroyed, so
402 that we don't get a pointer to a non-existent window here.) */
403 caller = gtk_object_get_data(GTK_OBJECT(win), E_FS_CALLER_PTR_KEY);
405 /* Tell it we no longer exist. */
406 gtk_object_set_data(GTK_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
408 /* Now nuke this window. */
409 gtk_grab_remove(GTK_WIDGET(win));
410 gtk_widget_destroy(GTK_WIDGET(win));
414 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
415 GtkWidget *if_cb, *filter_te, *file_te, *count_cb, *snap_sb, *sync_cb,
416 *auto_scroll_cb, *resolv_cb;
422 if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
423 filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
424 file_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
425 count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
426 snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
427 sync_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SYNC_KEY);
428 auto_scroll_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
429 resolv_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_RESOLVE_KEY);
432 g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
433 if_name = strtok(if_text, " \t");
434 if (if_name == NULL) {
435 simple_dialog(ESD_TYPE_WARN, NULL,
436 "You didn't specify an interface on which to capture packets.");
442 cf.iface = g_strdup(if_name);
445 /* XXX - don't try to get clever and set "cf.filter" to NULL if the
446 filter string is empty, as an indication that we don't have a filter
447 and thus don't have to set a filter when capturing - the version of
448 libpcap in Red Hat Linux 6.1, and versions based on later patches
449 in that series, don't bind the AF_PACKET socket to an interface
450 until a filter is set, which means they aren't bound at all if
451 no filter is set, which means no packets arrive as input on that
452 socket, which means Ethereal never sees any packets. */
453 filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
456 g_assert(filter_text != NULL);
457 cf.cfilter = g_strdup(filter_text);
459 save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
460 if (save_file && save_file[0]) {
461 /* User specified a file to which the capture should be written. */
462 save_file = g_strdup(save_file);
464 /* User didn't specify a file; save to a temporary file. */
468 cf.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
470 cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
472 cf.snap = WTAP_MAX_PACKET_SIZE;
473 else if (cf.snap < MIN_PACKET_SIZE)
474 cf.snap = MIN_PACKET_SIZE;
476 sync_mode = GTK_TOGGLE_BUTTON (sync_cb)->active;
478 auto_scroll_live = GTK_TOGGLE_BUTTON (auto_scroll_cb)->active;
480 g_resolving_actif = GTK_TOGGLE_BUTTON (resolv_cb)->active;
482 gtk_widget_destroy(GTK_WIDGET(parent_w));
484 do_capture(save_file);
488 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
490 gtk_grab_remove(GTK_WIDGET(parent_w));
491 gtk_widget_destroy(GTK_WIDGET(parent_w));
495 capture_prep_destroy_cb(GtkWidget *win, gpointer user_data)
497 GtkWidget *capture_prep_filter_w;
500 /* Is there a filter edit/selection dialog associated with this
501 Capture Preferences dialog? */
502 capture_prep_filter_w = gtk_object_get_data(GTK_OBJECT(win), E_FILT_DIALOG_PTR_KEY);
504 if (capture_prep_filter_w != NULL) {
505 /* Yes. Destroy it. */
506 gtk_widget_destroy(capture_prep_filter_w);
509 /* Is there a file selection dialog associated with this
510 Print File dialog? */
511 fs = gtk_object_get_data(GTK_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
514 /* Yes. Destroy it. */
515 gtk_widget_destroy(fs);
518 /* Note that we no longer have a "Capture Preferences" dialog box. */
522 #endif /* HAVE_LIBPCAP */