Replace "svn" with "git" all over the place.
[metze/wireshark/wip.git] / ui / gtk / capture_if_dlg.c
1 /* capture_if_dlg.c
2  * Routines for the capture interface dialog
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <gtk/gtk.h>
28
29 #ifdef HAVE_LIBPCAP
30
31 #include <string.h>
32
33 #include <epan/prefs.h>
34 #include <epan/to_str.h>
35
36 #include "../capture_opts.h"
37 #include "../capture_session.h"
38 #include "../capture_ifinfo.h"
39 #include "../capture.h"
40 #include "../capture-pcap-util.h"
41 #include "../capture_ui_utils.h"
42 #include "wsutil/file_util.h"
43 #include <wiretap/wtap.h>
44
45 #include "ui/capture_globals.h"
46 #include "ui/recent.h"
47 #include "ui/simple_dialog.h"
48
49 #ifdef _WIN32
50 #include "ui/gtk/capture_if_details_dlg_win32.h"
51 #include "../../capture-wpcap.h"
52 #endif
53
54 #include "ui/gtk/stock_icons.h"
55 #include "ui/gtk/capture_dlg.h"
56 #include "ui/gtk/capture_if_dlg.h"
57 #include "ui/gtk/gui_utils.h"
58 #include "ui/gtk/dlg_utils.h"
59 #include "ui/gtk/main.h"
60 #include "ui/gtk/main_toolbar.h"
61 #include "ui/gtk/help_dlg.h"
62 #include "ui/gtk/keys.h"
63 #include "ui/gtk/webbrowser.h"
64 #include "ui/gtk/network_icons.h"
65 #include "ui/gtk/pipe_icon.h"
66 #include "ui/gtk/main_welcome.h"
67
68 #include "ui/gtk/old-gtk-compat.h"
69
70 #ifdef HAVE_AIRPCAP
71 #include "../../image/toolbar/capture_airpcap_16.xpm"
72 #endif
73
74 #if defined(HAVE_PCAP_REMOTE)
75 #include "ui/gtk/remote_icons.h"
76 #endif
77
78 #include "../../image/toolbar/modem_16.xpm"
79
80 #include "../../image/toolbar/network_virtual_16.xpm"
81
82 /* new buttons to be used instead of labels for 'Capture','Prepare',' */
83 /*#include "../../image/toolbar/capture_capture_16.xpm"*/
84 /*#include "../../image/toolbar/capture_prepare_16.xpm"*/
85 /*#include "../../image/toolbar/capture_details_16.xpm"*/
86
87
88 #ifdef HAVE_AIRPCAP
89 #include "airpcap.h"
90 #include "airpcap_loader.h"
91 #include "airpcap_gui_utils.h"
92 #include "airpcap_dlg.h"
93 #endif
94
95 #define CAPTURE_IF_IP_ADDR_LABEL      "capture_if_ip_addr_label"
96 #define CAPTURE_IF_SELECTED_IP_ADDR   "capture_if_selected_ip_addr"
97 #define SOFT_LABEL_LEN 50
98
99 /*
100  * Keep a static pointer to the current "Capture Interfaces" window, if
101  * any, so that if somebody tries to do "Capture:Start" while there's
102  * already a "Capture Interfaces" window up, we just pop up the existing
103  * one, rather than creating a new one.
104  */
105 static GtkWidget       *cap_if_w;
106
107 static guint            timer_id;
108
109 static GtkWidget       *close_bt, *stop_bt, *capture_bt, *options_bt;
110
111 static GArray          *if_array;
112
113 static if_stat_cache_t *sc;
114 static GtkWidget       *cap_if_top_vb, *cap_if_sw;
115
116 /*
117  * Timeout, in milliseconds, for reads from the stream of captured packets.
118  */
119 #define CAP_READ_TIMEOUT 250
120
121
122 /* the "runtime" data of one interface */
123 typedef struct if_dlg_data_s {
124     gchar     *device;
125     GtkWidget *device_lb;
126     GtkWidget *descr_lb;
127     GtkWidget *ip_lb;
128     GtkWidget *curr_lb;
129     GtkWidget *last_lb;
130     GtkWidget *choose_bt;
131 #ifdef _WIN32
132     GtkWidget *details_bt;
133 #endif
134     gboolean   hidden;
135 } if_dlg_data_t;
136
137 static gboolean gbl_capture_in_progress = FALSE;
138
139 #if 0
140 void
141 add_interface(void)
142 {
143   if_dlg_data_t data;
144
145   data.device_lb  = NULL;
146   data.descr_lb   = NULL;
147   data.ip_lb      = NULL;
148   data.curr_lb    = NULL;
149   data.last_lb    = NULL;
150   data.choose_bt  = NULL;
151 #ifdef _WIN32
152   data.details_bt = NULL;
153 #endif
154   data.hidden     = FALSE;
155   g_array_append_val(if_array, data);
156   refresh_if_window();
157 }
158 #endif
159
160 void
161 update_selected_interface(gchar *name)
162 {
163   guint         i;
164   interface_t   device;
165   if_dlg_data_t data;
166
167   for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
168     device = g_array_index(global_capture_opts.all_ifaces, interface_t, i);
169     data = g_array_index(if_array, if_dlg_data_t, i);
170     if (strcmp(name, device.name) == 0) {
171       gtk_toggle_button_set_active((GtkToggleButton *)data.choose_bt, device.selected);
172       break;
173     }
174   }
175 }
176
177 static void
178 store_selected(GtkWidget *choose_bt, gpointer name)
179 {
180   interface_t device;
181   guint       i;
182
183   for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
184     device = g_array_index(global_capture_opts.all_ifaces, interface_t, i);
185     if (strcmp((char *)name, device.if_info.name) == 0) {
186       if (!device.locked) {
187         device.selected ^= 1;
188         if (device.selected) {
189           global_capture_opts.num_selected++;
190         } else {
191           global_capture_opts.num_selected--;
192         }
193         global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i);
194         g_array_insert_val(global_capture_opts.all_ifaces, i, device);
195         if (gtk_widget_is_focus(choose_bt) && get_welcome_window()) {
196           change_interface_selection(device.name, device.selected);
197         }
198         if (gtk_widget_is_focus(choose_bt) && capture_dlg_window_present()) {
199           enable_selected_interface(device.name, device.selected);
200         }
201         device.locked = FALSE;
202         global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i);
203         g_array_insert_val(global_capture_opts.all_ifaces, i, device);
204       }
205       break;
206     }
207   }
208   if (cap_if_w) {
209     gtk_widget_set_sensitive(capture_bt, !gbl_capture_in_progress && (global_capture_opts.num_selected > 0));
210   }
211 }
212
213 /* start capture button was pressed */
214 static void
215 capture_do_cb(GtkWidget *capture_bt_arg _U_, gpointer if_data _U_)
216 {
217   if_dlg_data_t data;
218   guint         ifs;
219
220   for (ifs = 0; ifs < if_array->len; ifs++) {
221     data = g_array_index(if_array, if_dlg_data_t, ifs);
222     if (data.hidden) {
223       continue;
224     }
225     gtk_widget_set_sensitive(data.choose_bt, FALSE);
226     if_array = g_array_remove_index(if_array, ifs);
227     g_array_insert_val(if_array, ifs, data);
228 #ifdef HAVE_AIRPCAP
229     airpcap_if_active = get_airpcap_if_from_name(airpcap_if_list, gtk_label_get_text(GTK_LABEL(data.device_lb)));
230     airpcap_if_selected = airpcap_if_active;
231 #endif
232   }
233
234   /* XXX - remove this? */
235   if (global_capture_opts.save_file) {
236     g_free(global_capture_opts.save_file);
237     global_capture_opts.save_file = NULL;
238   }
239
240   if (global_capture_opts.ifaces->len > 1) {
241     global_capture_opts.use_pcapng = TRUE;
242   }
243   /* stop capturing from all interfaces, we are going to do real work now ... */
244   window_destroy(cap_if_w);
245
246   capture_start_cb(NULL, NULL);
247 }
248
249
250 /* prepare capture button was pressed */
251 static void
252 capture_prepare_cb(GtkWidget *prepare_bt _U_, gpointer if_data _U_)
253 {
254   /* stop capturing from all interfaces, we are going to do real work now ... */
255   window_destroy(cap_if_w);
256   if (global_capture_opts.ifaces->len > 1) {
257     global_capture_opts.use_pcapng = TRUE;
258   }
259   capture_prep_cb(NULL, NULL);
260 }
261
262
263 #ifdef _WIN32
264 /* capture details button was pressed */
265 static void
266 capture_details_cb(GtkWidget *details_bt _U_, gpointer name)
267 {
268   capture_if_details_open(name);
269 }
270 #endif
271
272 /* update a single interface */
273 static void
274 update_if(gchar *name, if_stat_cache_t *sc_p)
275 {
276   struct pcap_stat  stats;
277   gchar            *str;
278   guint             diff, ifs, data_ifs;
279   interface_t       device;
280   if_dlg_data_t     data;
281   gboolean          found = FALSE;
282
283
284   /*
285    * Note that some versions of libpcap, on some versions of UN*X,
286    * pcap_stats() returns the number of packets since the last
287    * pcap_stats call.
288    *
289    * That's a bug, and should be fixed; "pcap_stats()" is supposed
290    * to work the same way on all platforms.
291    */
292   device.last_packets = 0;
293   data.curr_lb = NULL;
294   data.last_lb = NULL;
295   if (sc_p) {
296     for (ifs = 0, data_ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
297       device = g_array_index(global_capture_opts.all_ifaces, interface_t, ifs);
298       if (device.type != IF_PIPE) {
299         data = g_array_index(if_array, if_dlg_data_t, data_ifs++);
300         if (!device.hidden && strcmp(name, device.name) == 0) {
301           found = TRUE;
302           break;
303         }
304       }
305     }
306     if (found) {
307       if (capture_stats(sc_p, name, &stats)) {
308         if ((int)(stats.ps_recv - device.last_packets) < 0) {
309           diff = 0;
310         } else {
311           diff = stats.ps_recv - device.last_packets;
312         }
313         device.last_packets = stats.ps_recv;
314
315         str = g_strdup_printf("%u", device.last_packets);
316         gtk_label_set_text(GTK_LABEL(data.curr_lb), str);
317         g_free(str);
318         str = g_strdup_printf("%u", diff);
319         gtk_label_set_text(GTK_LABEL(data.last_lb), str);
320         g_free(str);
321
322         gtk_widget_set_sensitive(data.curr_lb, diff);
323         gtk_widget_set_sensitive(data.last_lb, diff);
324       } else {
325         gtk_label_set_text(GTK_LABEL(data.curr_lb), "error");
326         gtk_label_set_text(GTK_LABEL(data.last_lb), "error");
327       }
328       global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, ifs);
329       g_array_insert_val(global_capture_opts.all_ifaces, ifs, device);
330       if_array = g_array_remove_index(if_array, ifs);
331       g_array_insert_val(if_array, ifs, data);
332     }
333   }
334 }
335
336 /* update all interfaces */
337 static gboolean
338 update_all(gpointer data)
339 {
340   interface_t      device;
341   guint            ifs;
342   if_stat_cache_t *sc_p = (if_stat_cache_t *)data;
343
344   if (!cap_if_w) {
345     return FALSE;
346   }
347
348   for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
349     device = g_array_index(global_capture_opts.all_ifaces, interface_t, ifs);
350     update_if(device.name, sc_p);
351   }
352
353   return TRUE;
354 }
355
356 /* a live capture has started or stopped */
357 void
358 set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress)
359 {
360   gbl_capture_in_progress = capture_in_progress;
361   if (cap_if_w) {
362     gtk_widget_set_sensitive(stop_bt, capture_in_progress);
363     gtk_widget_set_sensitive(capture_bt, !capture_in_progress && (global_capture_opts.num_selected > 0));
364   }
365 }
366
367 /* a live capture is being stopped */
368 void
369 set_capture_if_dialog_for_capture_stopping(void)
370 {
371   if (cap_if_w) {
372     gtk_widget_set_sensitive(stop_bt, FALSE);
373   }
374 }
375
376
377 /* the window was closed, cleanup things */
378 static void
379 capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
380 {
381   g_source_remove(timer_id);
382
383   if (sc) {
384     capture_stat_stop(sc);
385     sc = NULL;
386   }
387   window_destroy(GTK_WIDGET(cap_if_w));
388   /* Note that we no longer have a "Capture Options" dialog box. */
389   cap_if_w      = NULL;
390   cap_if_top_vb = NULL;
391   cap_if_sw     = NULL;
392 #ifdef HAVE_AIRPCAP
393   if (airpcap_if_active)
394     airpcap_set_toolbar_stop_capture(airpcap_if_active);
395 #endif
396 }
397
398
399 /*
400  * Used to retrieve the interface icon.
401  */
402 GtkWidget * capture_get_if_icon(interface_t *device)
403 {
404 #ifdef HAVE_PCAP_REMOTE
405   if (!device->local) {
406     return pixbuf_to_widget(remote_sat_pb_data);
407   }
408 #endif
409   if (device->display_name && strstr(device->display_name,"Wi-Fi") != NULL) {
410     return pixbuf_to_widget(network_wireless_pb_data);
411   }
412   switch (device->type) {
413   case IF_DIALUP:
414     return xpm_to_widget(modem_16_xpm);
415   case IF_WIRELESS:
416     return pixbuf_to_widget(network_wireless_pb_data);
417 #ifdef HAVE_AIRPCAP
418   case IF_AIRPCAP:
419     return xpm_to_widget(capture_airpcap_16_xpm);
420 #endif
421   case IF_BLUETOOTH:
422     return pixbuf_to_widget(network_bluetooth_pb_data);
423   case IF_USB:
424     return pixbuf_to_widget(network_usb_pb_data);
425   case IF_VIRTUAL:
426     return xpm_to_widget(network_virtual_16_xpm);
427   case IF_WIRED:
428     return pixbuf_to_widget(network_wired_pb_data);
429   case IF_PIPE:
430   case IF_STDIN:
431     return pixbuf_to_widget(pipe_pb_data);
432   default:
433     printf("unknown device type\n");
434   }
435   return pixbuf_to_widget(network_wired_pb_data);
436 }
437
438
439 static int
440 get_ip_addr_count(GSList *addr_list)
441 {
442   GSList    *curr_addr;
443   if_addr_t *addr;
444   int        count;
445
446   count = 0;
447   for (curr_addr = addr_list; curr_addr != NULL;
448        curr_addr = g_slist_next(curr_addr)) {
449     addr = (if_addr_t *)curr_addr->data;
450     switch (addr->ifat_type) {
451
452     case IF_AT_IPv4:
453     case IF_AT_IPv6:
454       count++;
455       break;
456
457     default:
458       /* In case we add non-IP addresses */
459       break;
460     }
461   }
462   return count;
463 }
464
465 static const gchar *
466 set_ip_addr_label(GSList *addr_list, GtkWidget *ip_lb, guint selected_ip_addr)
467 {
468   GSList      *curr_addr;
469   if_addr_t   *addr;
470   const gchar *addr_str = NULL;
471
472   curr_addr = g_slist_nth(addr_list, selected_ip_addr);
473   if (curr_addr) {
474     addr = (if_addr_t *)curr_addr->data;
475     switch (addr->ifat_type) {
476
477     case IF_AT_IPv4:
478       addr_str = ip_to_str((guint8 *)&addr->addr.ip4_addr);
479       break;
480
481     case IF_AT_IPv6:
482       addr_str = ip6_to_str((struct e_in6_addr *)&addr->addr.ip6_addr);
483       break;
484
485     default:
486       /* Ignore non-IP addresses, in case we ever support them */
487       break;
488     }
489   }
490
491   if (addr_str) {
492     gtk_label_set_text(GTK_LABEL(ip_lb), addr_str);
493   } else {
494     gtk_label_set_text(GTK_LABEL(ip_lb), "none");
495   }
496   g_object_set_data(G_OBJECT(ip_lb), CAPTURE_IF_SELECTED_IP_ADDR, GINT_TO_POINTER(selected_ip_addr));
497
498   return addr_str;
499 }
500
501
502 static gboolean
503 ip_label_enter_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
504 {
505     gtk_widget_set_state(eb, GTK_STATE_PRELIGHT);
506
507     return FALSE;
508 }
509
510
511 static gboolean
512 ip_label_leave_cb(GtkWidget *eb, GdkEvent *event _U_, gpointer user_data _U_)
513 {
514     gtk_widget_set_state(eb, GTK_STATE_NORMAL);
515
516     return FALSE;
517 }
518
519
520 static gboolean
521 ip_label_press_cb(GtkWidget *widget, GdkEvent *event _U_, gpointer data)
522 {
523   GtkWidget *ip_lb            = (GtkWidget *)g_object_get_data(G_OBJECT(widget), CAPTURE_IF_IP_ADDR_LABEL);
524   GSList    *addr_list        = (GSList *)data;
525   GSList    *curr_addr, *start_addr;
526   if_addr_t *addr;
527   guint      selected_ip_addr = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(ip_lb), CAPTURE_IF_SELECTED_IP_ADDR));
528
529   /* Select next IP address */
530   start_addr = g_slist_nth(addr_list, selected_ip_addr);
531   for (;;) {
532     selected_ip_addr++;
533     if (g_slist_length(addr_list) <= selected_ip_addr) {
534       /* Wrap around */
535       selected_ip_addr = 0;
536     }
537     curr_addr = g_slist_nth(addr_list, selected_ip_addr);
538     if (curr_addr == start_addr) {
539       /* We wrapped all the way around */
540       break;
541     }
542
543     addr = (if_addr_t *)curr_addr->data;
544     switch (addr->ifat_type) {
545
546     case IF_AT_IPv4:
547     case IF_AT_IPv6:
548       goto found;
549
550     default:
551       /* In case we add non-IP addresses */
552       break;
553     }
554   }
555
556 found:
557   set_ip_addr_label(addr_list, ip_lb, selected_ip_addr);
558
559   return FALSE;
560 }
561
562 static void
563 capture_if_stop_cb(GtkWidget *w _U_, gpointer d _U_)
564 {
565   guint         ifs;
566   if_dlg_data_t data;
567
568   for (ifs = 0; ifs < if_array->len; ifs++) {
569     data = g_array_index(if_array, if_dlg_data_t, ifs);
570     if (data.hidden) {
571       continue;
572     }
573     gtk_widget_set_sensitive(data.choose_bt, TRUE);
574     if_array = g_array_remove_index(if_array, ifs);
575     g_array_insert_val(if_array, ifs, data);
576   }
577   capture_stop_cb(NULL, NULL);
578 }
579
580 static void
581 make_if_array(void)
582 {
583   if_dlg_data_t data;
584   guint         i;
585
586   if_array = g_array_new(FALSE, FALSE, sizeof(if_dlg_data_t));
587
588   for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
589      data.device_lb  = NULL;
590      data.descr_lb   = NULL;
591      data.ip_lb      = NULL;
592      data.curr_lb    = NULL;
593      data.last_lb    = NULL;
594      data.choose_bt  = NULL;
595 #ifdef _WIN32
596      data.details_bt = NULL;
597 #endif
598      data.hidden     = FALSE;
599      g_array_append_val(if_array, data);
600   }
601 }
602
603 /*
604  * If this is Windows, is WinPcap loaded?  If not, pop up a dialog noting
605  * that fact and return FALSE, as we can't capture traffic.
606  *
607  * Otherwise (not Windows or WinPcap loaded), are there any interfaces?
608  * If not, pop up a dialog noting that fact and return FALSE, as there
609  * are no interfaces on which to capture traffic.
610  *
611  * Otherwise, return TRUE, as we can capture.
612  */
613 static gboolean
614 can_capture(void)
615 {
616 #ifdef _WIN32
617   /* Is WPcap loaded? */
618   if (!has_wpcap) {
619     char *detailed_err;
620
621     detailed_err = cant_load_winpcap_err("Wireshark");
622     simple_error_message_box("%s", detailed_err);
623     g_free(detailed_err);
624     return FALSE;
625   }
626 #endif
627
628   if (global_capture_opts.all_ifaces->len == 0) {
629     simple_error_message_box("There are no interfaces on which a capture can be done.");
630     return FALSE;
631   }
632
633   return TRUE;
634 }
635
636 static void
637 capture_if_refresh_if_list(void)
638 {
639   GtkWidget      *if_vb, *if_grid, *icon, *if_lb, *eb;
640   GtkRequisition  requisition;
641   int             row = 0, height = 0;
642   guint           ifs;
643   interface_t     device;
644   const gchar    *addr_str;
645   gchar          *user_descr;
646   if_dlg_data_t   data;
647
648   if (!can_capture()) {
649     /* No interfaces or, on Windows, no WinPcap; we've already popped
650        up a message, so just get rid of the interface dialog. */
651     destroy_if_window();
652     return;
653   }
654
655   if (cap_if_sw) {
656     /* First, get rid of the old interface list, and stop updating
657        the statistics on it. */
658     gtk_container_remove(GTK_CONTAINER(cap_if_top_vb), cap_if_sw);
659     capture_stat_stop(sc);
660     g_source_remove(timer_id);
661   }
662
663   /* Now construct the new interface list, and start updating the
664      statistics on it. */
665
666   make_if_array();
667
668   cap_if_sw = gtk_scrolled_window_new(NULL, NULL);
669   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cap_if_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
670   gtk_box_pack_start(GTK_BOX(cap_if_top_vb), cap_if_sw, TRUE, TRUE, 0);
671
672   if_vb = gtk_alignment_new(0.0F, 0.0F, 1.0F, 0.0F);
673   gtk_container_set_border_width(GTK_CONTAINER(if_vb), 5);
674 #if ! GTK_CHECK_VERSION(3,8,0)
675   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(cap_if_sw), if_vb);
676 #else
677   gtk_container_add(GTK_CONTAINER(cap_if_sw), if_vb);
678 #endif
679
680   if_grid = ws_gtk_grid_new();
681   ws_gtk_grid_set_row_spacing(GTK_GRID(if_grid), 3);
682   ws_gtk_grid_set_column_spacing(GTK_GRID(if_grid), 3);
683   gtk_container_add(GTK_CONTAINER(if_vb), if_grid);
684
685   /* This is the icon column, used to display which kind of interface we have */
686   if_lb = gtk_label_new("");
687   ws_gtk_grid_attach_extended(GTK_GRID(if_grid), if_lb, 0, row, 1, 1,
688       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
689
690   if_lb = gtk_label_new("Device");
691   ws_gtk_grid_attach_defaults(GTK_GRID(if_grid), if_lb, 1, row, 3, 1);
692
693   if_lb = gtk_label_new("Description");
694   ws_gtk_grid_attach_defaults(GTK_GRID(if_grid), if_lb, 4, row, 1, 1);
695
696   if_lb = gtk_label_new(" IP ");
697   ws_gtk_grid_attach_extended(GTK_GRID(if_grid), if_lb, 5, row, 1, 1,
698       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
699
700   if_lb = gtk_label_new("Packets");
701   ws_gtk_grid_attach_extended(GTK_GRID(if_grid), if_lb, 6, row, 1, 1,
702       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
703
704   if_lb = gtk_label_new(" Packets/s ");
705   ws_gtk_grid_attach_extended(GTK_GRID(if_grid), if_lb, 7, row, 1, 1,
706       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
707   row++;
708
709   height += 30;
710   /* Start gathering statistics (using dumpcap) */
711   sc = capture_stat_start(&global_capture_opts);
712
713   /* List the interfaces */
714   for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
715     device = g_array_index(global_capture_opts.all_ifaces, interface_t, ifs);
716     data = g_array_index(if_array, if_dlg_data_t, ifs);
717     /* Continue if capture device is hidden */
718     if (device.hidden) {
719       data.hidden = TRUE;
720       if_array = g_array_remove_index(if_array, ifs);
721       g_array_insert_val(if_array, ifs, data);
722       continue;
723     }
724     data.choose_bt = gtk_check_button_new();
725     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), data.choose_bt, 0, row, 1, 1,
726       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
727     if (gbl_capture_in_progress) {
728       gtk_widget_set_sensitive(data.choose_bt, FALSE);
729     } else {
730       gtk_widget_set_sensitive(data.choose_bt, TRUE);
731     }
732     gtk_toggle_button_set_active((GtkToggleButton *)data.choose_bt, device.selected);
733     g_signal_connect(data.choose_bt, "toggled", G_CALLBACK(store_selected), device.name);
734     /* Kind of adaptor (icon) */
735     icon = capture_get_if_icon(&(device));
736     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), icon, 1, row, 1, 1,
737       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
738
739      /* device name */
740 #ifdef _WIN32
741   /*
742    * On Windows, device names are generally not meaningful - NT 5
743    * uses long blobs with GUIDs in them, for example - so instead we use
744    * the display name which should match that shown in ncpa.cpl
745    */
746     data.device_lb = gtk_label_new(device.display_name);
747 #else
748     data.device_lb = gtk_label_new(device.name);
749 #endif
750     gtk_misc_set_alignment(GTK_MISC(data.device_lb), 0.0f, 0.5f);
751     if (strlen(gtk_label_get_text(GTK_LABEL(data.device_lb))) > SOFT_LABEL_LEN) {
752         gtk_label_set_ellipsize(GTK_LABEL(data.device_lb), PANGO_ELLIPSIZE_MIDDLE);
753         gtk_label_set_width_chars(GTK_LABEL(data.device_lb), SOFT_LABEL_LEN);
754     }
755     ws_gtk_grid_attach_defaults(GTK_GRID(if_grid), data.device_lb, 2, row, 2, 1);
756
757     /* description */
758     user_descr = capture_dev_user_descr_find(device.name);
759     if (user_descr) {
760       data.descr_lb = gtk_label_new(user_descr);
761       g_free (user_descr);
762     } else {
763       if (device.if_info.vendor_description)
764         data.descr_lb = gtk_label_new(device.if_info.vendor_description);
765       else
766         data.descr_lb = gtk_label_new("");
767     }
768     gtk_misc_set_alignment(GTK_MISC(data.descr_lb), 0.0f, 0.5f);
769     if (strlen(gtk_label_get_text(GTK_LABEL(data.descr_lb))) > SOFT_LABEL_LEN) {
770         gtk_label_set_ellipsize(GTK_LABEL(data.descr_lb), PANGO_ELLIPSIZE_MIDDLE);
771         gtk_label_set_width_chars(GTK_LABEL(data.descr_lb), SOFT_LABEL_LEN);
772     }
773     ws_gtk_grid_attach_defaults(GTK_GRID(if_grid), data.descr_lb, 4, row, 1, 1);
774
775     /* IP address */
776     /* Only one IP address will be shown, start with the first */
777     data.ip_lb = gtk_label_new("");
778     addr_str = set_ip_addr_label (device.if_info.addrs, data.ip_lb, 0);
779     if (addr_str) {
780       gtk_widget_set_sensitive(data.ip_lb, TRUE);
781     } else {
782       gtk_widget_set_sensitive(data.ip_lb, FALSE);
783     }
784     eb = gtk_event_box_new ();
785     gtk_container_add(GTK_CONTAINER(eb), data.ip_lb);
786     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), eb, 5, row, 1, 1,
787       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
788     if (get_ip_addr_count(device.if_info.addrs) > 1) {
789       /* More than one IP address, make it possible to toggle */
790       g_object_set_data(G_OBJECT(eb), CAPTURE_IF_IP_ADDR_LABEL, data.ip_lb);
791       gtk_widget_set_tooltip_text(eb, "Click to see additional IP addresses");
792       g_signal_connect(eb, "enter-notify-event", G_CALLBACK(ip_label_enter_cb), NULL);
793       g_signal_connect(eb, "leave-notify-event", G_CALLBACK(ip_label_leave_cb), NULL);
794       g_signal_connect(eb, "button-press-event", G_CALLBACK(ip_label_press_cb), device.if_info.addrs);
795     }
796
797     /* packets */
798     data.curr_lb = gtk_label_new("-");
799     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), data.curr_lb, 6, row, 1, 1,
800       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
801
802     /* packets/s */
803     data.last_lb = gtk_label_new("-");
804     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), data.last_lb, 7, row, 1, 1,
805       (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0);
806
807     /* details button */
808 #ifdef _WIN32
809     data.details_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_CAPTURE_DETAILS);
810     gtk_widget_set_tooltip_text(data.details_bt, "Open the capture details dialog of this interface.");
811     ws_gtk_grid_attach_extended(GTK_GRID(if_grid), data.details_bt, 8, row, 1, 1, 0, 0, 0, 0);
812     if (capture_if_has_details(device.name)) {
813       g_signal_connect(data.details_bt, "clicked", G_CALLBACK(capture_details_cb), device.name);
814     } else {
815       gtk_widget_set_sensitive(data.details_bt, FALSE);
816     }
817 #endif
818     if_array = g_array_remove_index(if_array, ifs);
819     g_array_insert_val(if_array, ifs, data);
820
821     row++;
822     if (row <= 20) {
823       /* Add up to 20 rows of interfaces, otherwise the window may become too high */
824 #ifdef _WIN32
825       gtk_widget_get_preferred_size(GTK_WIDGET(data.details_bt), &requisition, NULL);
826 #else
827       gtk_widget_get_preferred_size(GTK_WIDGET(data.choose_bt), &requisition, NULL);
828 #endif
829       height += requisition.height;
830     }
831   }
832
833   gtk_widget_get_preferred_size(GTK_WIDGET(close_bt), &requisition, NULL);
834   /* height + static offset + grid spacing */
835   height += requisition.height + 40 + (row * 3);
836
837   gtk_window_set_default_size(GTK_WINDOW(cap_if_w), -1, height);
838
839   gtk_widget_show_all(cap_if_w);
840
841   /* update the interface list every 1000ms */
842   timer_id = g_timeout_add(1000, update_all, sc);
843 }
844
845 /* start getting capture stats from all interfaces */
846 void
847 capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
848 {
849   GtkWidget *bbox,
850             *help_bt;
851 #ifdef HAVE_AIRPCAP
852   GtkWidget *decryption_cb;
853 #endif
854
855   if (cap_if_w != NULL) {
856     /* There's already a "Capture Interfaces" dialog box; reactivate it. */
857     reactivate_window(cap_if_w);
858     return;
859   }
860
861   if (!can_capture()) {
862     /* No interfaces or, on Windows, no WinPcap; just give up. */
863     return;
864   }
865
866 #ifdef HAVE_AIRPCAP
867   /* LOAD AIRPCAP INTERFACES */
868
869   /* If no airpcap interface is present, gray everything */
870   if (airpcap_if_active == NULL) {
871     if (airpcap_if_list == NULL) {
872       /*No airpcap device found */
873       airpcap_enable_toolbar_widgets(wireless_tb,FALSE);
874     } else {
875       /* default adapter is not airpcap... or is airpcap but is not found*/
876       if (airpcap_if_active){
877         /* XXX can never happen? */
878         airpcap_set_toolbar_stop_capture(airpcap_if_active);
879       }
880       airpcap_enable_toolbar_widgets(wireless_tb,FALSE);
881     }
882   }
883   if (airpcap_if_active){
884     decryption_cb = (GtkWidget *)g_object_get_data(G_OBJECT(wireless_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY);
885     /* XXXX update_decryption_mode_list() trigers a rescan, should only be done if the mode is changed */
886     update_decryption_mode_list(decryption_cb);
887     airpcap_set_toolbar_start_capture(airpcap_if_active);
888   }
889 #endif
890
891   cap_if_w = dlg_window_new("Wireshark: Capture Interfaces");  /* transient_for top_level  */
892   /* XXX: Note that on Windows the following is effectively a no-op; See dlg_window_new(). */
893   /*      This means, on Windows, if the top_level Wireshark window is destroyed when      */
894   /*      the capture_interfaces dialog window is open, that the dumpcap process will not  */
895   /*      be stopped since the destroy callback for the capture_interfaces dialog window   */
896   /*      will not be called.                                                              */
897   /*      ToDo: Fix this !                                                                 */
898   gtk_window_set_destroy_with_parent (GTK_WINDOW(cap_if_w), TRUE);
899
900   cap_if_top_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, DLG_UNRELATED_SPACING, FALSE);
901   gtk_container_add(GTK_CONTAINER(cap_if_w), cap_if_top_vb);
902   gtk_container_set_border_width(GTK_CONTAINER(cap_if_top_vb), DLG_OUTER_MARGIN);
903
904   /* Button row: close, help, stop, start, and options button */
905   bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_CAPTURE_START, WIRESHARK_STOCK_CAPTURE_OPTIONS, WIRESHARK_STOCK_CAPTURE_STOP, GTK_STOCK_CLOSE, NULL);
906
907   gtk_box_pack_end(GTK_BOX(cap_if_top_vb), bbox, FALSE, FALSE, 0);
908   help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
909   g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)(HELP_CAPTURE_INTERFACES_DIALOG));
910
911   stop_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_STOP);
912   g_signal_connect(stop_bt, "clicked", G_CALLBACK(capture_if_stop_cb), NULL);
913   close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
914   window_set_cancel_button(cap_if_w, close_bt, window_cancel_button_cb);
915   gtk_widget_set_tooltip_text(close_bt, "Close this window.");
916   options_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_OPTIONS);
917   g_signal_connect(options_bt, "clicked", G_CALLBACK(capture_prepare_cb), NULL);
918   capture_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_START);
919   g_signal_connect(capture_bt, "clicked", G_CALLBACK(capture_do_cb), NULL);
920
921   gtk_widget_grab_default(close_bt);
922
923   g_signal_connect(cap_if_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
924   g_signal_connect(cap_if_w, "destroy", G_CALLBACK(capture_if_destroy_cb), sc);
925
926   capture_if_refresh_if_list();
927
928   window_present(cap_if_w);
929
930   set_capture_if_dialog_for_capture_in_progress(gbl_capture_in_progress);
931 }
932
933 gboolean interfaces_dialog_window_present(void)
934 {
935   return (cap_if_w?TRUE:FALSE);
936 }
937
938 void refresh_if_window(void)
939 {
940   if (cap_if_w) {
941     capture_if_refresh_if_list();
942   }
943 }
944
945 void select_all_interfaces(gboolean enable _U_)
946 {
947   guint       ifs;
948   interface_t device;
949
950   for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) {
951     device = g_array_index(global_capture_opts.all_ifaces, interface_t, ifs);
952     update_selected_interface(device.if_info.name);
953   }
954 }
955
956 void destroy_if_window(void)
957 {
958   if (cap_if_w) {
959     window_destroy(cap_if_w);
960   }
961 }
962 #else /* HAVE_LIBPCAP */
963
964 void
965 set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress _U_)
966 {
967 }
968
969 #endif /* HAVE_LIBPCAP */
970
971 /*
972  * Editor modelines
973  *
974  * Local Variables:
975  * c-basic-offset: 2
976  * tab-width: 8
977  * indent-tabs-mode: nil
978  * End:
979  *
980  * ex: set shiftwidth=2 tabstop=8 expandtab:
981  * :indentSize=2:tabSize=8:noTabs=true:
982  */