Fix compile and icon.
[dhcpcd-ui] / prefs.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009 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 <errno.h>
28
29 #include "dhcpcd-config.h"
30 #include "dhcpcd-gtk.h"
31 #include "prefs.h"
32
33 static GtkWidget *dialog, *blocks, *names, *controls, *clear, *rebind;
34 static GtkWidget *autoconf, *address, *router, *dns_servers, *dns_search;
35 static GPtrArray *config;
36 static char *block, *name;
37
38 static void
39 show_config(GPtrArray *array)
40 {
41         const char *val;
42         bool autocnf;
43
44         if (get_config_static(array, "ip_address=", &val) != -1)
45                 autocnf = false;
46         else {
47                 get_config(array, "inform", &val);
48                 autocnf = true;
49         }
50         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autoconf), autocnf);
51         gtk_entry_set_text(GTK_ENTRY(address), val ? val : "");
52         get_config_static(array, "routers=", &val);
53         gtk_entry_set_text(GTK_ENTRY(router), val ? val : "");
54         get_config_static(array, "domain_name_servers=", &val);
55         gtk_entry_set_text(GTK_ENTRY(dns_servers), val ? val : "");
56         get_config_static(array, "domain_search=", &val);
57         gtk_entry_set_text(GTK_ENTRY(dns_search), val ? val : "");
58 }
59
60 static char *
61 combo_active_text(GtkWidget *widget)
62 {
63         GtkListStore *store;
64         GtkTreeIter iter;
65         GValue val;
66         char *text;
67
68         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)));
69         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
70                 return NULL;
71         memset(&val, 0, sizeof(val));
72         gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, 1, &val);
73         text = g_strdup(g_value_get_string(&val));
74         g_value_unset(&val);
75         return text;
76 }
77
78 static void
79 make_config(GPtrArray *array)
80 {
81         const char *val;
82         bool a;
83
84         a = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autoconf));
85         val = gtk_entry_get_text(GTK_ENTRY(address));
86         if (*val == '\0')
87                 val = NULL;
88         set_option(array, false, "inform", a ? val : NULL);
89         set_option(array, true, "ip_address=", a ? NULL : val);
90         
91         val = gtk_entry_get_text(GTK_ENTRY(router));
92         if (a && *val == '\0')
93                 val = NULL;
94         set_option(array, true, "routers=", val);
95         
96         val = gtk_entry_get_text(GTK_ENTRY(dns_servers));
97         if (a && *val == '\0')
98                 val = NULL;
99         set_option(array, true, "domain_name_servers=", val);
100         
101         val = gtk_entry_get_text(GTK_ENTRY(dns_search));
102         if (a && *val == '\0')
103                 val = NULL;
104         set_option(array, true, "domain_search=", val);
105 }
106
107 static void
108 set_name_active_icon(const char *iname)
109 {
110         GtkListStore *store;
111         GtkTreeIter iter;
112         GtkIconTheme *it;
113         GtkTreePath *path;
114         GdkPixbuf *pb;
115
116         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
117         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(names), &iter))
118                 return;
119         it = gtk_icon_theme_get_default();
120         pb = gtk_icon_theme_load_icon(it, iname,
121             GTK_ICON_SIZE_MENU, 0, NULL);
122         if (pb) {
123                 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
124                 gtk_list_store_set(store, &iter, 0, pb, -1);
125                 g_object_unref(pb);
126                 gtk_tree_model_row_changed(GTK_TREE_MODEL(store), path, &iter);
127                 gtk_tree_path_free(path);
128         }
129 }
130
131 static GSList *
132 list_interfaces(void)
133 {
134         GSList *list, *l;
135         const struct if_msg *ifm;
136
137         list = NULL;
138         for (l = interfaces; l; l = l->next) {
139                 ifm = (const struct if_msg *)l->data;
140                 list = g_slist_append(list, ifm->ifname);
141         }
142         return list;
143 }
144
145 static GSList *
146 list_ssids(void)
147 {
148         GSList *list, *l, *a, *la;
149         const struct if_msg *ifm;
150         const struct if_ap *ifa;
151
152         list = NULL;
153         for (l = interfaces; l; l = l->next) {
154                 ifm = (const struct if_msg *)l->data;
155                 if (!ifm->wireless)
156                         continue;
157                 for (a = ifm->scan_results; a; a = a->next) {
158                         ifa = (const struct if_ap *)a->data;
159                         for (la = list; la; la = la->next)
160                                 if (g_strcmp0((const char *)la->data,
161                                         ifa->ssid) == 0)
162                                         break;
163                         if (la == NULL)
164                                 list = g_slist_append(list, ifa->ssid);
165                 }
166         }
167         return list;
168 }
169
170 static void
171 blocks_on_change(GtkWidget *widget)
172 {
173         GtkListStore *store;
174         GtkTreeIter iter;
175         GError *error;
176         char **list, **lp;
177         const char *iname, *nn;
178         GSList *l, *new_names;
179         GtkIconTheme *it;
180         GdkPixbuf *pb;
181         int n;
182
183         if (name) {
184                 make_config(config);
185                 save_config(block, name, config);
186                 free_config(&config);
187                 show_config(config);
188                 g_free(block);
189                 g_free(name);
190                 name = NULL;
191         }
192         block = combo_active_text(widget);
193         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
194         gtk_list_store_clear(store);
195         error = NULL;
196         if (!dbus_g_proxy_call(dbus, "GetConfigBlocks", &error,
197                 G_TYPE_STRING, block, G_TYPE_INVALID,
198                 G_TYPE_STRV, &list, G_TYPE_INVALID))
199         {
200                 g_warning("GetConfigBlocks: %s", error->message);
201                 g_clear_error(&error);
202                 return;
203         }
204
205         it = gtk_icon_theme_get_default();
206         if (g_strcmp0(block, "interface") == 0)
207                 new_names = list_interfaces();
208         else
209                 new_names = list_ssids();
210
211         n = 0;
212         for (l = new_names; l; l = l->next) {
213                 nn = (const char *)l->data;
214                 for (lp = list; *lp; lp++)
215                         if (g_strcmp0(nn, *lp) == 0)
216                                 break;
217                 if (*lp)
218                         iname = "document-save";
219                 else
220                         iname = "document-new";
221                 pb = gtk_icon_theme_load_icon(it, iname,
222                     GTK_ICON_SIZE_MENU, 0, &error);
223                 gtk_list_store_append(store, &iter);
224                 gtk_list_store_set(store, &iter, 0, pb, 1, nn, -1);
225                 g_object_unref(pb);
226                 n++;
227         }
228
229         for (lp = list; *lp; lp++) {
230                 for (l = new_names; l; l = l->next)
231                         if (g_strcmp0((const char *)l->data, *lp) == 0)
232                                 break;
233                 if (l != NULL)
234                         continue;
235                 pb = gtk_icon_theme_load_icon(it, "document-save",
236                     GTK_ICON_SIZE_MENU, 0, &error);
237                 gtk_list_store_append(store, &iter);
238                 gtk_list_store_set(store, &iter, 0, pb, 1, *lp, -1);
239                 g_object_unref(pb);
240                 n++;
241         }
242         gtk_widget_set_sensitive(names, n);
243         g_slist_free(new_names);
244         g_strfreev(list);
245 }
246
247 static void
248 names_on_change(void)
249 {
250         
251         if (name) {
252                 make_config(config);
253                 save_config(block, name, config);
254                 g_free(name);
255         }
256         name = combo_active_text(names);
257         free_config(&config);
258         config = load_config(block, name);
259         show_config(config);
260         gtk_widget_set_sensitive(controls, name ? true : false);
261         gtk_widget_set_sensitive(clear, name ? true : false);
262         gtk_widget_set_sensitive(rebind, name ? true : false);
263 }
264
265 static bool
266 valid_address(const char *val, bool allow_cidr)
267 {
268         char *addr, *p, *e;
269         struct in_addr in;
270         gint64 cidr;
271         bool retval;
272
273         addr = g_strdup(val);
274         if (allow_cidr) {
275                 p = strchr(addr, '/');
276                 if (p != NULL) {
277                         *p++ = '\0';
278                         errno = 0;
279                         e = NULL;
280                         cidr = g_ascii_strtoll(p, &e, 10);
281                         if (cidr < 0 || cidr > 32 ||
282                             errno != 0 || *e != '\0')
283                         {
284                                 retval = false;
285                                 goto out;
286                         }
287                 }
288         }
289         retval = inet_aton(addr, &in) == 0 ? false : true;
290         
291 out:
292         g_free(addr);
293         return retval;
294 }
295
296         
297 static bool
298 address_lost_focus(GtkEntry *entry)
299 {
300         const char *val;
301
302         val = gtk_entry_get_text(entry);
303         if (*val != '\0' && !valid_address(val, true))
304                 gtk_entry_set_text(entry, "");
305         return false;
306 }
307
308 static bool
309 entry_lost_focus(GtkEntry *entry)
310 {
311         const char *val;
312         char **a, **p;
313
314         val = gtk_entry_get_text(entry);
315         a = g_strsplit(val, " ", 0);
316         for (p = a; *p; p++) {
317                 if (**p != '\0' && !valid_address(*p, false)) {
318                         gtk_entry_set_text(entry, "");
319                         break;
320                 }
321         }
322         g_strfreev(a);
323         return false;
324 }
325
326 static void
327 on_clear(void)
328 {
329
330         free_config(&config);
331         config = g_ptr_array_new();
332         if (save_config(block, name, config)) {
333                 set_name_active_icon("document-new");
334                 show_config(config);
335         }
336 }
337
338 static void
339 rebind_interface(const char *iface)
340 {
341         GError *error;
342         
343         error = NULL;
344         if (!dbus_g_proxy_call(dbus, "Rebind", &error,
345                 G_TYPE_STRING, iface, G_TYPE_INVALID, G_TYPE_INVALID))
346         {
347                 g_critical("Rebind: %s: %s", iface, error->message);
348                 g_clear_error(&error);
349         }
350 }
351
352 static void
353 on_rebind(void)
354 {
355         GSList *l;
356         const struct if_msg *ifm;
357
358         make_config(config);
359         if (save_config(block, name, config)) {
360                 set_name_active_icon(config->len == 0 ?
361                     "document-new" : "document-save");
362                 show_config(config);
363                 if (g_strcmp0(block, "interface") == 0)
364                         rebind_interface(name);
365                 else {
366                         for (l = interfaces; l; l = l->next) {
367                                 ifm = (const struct if_msg *)l->data;
368                                 if (g_strcmp0(ifm->ssid, name) == 0)
369                                         rebind_interface(ifm->ifname);
370                         }
371                 }
372         }
373 }
374
375 static void
376 on_destroy(void)
377 {
378         
379         if (name != NULL) {
380                 make_config(config);
381                 save_config(block, name, config);
382                 g_free(block);
383                 g_free(name);
384                 block = name = NULL;
385         }
386         free_config(&config);
387         dialog = NULL;
388 }
389
390 void
391 dhcpcd_prefs_close(void)
392 {
393         
394         if (dialog != NULL) {
395                 gtk_widget_destroy(dialog);
396                 dialog = NULL;
397         }
398 }
399
400 void
401 dhcpcd_prefs_show(void)
402 {
403         GtkWidget *dialog_vbox, *hbox, *vbox, *table, *w;
404         GtkListStore *store;
405         GtkTreeIter iter;
406         GtkCellRenderer *rend;
407         GError *error;
408         GtkIconTheme *it;
409         GdkPixbuf *pb;
410         
411         if (dialog) {
412                 gtk_window_present(GTK_WINDOW(dialog));
413                 return;
414         }
415
416         dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
417         g_signal_connect(G_OBJECT(dialog), "destroy", on_destroy, NULL);
418
419         gtk_window_set_title(GTK_WINDOW(dialog), _("dhcpcd preferences"));
420         gtk_window_set_resizable(GTK_WINDOW(dialog), false);
421         gtk_window_set_icon_name(GTK_WINDOW(dialog), "config-users");
422         gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
423         gtk_window_set_type_hint(GTK_WINDOW(dialog),
424             GDK_WINDOW_TYPE_HINT_DIALOG);
425
426         dialog_vbox = gtk_vbox_new(false, 10);
427         gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
428         gtk_container_add(GTK_CONTAINER(dialog), dialog_vbox);
429
430         hbox = gtk_hbox_new(false, 0);
431         gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, false, false, 3);
432         w = gtk_label_new("Configure:");
433         gtk_box_pack_start(GTK_BOX(hbox), w, false, false, 3);
434         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
435         it = gtk_icon_theme_get_default();
436         error = NULL;
437         pb = gtk_icon_theme_load_icon(it, "network-wired",
438             GTK_ICON_SIZE_MENU, 0, &error);     
439         gtk_list_store_append(store, &iter);
440         gtk_list_store_set(store, &iter, 0, pb, 1, "interface", -1);
441         g_object_unref(pb);
442         pb = gtk_icon_theme_load_icon(it, "network-wireless",
443             GTK_ICON_SIZE_MENU, 0, &error);
444         gtk_list_store_append(store, &iter);
445         gtk_list_store_set(store, &iter, 0, pb, 1, "ssid", -1);
446         g_object_unref(pb);
447         blocks = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
448         rend = gtk_cell_renderer_pixbuf_new();
449         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, false);
450         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
451             rend, "pixbuf", 0);
452         rend = gtk_cell_renderer_text_new();
453         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, true);
454         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
455             rend, "text", 1);
456         gtk_combo_box_set_active(GTK_COMBO_BOX(blocks), 0);
457         gtk_box_pack_start(GTK_BOX(hbox), blocks, false, false, 3);
458         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
459         names = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
460         rend = gtk_cell_renderer_pixbuf_new();
461         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, false);
462         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names),
463             rend, "pixbuf", 0);
464         rend = gtk_cell_renderer_text_new();
465         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, true);
466         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names), rend, "text", 1);
467         gtk_widget_set_sensitive(names, false);
468         gtk_box_pack_start(GTK_BOX(hbox), names, false, false, 3);
469         g_signal_connect(G_OBJECT(blocks), "changed",
470             G_CALLBACK(blocks_on_change), NULL);
471         g_signal_connect(G_OBJECT(names), "changed",
472             G_CALLBACK(names_on_change), NULL);
473         
474         w = gtk_hseparator_new();
475         gtk_box_pack_start(GTK_BOX(dialog_vbox), w, true, false, 3);
476         controls = gtk_vbox_new(false, 10);
477         gtk_widget_set_sensitive(controls, false);
478         gtk_box_pack_start(GTK_BOX(dialog_vbox), controls, true, true, 0);
479         vbox = gtk_vbox_new(false, 3);
480         gtk_box_pack_start(GTK_BOX(controls), vbox, false, false, 0);
481         autoconf = gtk_check_button_new_with_label(
482                 _("Automatically configure empty options"));
483         gtk_box_pack_start(GTK_BOX(vbox), autoconf, false, false, 3);
484         table = gtk_table_new(6, 2, false);
485         gtk_box_pack_start(GTK_BOX(controls), table, false, false, 0);
486
487 #define attach_label(a, b, c, d, e)                                           \
488         do {                                                                  \
489                 gtk_misc_set_alignment(GTK_MISC(a), 0.0, 0.5);                \
490                 gtk_table_attach(GTK_TABLE(table), a, b, c, d, e,             \
491                     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3);      \
492         } while (0)
493 #define attach_entry(a, b, c, d, e)                                           \
494         gtk_table_attach(GTK_TABLE(table), a, b, c, d, e,                     \
495             GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3);
496         
497         w = gtk_label_new(_("IP Address:"));
498         address = gtk_entry_new();
499         gtk_entry_set_max_length(GTK_ENTRY(address), 15);
500         g_signal_connect(G_OBJECT(address), "focus-out-event",
501             G_CALLBACK(address_lost_focus), NULL);
502         attach_label(w, 0, 1, 0, 1);
503         attach_entry(address, 1, 2, 0, 1);
504
505         w = gtk_label_new(_("Router:"));
506         router = gtk_entry_new();
507         gtk_entry_set_max_length(GTK_ENTRY(router), 12);
508         g_signal_connect(G_OBJECT(router), "focus-out-event",
509             G_CALLBACK(entry_lost_focus), NULL);
510         attach_label(w, 0, 1, 2, 3);
511         attach_entry(router, 1, 2, 2, 3);
512
513         w = gtk_label_new(_("DNS Servers:"));
514         dns_servers = gtk_entry_new();
515         g_signal_connect(G_OBJECT(dns_servers), "focus-out-event",
516             G_CALLBACK(entry_lost_focus), NULL);
517         attach_label(w, 0, 1, 3, 4);
518         attach_entry(dns_servers, 1, 2, 3, 4);
519
520         w = gtk_label_new(_("DNS Search:"));
521         dns_search = gtk_entry_new();
522         attach_label(w, 0, 1, 4, 5);
523         attach_entry(dns_search, 1, 2, 4, 5);
524
525         hbox = gtk_hbox_new(false, 10);
526         gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, true, true, 3);
527         clear = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
528         gtk_widget_set_sensitive(clear, false);
529         gtk_box_pack_start(GTK_BOX(hbox), clear, false, false, 0);
530         g_signal_connect(G_OBJECT(clear), "clicked", on_clear, NULL);
531         rebind = gtk_button_new_with_mnemonic(_("_Rebind"));
532         gtk_widget_set_sensitive(rebind, false);
533         w = gtk_image_new_from_stock(GTK_STOCK_EXECUTE,
534             GTK_ICON_SIZE_BUTTON);
535         gtk_button_set_image(GTK_BUTTON(rebind), w);
536         gtk_box_pack_start(GTK_BOX(hbox), rebind, false, false, 0);
537         g_signal_connect(G_OBJECT(rebind), "clicked", on_rebind, NULL);
538         w = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
539         gtk_box_pack_end(GTK_BOX(hbox), w, false, false, 0);
540         g_signal_connect(G_OBJECT(w), "clicked",
541             dhcpcd_prefs_close, NULL);
542         
543         blocks_on_change(blocks);
544         show_config(NULL);
545         gtk_widget_show_all(dialog);
546 }