Fix https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8105 :
[metze/wireshark/wip.git] / ui / gtk / main_80211_toolbar.c
1 /* main_80211_toolbar.c
2  * The 802.11 toolbar by Pontus Fuchs <pontus.fuchs@gmail.com>
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 /*
26  * This file implements the "80211" toolbar for Wireshark.
27  */
28
29 #include "config.h"
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <gtk/gtk.h>
35
36 #include "epan/prefs.h"
37
38 #include "gtkglobals.h"
39 #include "stock_icons.h"
40
41 #include "main.h"
42 #include "main_toolbar.h"
43
44 #include "ui/recent.h"
45 #include "ui/gtk/old-gtk-compat.h"
46
47 #include <ws80211_utils.h>
48 #include "capture_sync.h"
49
50 static GtkWidget *tb80211_tb, *tb80211_iface_list_box, *tb80211_freq_list_box, *tb80211_chan_type_box, *tb80211_info_label;
51
52 static GArray *tb80211_interfaces;
53 static struct ws80211_interface *tb80211_current_iface;
54 static gint32 tb80211_current_freq = -1;
55 static gint32 tb80211_current_type = -1;
56
57 static gboolean tb80211_dont_set_chan;
58 static gboolean tb80211_dont_set_iface;
59
60 static void tb80211_set_info(const char *errstr)
61 {
62     gtk_label_set_markup(GTK_LABEL(tb80211_info_label), errstr);
63 }
64
65 static
66 void add_channel_type(const char *type, int oldtype, int indx )
67 {
68     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(tb80211_chan_type_box), type);
69
70     if (oldtype != -1 && oldtype == ws80211_str_to_chan_type(type)) {
71         gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_chan_type_box), indx);
72         tb80211_current_type = oldtype;
73     }
74 }
75
76 static
77 void tb80211_update_chan_type(void)
78 {
79     static unsigned int tb80211_type_cnt;
80     unsigned int i;
81     int oldtype = -1;
82     for (i = 0; i < tb80211_type_cnt; i++) {
83         gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(tb80211_chan_type_box), 0);
84     }
85
86     if (!tb80211_current_iface) {
87         return;
88     }
89
90     oldtype = tb80211_current_type;
91     tb80211_current_type = -1;
92
93     i = 0;
94     add_channel_type(CHAN_NO_HT, oldtype, i++);
95
96     if (tb80211_current_iface->channel_types & (1 << WS80211_CHAN_HT20)) {
97         add_channel_type(CHAN_HT20, oldtype, i++);
98     }
99     if (tb80211_current_iface->channel_types & (1 << WS80211_CHAN_HT40MINUS)) {
100         add_channel_type(CHAN_HT40MINUS, oldtype, i++);
101     }
102     if (tb80211_current_iface->channel_types & (1 << WS80211_CHAN_HT40PLUS)) {
103         add_channel_type(CHAN_HT40PLUS, oldtype, i++);
104     }
105     tb80211_type_cnt = i;
106 }
107
108 static
109 void tb80211_update_freq(void)
110 {
111     static unsigned int tb80211_freq_cnt;
112     unsigned int i;
113     gchar *str;
114     gint32 oldfreq = 0;
115
116     for (i = 0; i < tb80211_freq_cnt; i++) {
117         gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(tb80211_freq_list_box), 0);
118     }
119
120     oldfreq = tb80211_current_freq;
121     tb80211_current_freq = -1;
122
123     if (!tb80211_current_iface)
124         return;
125
126     tb80211_freq_cnt = tb80211_current_iface->frequencies->len;
127     for (i = 0; i < tb80211_freq_cnt; i++) {
128         int freq;
129         freq = g_array_index(tb80211_current_iface->frequencies, int, i);
130         str = g_strdup_printf("%d MHz (%d)", freq, ws80211_frequency_to_channel(freq));
131         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(tb80211_freq_list_box), str);
132         g_free(str);
133
134         if (freq == oldfreq) {
135             gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_freq_list_box), i);
136             tb80211_current_freq = oldfreq;
137         }
138     }
139 }
140
141 static
142 void tb80211_update_freq_and_type(void)
143 {
144     tb80211_dont_set_chan = TRUE;
145     tb80211_update_freq();
146     tb80211_update_chan_type();
147     tb80211_dont_set_chan = FALSE;
148 }
149
150 #ifdef HAVE_LIBPCAP
151 /* Get currently selected channel type type enum */
152 static
153 int get_selected_channel_type(void)
154 {
155     int ret;
156     gchar *s = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(tb80211_chan_type_box));
157
158     ret = ws80211_str_to_chan_type(s);
159
160     g_free(s);
161     return ret;
162 }
163
164 /* Invoke dumpcap to set channel */
165 static int
166 tb80211_do_set_channel(char *iface, int freq, int type)
167 {
168         gchar *freq_s;
169         const gchar *type_s;
170         gchar *data, *primary_msg, *secondary_msg;
171         int ret;
172
173         freq_s = g_strdup_printf("%d", freq);
174         type_s = ws80211_chan_type_to_str(type);
175         ret = sync_interface_set_80211_chan(iface, freq_s, type_s,
176                                             &data, &primary_msg, &secondary_msg);
177
178         /* Parse the error msg */
179         if (ret && primary_msg) {
180                 return atoi(primary_msg);
181         }
182         g_free(data);
183         g_free(primary_msg);
184         g_free(secondary_msg);
185         g_free(freq_s);
186         return ret;
187 }
188
189 /* Called on freq and type combo box change. */
190 static void
191 tb80211_set_channel(void)
192 {
193     gchar *info = NULL;
194     int err, selected_chan, new_type, new_freq;
195
196     GtkComboBox *freq_combo = GTK_COMBO_BOX(tb80211_freq_list_box);
197
198     GtkComboBox *type_combo = GTK_COMBO_BOX(tb80211_chan_type_box);
199
200     selected_chan = gtk_combo_box_get_active(freq_combo);
201     if (selected_chan < 0)
202         return;
203
204     new_freq = g_array_index(tb80211_current_iface->frequencies, int, selected_chan);
205     new_type = get_selected_channel_type();
206
207     err = tb80211_do_set_channel(tb80211_current_iface->ifname, new_freq, new_type);
208     if (err) {
209         info = g_strdup_printf("<b>Failed to set channel: %s</b>", g_strerror(abs(err)));
210         /* Try to set back to last working chan */
211         err = tb80211_do_set_channel(tb80211_current_iface->ifname, tb80211_current_freq, tb80211_current_type);
212         if (err) {
213             gtk_combo_box_set_active(freq_combo, -1);
214             gtk_combo_box_set_active(type_combo, -1);
215             tb80211_current_freq = -1;
216             tb80211_current_type = -1;
217         }
218         else {
219             tb80211_update_freq_and_type();
220         }
221     }
222     else {
223         info = g_strdup_printf("%s Switched to %d MHz (%d)", tb80211_current_iface->ifname, new_freq, ws80211_frequency_to_channel(new_freq));
224         tb80211_current_freq = new_freq;
225         tb80211_current_type = new_type;
226     }
227     tb80211_set_info(info);
228     g_free(info);
229 }
230
231 static void
232 tb80211_set_chan_cb(GtkWidget *widget _U_, gpointer data _U_)
233 {
234     if (!tb80211_current_iface || tb80211_dont_set_chan)
235         return;
236
237     tb80211_set_channel();
238 }
239 #endif
240
241 static void
242 tb80211_iface_changed_cb(GtkWidget *widget, gpointer data _U_)
243 {
244     unsigned int i;
245     int ret;
246     struct ws80211_interface *iface;
247     struct ws80211_iface_info iface_info;
248     gchar *active;
249
250     if (tb80211_dont_set_iface)
251         return;
252
253     active = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget));
254
255     if (!active || tb80211_dont_set_chan)
256         goto out_free;
257
258     for (i = 0; i < tb80211_interfaces->len; i++) {
259         iface = g_array_index(tb80211_interfaces, struct ws80211_interface *, i);
260         if (strcmp(active, iface->ifname) == 0) {
261             tb80211_current_iface = iface;
262             break;
263         }
264     }
265
266     tb80211_current_freq = -1;
267     tb80211_current_type = -1;
268     if (tb80211_current_iface) {
269         ret = ws80211_get_iface_info(tb80211_current_iface->ifname, &iface_info);
270         if (!ret) {
271             tb80211_current_freq = iface_info.current_freq;
272             tb80211_current_type = iface_info.current_chan_type;
273         }
274     }
275     tb80211_update_freq_and_type();
276
277 out_free:
278     g_free(active);
279 }
280
281 void
282 tb80211_refresh_interfaces(void)
283 {
284     struct ws80211_interface *iface;
285     unsigned int i;
286     gboolean same = FALSE;
287     gchar *selected_iface = NULL, *info;
288
289     if (!tb80211_tb)
290         return;
291
292     if (tb80211_interfaces) {
293         selected_iface = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(tb80211_iface_list_box));
294
295         for (i = 0; i < tb80211_interfaces->len; i++)
296                 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(tb80211_iface_list_box), 0);
297
298         ws80211_free_interfaces(tb80211_interfaces);
299         tb80211_interfaces = NULL;
300         tb80211_current_iface = NULL;
301     }
302     tb80211_interfaces = ws80211_find_interfaces();
303
304     if (!tb80211_interfaces) {
305         goto out_free;
306     }
307
308     tb80211_dont_set_iface = TRUE;
309     for (i = 0; i < tb80211_interfaces->len; i++) {
310         iface = g_array_index(tb80211_interfaces, struct ws80211_interface *, i);
311         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(tb80211_iface_list_box), iface->ifname);
312
313         if (selected_iface && strcmp(selected_iface, iface->ifname) == 0) {
314             gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_iface_list_box), i);
315             tb80211_current_iface = iface;
316             same = TRUE;
317         }
318     }
319     tb80211_dont_set_iface = FALSE;
320
321     /* Reset selectors if interface disappeared */
322     if (!same) {
323         gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_iface_list_box), -1);
324         gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_freq_list_box), -1);
325         gtk_combo_box_set_active(GTK_COMBO_BOX(tb80211_chan_type_box), -1);
326     }
327
328     info = g_strdup_printf("%d monitor interfaces found", tb80211_interfaces->len);
329     tb80211_set_info(info);
330     g_free(info);
331 out_free:
332     g_free(selected_iface);
333 }
334
335 static void
336 tb80211_add_label(const gchar *text, GtkWidget *tb)
337 {
338     GtkWidget     *label;
339     GtkToolItem   *label_ti;
340
341     label_ti = gtk_tool_item_new();
342     gtk_widget_show(GTK_WIDGET (label_ti));
343     label = gtk_label_new(text);
344     gtk_widget_show(GTK_WIDGET (label));
345     gtk_container_add(GTK_CONTAINER(label_ti), label);
346     gtk_toolbar_insert(GTK_TOOLBAR(tb), label_ti, -1);
347 }
348
349 GtkWidget *
350 ws80211_toolbar_new(void)
351 {
352     GtkToolItem   *ti;
353     int ret;
354
355     /* filter toolbar */
356     tb80211_tb = gtk_toolbar_new();
357     gtk_orientable_set_orientation(GTK_ORIENTABLE(tb80211_tb),
358                                 GTK_ORIENTATION_HORIZONTAL);
359
360     gtk_widget_show(tb80211_tb);
361
362     tb80211_add_label(" Interface: ", tb80211_tb);
363
364     ti = gtk_tool_item_new();
365     gtk_widget_show(GTK_WIDGET (ti));
366     tb80211_iface_list_box = gtk_combo_box_text_new();
367     g_signal_connect(tb80211_iface_list_box, "changed", G_CALLBACK(tb80211_iface_changed_cb), NULL);
368     gtk_container_add(GTK_CONTAINER(ti), tb80211_iface_list_box);
369     gtk_widget_show(GTK_WIDGET (tb80211_iface_list_box));
370     gtk_toolbar_insert(GTK_TOOLBAR(tb80211_tb), ti, -1);
371
372     tb80211_add_label(" Frequency: ", tb80211_tb);
373
374     ti = gtk_tool_item_new();
375     gtk_widget_show(GTK_WIDGET (ti));
376     tb80211_freq_list_box = gtk_combo_box_text_new();
377 #ifdef HAVE_LIBPCAP
378     g_signal_connect(tb80211_freq_list_box, "changed", G_CALLBACK(tb80211_set_chan_cb), NULL);
379 #else
380     gtk_widget_set_sensitive(GTK_WIDGET(tb80211_freq_list_box), FALSE);
381 #endif
382     gtk_container_add(GTK_CONTAINER(ti), tb80211_freq_list_box);
383     gtk_widget_show(GTK_WIDGET (tb80211_freq_list_box));
384     gtk_toolbar_insert(GTK_TOOLBAR(tb80211_tb), ti, -1);
385
386     ti = gtk_tool_item_new();
387     gtk_widget_show(GTK_WIDGET (ti));
388     tb80211_chan_type_box = gtk_combo_box_text_new();
389 #ifdef HAVE_LIBPCAP
390     g_signal_connect(tb80211_chan_type_box, "changed", G_CALLBACK(tb80211_set_chan_cb), NULL);
391 #else
392     gtk_widget_set_sensitive(GTK_WIDGET(tb80211_freq_list_box), FALSE);
393 #endif
394     gtk_container_add(GTK_CONTAINER(ti), tb80211_chan_type_box);
395     gtk_widget_show(GTK_WIDGET (tb80211_chan_type_box));
396     gtk_toolbar_insert(GTK_TOOLBAR(tb80211_tb), ti, -1);
397
398     ti = gtk_separator_tool_item_new();
399     gtk_widget_show(GTK_WIDGET (ti));
400     gtk_toolbar_insert(GTK_TOOLBAR(tb80211_tb), ti, -1);
401
402     ti = gtk_tool_item_new();
403     gtk_widget_show(GTK_WIDGET (ti));
404     tb80211_info_label = gtk_label_new("");
405     gtk_container_add(GTK_CONTAINER(ti), tb80211_info_label);
406     gtk_widget_show(GTK_WIDGET (tb80211_info_label));
407     gtk_toolbar_insert(GTK_TOOLBAR(tb80211_tb), ti, -1);
408
409     /* make current preferences effective */
410     toolbar_redraw_all();
411
412     ret = ws80211_init();
413     if(ret) {
414         tb80211_set_info("<b>Failed to initialize ws80211</b>");
415     } else {
416         tb80211_refresh_interfaces();
417     }
418
419     return tb80211_tb;
420 }