If we don't understand the AP security (such as EAP) then disable the
[dhcpcd-ui] / src / dhcpcd-gtk / menu.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009-2015 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "dhcpcd-gtk.h"
29
30 static const char *copyright = "Copyright (c) 2009-2015 Roy Marples";
31
32 static GtkStatusIcon *sicon;
33 static GtkWidget *menu;
34 static GtkAboutDialog *about;
35 static guint bgscan_timer;
36
37 static void
38 on_pref(_unused GObject *o, gpointer data)
39 {
40
41         prefs_show((DHCPCD_CONNECTION *)data);
42 }
43
44 static void
45 on_quit(void)
46 {
47
48         wpa_abort();
49         gtk_main_quit();
50 }
51
52 static void
53 ssid_hook(GtkMenuItem *item, _unused gpointer data)
54 {
55         DHCPCD_WI_SCAN *scan;
56         WI_SCAN *wi;
57
58         scan = g_object_get_data(G_OBJECT(item), "dhcpcd_wi_scan");
59         wi = wi_scan_find(scan);
60         if (wi) {
61                 DHCPCD_CONNECTION *con;
62
63                 con = dhcpcd_if_connection(wi->interface);
64                 if (con) {
65                         DHCPCD_WPA *wpa;
66
67                         wpa = dhcpcd_wpa_find(con, wi->interface->ifname);
68                         if (wpa)
69                                 wpa_configure(wpa, scan);
70                 }
71         }
72 }
73
74 static void
75 on_about(_unused GtkMenuItem *item)
76 {
77
78         if (about == NULL) {
79                 about = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
80                 gtk_about_dialog_set_version(about, VERSION);
81                 gtk_about_dialog_set_copyright(about, copyright);
82                 gtk_about_dialog_set_website_label(about, "dhcpcd Website");
83                 gtk_about_dialog_set_website(about,
84                     "http://roy.marples.name/projects/dhcpcd");
85                 gtk_about_dialog_set_logo_icon_name(about,
86                     "network-transmit-receive");
87                 gtk_about_dialog_set_comments(about,
88                     "Part of the dhcpcd project");
89         }
90         gtk_window_set_position(GTK_WINDOW(about), GTK_WIN_POS_MOUSE);
91         gtk_window_present(GTK_WINDOW(about));
92         gtk_dialog_run(GTK_DIALOG(about));
93         gtk_widget_hide(GTK_WIDGET(about));
94 }
95
96 static bool
97 is_associated(WI_SCAN *wi, DHCPCD_WI_SCAN *scan)
98 {
99
100         return dhcpcd_wi_associated(wi->interface, scan);
101 }
102
103 static bool
104 get_security_icon(int flags, const char **icon)
105 {
106         bool active;
107
108         active = true;
109         if (flags & WSF_SECURE) {
110                 if (flags & WSF_PSK)
111                         *icon = "network-wireless-encrypted";
112                 else {
113                         *icon = "network-error";        
114                         active = false;
115                 }
116         } else
117                 *icon = "dialog-warning";
118         return active;
119 }
120
121 static void
122 update_item(WI_SCAN *wi, WI_MENU *m, DHCPCD_WI_SCAN *scan)
123 {
124         const char *icon;
125         GtkWidget *sel;
126         bool active;
127
128         m->scan = scan;
129
130         g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
131
132         m->associated = is_associated(wi, scan);
133         if (m->associated)
134                 sel = gtk_image_new_from_icon_name("dialog-ok-apply",
135                     GTK_ICON_SIZE_MENU);
136         else
137                 sel = NULL;
138         gtk_image_menu_item_set_image(
139             GTK_IMAGE_MENU_ITEM(m->menu), sel);
140
141         if (m->associated) {
142                 gchar *lbl;
143
144                 lbl = g_markup_printf_escaped("<b>%s</b>",
145                     scan->ssid);
146                 gtk_label_set_markup(GTK_LABEL(m->ssid), lbl);
147                 g_free(lbl);
148         } else
149                 gtk_label_set_text(GTK_LABEL(m->ssid), scan->ssid);
150         
151         active = get_security_icon(scan->flags, &icon);
152         m->icon = gtk_image_new_from_icon_name(icon,
153             GTK_ICON_SIZE_MENU);
154         if (gtk_widget_get_sensitive(GTK_WIDGET(m->menu)) != active)
155                 gtk_widget_set_sensitive(GTK_WIDGET(m->menu), active);
156
157         icon = get_strength_icon_name(scan->strength.value);
158         m->strength = gtk_image_new_from_icon_name(icon,
159                 GTK_ICON_SIZE_MENU);
160
161 #if 0
162         if (scan->wpa_flags[0] == '\0')
163                 gtk_widget_set_tooltip_text(m->menu, scan->bssid);
164         else {
165                 char *tip = g_strconcat(scan->bssid, " ", scan->wpa_flags,
166                     NULL);
167                 gtk_widget_set_tooltip_text(m->menu, tip);
168                 g_free(tip);
169         }
170 #endif
171
172         g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
173 }
174
175 static WI_MENU *
176 create_menu(WI_SCAN *wis, DHCPCD_WI_SCAN *scan)
177 {
178         WI_MENU *wim;
179         GtkWidget *box;
180         const char *icon;
181
182         wim = g_malloc(sizeof(*wim));
183         wim->scan = scan;
184         wim->menu = gtk_image_menu_item_new();
185         box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
186         gtk_container_add(GTK_CONTAINER(wim->menu), box);
187
188         wim->ssid = gtk_label_new(NULL);
189         gtk_misc_set_alignment(GTK_MISC(wim->ssid), 0.0, 0.5);
190         gtk_box_pack_start(GTK_BOX(box), wim->ssid, TRUE, TRUE, 0);
191
192         get_security_icon(scan->flags, &icon);
193         wim->icon = gtk_image_new_from_icon_name(icon,
194             GTK_ICON_SIZE_MENU);
195         gtk_box_pack_start(GTK_BOX(box), wim->icon, FALSE, FALSE, 0);
196
197         icon = get_strength_icon_name(scan->strength.value);
198         wim->strength = gtk_image_new_from_icon_name(icon,
199                 GTK_ICON_SIZE_MENU);
200         gtk_box_pack_start(GTK_BOX(box), wim->strength, FALSE, FALSE, 0);
201
202 #if 0
203         if (scan->wpa_flags[0] == '\0')
204                 gtk_widget_set_tooltip_text(wim->menu, scan->bssid);
205         else {
206                 char *tip;
207
208                 tip = g_strconcat(scan->bssid, " ", scan->wpa_flags, NULL);
209                 gtk_widget_set_tooltip_text(wim->menu, tip);
210                 g_free(tip);
211         }
212 #endif
213         update_item(wis, wim, scan);
214
215         g_signal_connect(G_OBJECT(wim->menu), "activate",
216             G_CALLBACK(ssid_hook), NULL);
217         g_object_set_data(G_OBJECT(wim->menu), "dhcpcd_wi_scan", scan);
218
219         return wim;
220 }
221
222 void
223 menu_update_scans(WI_SCAN *wi, DHCPCD_WI_SCAN *scans)
224 {
225         WI_MENU *wim, *win;
226         DHCPCD_WI_SCAN *s;
227         bool found;
228         int position;
229
230         if (wi->ifmenu == NULL) {
231                 dhcpcd_wi_scans_free(wi->scans);
232                 wi->scans = scans;
233                 return;
234         }
235
236         TAILQ_FOREACH_SAFE(wim, &wi->menus, next, win) {
237                 found = false;
238                 for (s = scans; s; s = s->next) {
239                         if (strcmp(wim->scan->ssid, s->ssid) == 0) {
240                                 /* If assoication changes, we
241                                  * need to remove the item to replace it */
242                                 if (wim->associated ==
243                                     is_associated(wi, s))
244                                 {
245                                         found = true;
246                                         update_item(wi, wim, s);
247                                 }
248                                 break;
249                         }
250                 }
251                 if (!found) {
252                         TAILQ_REMOVE(&wi->menus, wim, next);
253                         gtk_widget_destroy(wim->menu);
254                         g_free(wim);
255                 }
256         }
257
258         for (s = scans; s; s = s->next) {
259                 found = false;
260                 position = 0;
261                 TAILQ_FOREACH(wim, &wi->menus, next) {
262                         if (strcmp(wim->scan->ssid, s->ssid) == 0) {
263                                 found = true;
264                                 break;
265                         }
266                         /* Assoicated scans are always first */
267                         if (!is_associated(wi, s) &&
268                             dhcpcd_wi_scan_compare(wim->scan, s) < 0)
269                                 position++;
270                 }
271                 if (!found) {
272                         wim = create_menu(wi, s);
273                         TAILQ_INSERT_TAIL(&wi->menus, wim, next);
274                         gtk_menu_shell_insert(GTK_MENU_SHELL(wi->ifmenu),
275                             wim->menu, position);
276                         gtk_widget_show_all(wim->menu);
277                 }
278         }
279
280         dhcpcd_wi_scans_free(wi->scans);
281         wi->scans = scans;
282
283         if (gtk_widget_get_visible(wi->ifmenu))
284                 gtk_menu_reposition(GTK_MENU(wi->ifmenu));
285 }
286
287 void
288 menu_remove_if(WI_SCAN *wi)
289 {
290         WI_MENU *wim;
291
292         if (wi->ifmenu == NULL)
293                 return;
294
295         if (wi->ifmenu == menu)
296                 menu = NULL;
297
298         gtk_widget_destroy(wi->ifmenu);
299         wi->ifmenu = NULL;
300         while ((wim = TAILQ_FIRST(&wi->menus))) {
301                 TAILQ_REMOVE(&wi->menus, wim, next);
302                 g_free(wim);
303         }
304
305         if (menu && gtk_widget_get_visible(menu))
306                 gtk_menu_reposition(GTK_MENU(menu));
307 }
308
309 static GtkWidget *
310 add_scans(WI_SCAN *wi)
311 {
312         GtkWidget *m;
313         DHCPCD_WI_SCAN *wis;
314         WI_MENU *wim;
315         int position;
316
317         if (wi->scans == NULL)
318                 return NULL;
319
320         m = gtk_menu_new();
321         position = 0;
322         for (wis = wi->scans; wis; wis = wis->next) {
323                 wim = create_menu(wi, wis);
324                 TAILQ_INSERT_TAIL(&wi->menus, wim, next);
325                 gtk_menu_shell_insert(GTK_MENU_SHELL(m),
326                     wim->menu, is_associated(wi, wis) ? 0 : position);
327                 position++;
328         }
329
330         return m;
331 }
332
333 void
334 menu_abort(void)
335 {
336         WI_SCAN *wis;
337         WI_MENU *wim;
338
339         if (bgscan_timer) {
340                 g_source_remove(bgscan_timer);
341                 bgscan_timer = 0;
342         }
343
344         TAILQ_FOREACH(wis, &wi_scans, next) {
345                 wis->ifmenu = NULL;
346                 while ((wim = TAILQ_FIRST(&wis->menus))) {
347                         TAILQ_REMOVE(&wis->menus, wim, next);
348                         g_free(wim);
349                 }
350         }
351
352         if (menu != NULL) {
353                 gtk_widget_destroy(menu);
354                 g_object_ref_sink(menu);
355                 g_object_unref(menu);
356                 menu = NULL;
357         }
358 }
359
360 static gboolean
361 menu_bgscan(gpointer data)
362 {
363         WI_SCAN *w;
364         DHCPCD_CONNECTION *con;
365         DHCPCD_WPA *wpa;
366
367         if (menu == NULL || !gtk_widget_get_visible(menu)) {
368                 bgscan_timer = 0;
369                 return FALSE;
370         }
371
372         con = (DHCPCD_CONNECTION *)data;
373         TAILQ_FOREACH(w, &wi_scans, next) {
374                 if (w->interface->wireless && w->interface->up) {
375                         wpa = dhcpcd_wpa_find(con, w->interface->ifname);
376                         if (wpa &&
377                             (!w->interface->up ||
378                             dhcpcd_wpa_can_background_scan(wpa)))
379                                 dhcpcd_wpa_scan(wpa);
380                 }
381         }
382
383         return TRUE;
384 }
385
386 static void
387 on_activate(GtkStatusIcon *icon)
388 {
389         WI_SCAN *w, *l;
390         GtkWidget *item, *image;
391
392         sicon = icon;
393         notify_close();
394         prefs_abort();
395         menu_abort();
396
397         if ((w = TAILQ_FIRST(&wi_scans)) == NULL)
398                 return;
399
400         if ((l = TAILQ_LAST(&wi_scans, wi_scan_head)) && l != w) {
401                 menu = gtk_menu_new();
402                 TAILQ_FOREACH(l, &wi_scans, next) {
403                         item = gtk_image_menu_item_new_with_label(
404                                 l->interface->ifname);
405                         image = gtk_image_new_from_icon_name(
406                                 "network-wireless", GTK_ICON_SIZE_MENU);
407                         gtk_image_menu_item_set_image(
408                                 GTK_IMAGE_MENU_ITEM(item), image);
409                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
410                         l->ifmenu = add_scans(l);
411                         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
412                             l->ifmenu);
413                 }
414         } else {
415                 w->ifmenu = menu = add_scans(w);
416         }
417
418         if (menu) {
419                 gtk_widget_show_all(GTK_WIDGET(menu));
420                 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
421                     gtk_status_icon_position_menu, icon,
422                     1, gtk_get_current_event_time());
423
424                 bgscan_timer = g_timeout_add(DHCPCD_WPA_SCAN_SHORT,
425                     menu_bgscan, dhcpcd_if_connection(w->interface));
426         }
427 }
428
429 static void
430 on_popup(GtkStatusIcon *icon, guint button, guint32 atime, gpointer data)
431 {
432         DHCPCD_CONNECTION *con;
433         GtkMenu *mnu;
434         GtkWidget *item, *image;
435
436         notify_close();
437
438         con = (DHCPCD_CONNECTION *)data;
439         mnu = (GtkMenu *)gtk_menu_new();
440
441         item = gtk_image_menu_item_new_with_mnemonic(_("_Preferences"));
442         image = gtk_image_new_from_icon_name("preferences-system-network",
443             GTK_ICON_SIZE_MENU);
444         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
445         if (dhcpcd_status(con, NULL) == DHC_DOWN)
446                 gtk_widget_set_sensitive(item, false);
447         else
448                 g_signal_connect(G_OBJECT(item), "activate",
449                     G_CALLBACK(on_pref), data);
450         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
451
452         item = gtk_separator_menu_item_new();
453         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
454
455         item = gtk_image_menu_item_new_with_mnemonic(_("_About"));
456         image = gtk_image_new_from_icon_name("help-about",
457             GTK_ICON_SIZE_MENU);
458         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
459         g_signal_connect(G_OBJECT(item), "activate",
460             G_CALLBACK(on_about), icon);
461         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
462
463         item = gtk_image_menu_item_new_with_mnemonic(_("_Quit"));
464         image = gtk_image_new_from_icon_name("application-exit",
465             GTK_ICON_SIZE_MENU);
466         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
467         g_signal_connect(G_OBJECT(item), "activate",
468             G_CALLBACK(on_quit), icon);
469         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
470
471         gtk_widget_show_all(GTK_WIDGET(mnu));
472         gtk_menu_popup(GTK_MENU(mnu), NULL, NULL,
473             gtk_status_icon_position_menu, icon, button, atime);
474         if (button == 0)
475                 gtk_menu_shell_select_first(GTK_MENU_SHELL(mnu), FALSE);
476 }
477
478 void
479 menu_init(GtkStatusIcon *icon, DHCPCD_CONNECTION *con)
480 {
481
482         g_signal_connect(G_OBJECT(icon), "activate",
483             G_CALLBACK(on_activate), con);
484         g_signal_connect(G_OBJECT(icon), "popup_menu",
485             G_CALLBACK(on_popup), con);
486 }
487
488
489 #if GTK_MAJOR_VERSION == 2
490 GtkWidget *
491 gtk_box_new(GtkOrientation o, gint s)
492 {
493
494         if (o == GTK_ORIENTATION_HORIZONTAL)
495                 return gtk_hbox_new(false, s);
496         else
497                 return gtk_vbox_new(false, s);
498 }
499 #endif