3 * Copyright 2009-2014 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/types.h>
28 #include <arpa/inet.h>
33 #include "dhcpcd-gtk.h"
35 static GtkWidget *dialog, *blocks, *names, *controls, *clear, *rebind;
36 static GtkWidget *autoconf, *address, *router, *dns_servers, *dns_search;
37 static DHCPCD_OPTION *config;
38 static char *block, *name;
39 static DHCPCD_IF *iface;
43 config_err_dialog(DHCPCD_CONNECTION *con, bool writing, const char *txt)
48 t = g_strconcat(_(writing ? "Error saving" : "Error reading"), " ",
49 dhcpcd_cffile(con), "\n\n", txt, NULL);
50 edialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
51 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", t);
52 gtk_window_set_title(GTK_WINDOW(edialog), _("Config error"));
53 gtk_dialog_run(GTK_DIALOG(edialog));
54 gtk_widget_destroy(edialog);
59 show_config(DHCPCD_OPTION *conf)
64 if ((val = dhcpcd_config_get_static(conf, "ip_address=")) != NULL)
67 if ((val = dhcpcd_config_get(conf, "inform")) == NULL &&
68 (iface && iface->flags & IFF_POINTOPOINT))
73 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autoconf), autocnf);
74 gtk_entry_set_text(GTK_ENTRY(address), val ? val : "");
75 val = dhcpcd_config_get_static(conf, "routers=");
76 gtk_entry_set_text(GTK_ENTRY(router), val ? val : "");
77 val = dhcpcd_config_get_static(conf, "domain_name_servers=");
78 gtk_entry_set_text(GTK_ENTRY(dns_servers), val ? val : "");
79 val = dhcpcd_config_get_static(conf, "domain_search=");
80 gtk_entry_set_text(GTK_ENTRY(dns_search), val ? val : "");
84 combo_active_text(GtkWidget *widget)
91 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)));
92 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
94 memset(&val, 0, sizeof(val));
95 gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, 1, &val);
96 text = g_strdup(g_value_get_string(&val));
102 set_option(DHCPCD_OPTION **conf, bool s, const char *opt, const char *val,
107 if (!dhcpcd_config_set_static(conf, opt, val))
108 g_critical("dhcpcd_config_set_static: %s",
113 if (!dhcpcd_config_set(conf, opt, val))
114 g_critical("dhcpcd_config_set: %s",
126 make_config(DHCPCD_OPTION **conf)
128 const char *val, ns[] = "";
132 a = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autoconf));
133 if (iface && iface->flags & IFF_POINTOPOINT)
134 set_option(conf, true, "ip_address=", a ? NULL : ns, &ret);
136 val = gtk_entry_get_text(GTK_ENTRY(address));
139 set_option(conf, false, "inform", a ? val : NULL, &ret);
140 set_option(conf, true, "ip_address=", a ? NULL : val, &ret);
143 val = gtk_entry_get_text(GTK_ENTRY(router));
144 if (a && *val == '\0')
146 set_option(conf, true, "routers=", val, &ret);
148 val = gtk_entry_get_text(GTK_ENTRY(dns_servers));
149 if (a && *val == '\0')
151 set_option(conf, true, "domain_name_servers=", val, &ret);
153 val = gtk_entry_get_text(GTK_ENTRY(dns_search));
154 if (a && *val == '\0')
156 set_option(conf, true, "domain_search=", val, &ret);
162 write_config(DHCPCD_CONNECTION *con, DHCPCD_OPTION **conf)
165 if (make_config(conf) &&
166 !dhcpcd_config_write(con, block, name, *conf))
171 g_warning("dhcpcd_config_write: %s", s);
172 config_err_dialog(con, true, s);
179 load_icon(const char *iname)
183 if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height))
185 return gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
186 iname, MIN(width, height), 0, NULL);
190 set_name_active_icon(const char *iname)
197 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
198 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(names), &iter))
200 pb = load_icon(iname);
202 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
203 gtk_list_store_set(store, &iter, 0, pb, -1);
205 gtk_tree_model_row_changed(GTK_TREE_MODEL(store), path, &iter);
206 gtk_tree_path_free(path);
211 list_interfaces(DHCPCD_CONNECTION *con)
217 dhcpcd_freev(ifaces);
218 ifaces = dhcpcd_interface_names_sorted(con);
219 for (i = ifaces; i && *i; i++)
220 list = g_slist_append(list, *i);
225 list_ssids(WI_SCANS *scans)
232 TAILQ_FOREACH(w, scans, next) {
233 for (wis = w->scans; wis; wis = wis->next) {
234 for (l = list; l; l = l->next)
235 if (g_strcmp0((const char *)l->data,
239 list = g_slist_append(list, wis->ssid);
246 blocks_on_change(GtkWidget *widget, gpointer data)
248 DHCPCD_CONNECTION *con;
252 const char *iname, *nn;
253 GSList *l, *new_names;
257 con = (DHCPCD_CONNECTION *)data;
259 write_config(con, &config);
260 dhcpcd_config_free(config);
267 block = combo_active_text(widget);
268 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
269 gtk_list_store_clear(store);
270 list = dhcpcd_config_blocks(con, block);
272 if (g_strcmp0(block, "interface") == 0)
273 new_names = list_interfaces(con);
275 new_names = list_ssids(&wi_scans);
278 for (l = new_names; l; l = l->next) {
279 nn = (const char *)l->data;
281 for (lp = list; *lp; lp++)
282 if (g_strcmp0(nn, *lp) == 0)
285 iname = "document-save";
287 iname = "document-new";
289 iname = "document-new";
290 pb = load_icon(iname);
291 gtk_list_store_append(store, &iter);
292 gtk_list_store_set(store, &iter, 0, pb, 1, nn, -1);
297 for (lp = list; lp && *lp; lp++) {
298 for (l = new_names; l; l = l->next)
299 if (g_strcmp0((const char *)l->data, *lp) == 0)
303 pb = load_icon("document-save");
304 gtk_list_store_append(store, &iter);
305 gtk_list_store_set(store, &iter, 0, pb, 1, *lp, -1);
309 gtk_widget_set_sensitive(names, n);
310 g_slist_free(new_names);
315 names_on_change(_unused GtkWidget *widget, gpointer data)
317 DHCPCD_CONNECTION *con;
320 con = (DHCPCD_CONNECTION *)data;
322 write_config(con, &config);
325 name = combo_active_text(names);
326 dhcpcd_config_free(config);
328 if (g_strcmp0(block, "interface") == 0) {
329 for (i = dhcpcd_interfaces(con); i; i = i->next)
330 if (g_strcmp0(name, i->ifname) == 0) {
335 gtk_widget_set_sensitive(address,
336 !iface || (iface->flags & IFF_POINTOPOINT) == 0);
339 config = dhcpcd_config_read(con, block, name);
340 if (config == NULL && errno) {
344 g_warning("dhcpcd_config_read: %s", s);
345 config_err_dialog(con, false, s);
350 gtk_widget_set_sensitive(controls, name ? true : false);
351 gtk_widget_set_sensitive(clear, name ? true : false);
352 gtk_widget_set_sensitive(rebind, name ? true : false);
356 valid_address(const char *val, bool allow_cidr)
363 addr = g_strdup(val);
365 p = strchr(addr, '/');
370 cidr = g_ascii_strtoll(p, &e, 10);
371 if (cidr < 0 || cidr > 32 ||
372 errno != 0 || *e != '\0')
379 retval = inet_aton(addr, &in) == 0 ? false : true;
387 address_lost_focus(GtkEntry *entry)
391 val = gtk_entry_get_text(entry);
392 if (*val != '\0' && !valid_address(val, true))
393 gtk_entry_set_text(entry, "");
398 entry_lost_focus(GtkEntry *entry)
403 val = gtk_entry_get_text(entry);
404 a = g_strsplit(val, " ", 0);
405 for (p = a; *p; p++) {
406 if (**p != '\0' && !valid_address(*p, false)) {
407 gtk_entry_set_text(entry, "");
416 on_clear(_unused GtkWidget *o, gpointer data)
418 DHCPCD_CONNECTION *con;
420 con = (DHCPCD_CONNECTION *)data;
421 dhcpcd_config_free(config);
423 if (dhcpcd_config_write(con, block, name, config)) {
424 set_name_active_icon("document-new");
427 g_critical("dhcpcd_config_write: %s", strerror(errno));
431 on_rebind(_unused GObject *widget, gpointer data)
433 DHCPCD_CONNECTION *con;
436 con = (DHCPCD_CONNECTION *)data;
437 if (write_config(con, &config)) {
438 set_name_active_icon(config == NULL ?
439 "document-new" : "document-save");
441 if (g_strcmp0(block, "interface") == 0) {
442 if (dhcpcd_rebind(con, iface->ifname) == -1)
443 g_critical("dhcpcd_rebind %s: %s",
444 iface->ifname, strerror(errno));
446 for (i = dhcpcd_interfaces(con); i; i = i->next) {
447 if (g_strcmp0(i->ssid, name) == 0) {
448 if (dhcpcd_rebind(con, i->ifname) == -1)
450 "dhcpcd_rebind %s: %s",
460 on_destroy(_unused GObject *o, gpointer data)
464 write_config((DHCPCD_CONNECTION *)data, &config);
469 dhcpcd_config_free(config);
471 dhcpcd_freev(ifaces);
480 if (dialog != NULL) {
481 gtk_widget_destroy(dialog);
494 #if GTK_MAJOR_VERSION == 2
496 gtk_separator_new(GtkOrientation o)
499 if (o == GTK_ORIENTATION_HORIZONTAL)
500 return gtk_hseparator_new();
502 return gtk_vseparator_new();
507 prefs_show(DHCPCD_CONNECTION *con)
509 GtkWidget *dialog_vbox, *hbox, *vbox, *table, *w;
512 GtkCellRenderer *rend;
516 gtk_window_present(GTK_WINDOW(dialog));
520 if (g_strcmp0(dhcpcd_status(con), "down") == 0)
523 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
524 g_signal_connect(G_OBJECT(dialog), "destroy",
525 G_CALLBACK(on_destroy), con);
527 gtk_window_set_title(GTK_WINDOW(dialog), _("Network Preferences"));
528 gtk_window_set_resizable(GTK_WINDOW(dialog), false);
529 gtk_window_set_icon_name(GTK_WINDOW(dialog),
530 "preferences-system-network");
531 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
532 gtk_window_set_type_hint(GTK_WINDOW(dialog),
533 GDK_WINDOW_TYPE_HINT_DIALOG);
535 dialog_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
536 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
537 gtk_container_add(GTK_CONTAINER(dialog), dialog_vbox);
539 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
540 gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, false, false, 3);
541 w = gtk_label_new("Configure:");
542 gtk_box_pack_start(GTK_BOX(hbox), w, false, false, 3);
543 store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
544 pb = load_icon("network-wired");
545 gtk_list_store_append(store, &iter);
546 gtk_list_store_set(store, &iter, 0, pb, 1, "interface", -1);
548 pb = load_icon("network-wireless");
549 gtk_list_store_append(store, &iter);
550 gtk_list_store_set(store, &iter, 0, pb, 1, "SSID", -1);
552 blocks = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
553 rend = gtk_cell_renderer_pixbuf_new();
554 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, false);
555 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
557 rend = gtk_cell_renderer_text_new();
558 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, true);
559 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
561 gtk_combo_box_set_active(GTK_COMBO_BOX(blocks), 0);
562 gtk_box_pack_start(GTK_BOX(hbox), blocks, false, false, 3);
563 store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
564 names = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
565 rend = gtk_cell_renderer_pixbuf_new();
566 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, false);
567 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names),
569 rend = gtk_cell_renderer_text_new();
570 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, true);
571 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names), rend, "text", 1);
572 gtk_widget_set_sensitive(names, false);
573 gtk_box_pack_start(GTK_BOX(hbox), names, false, false, 3);
574 g_signal_connect(G_OBJECT(blocks), "changed",
575 G_CALLBACK(blocks_on_change), con);
576 g_signal_connect(G_OBJECT(names), "changed",
577 G_CALLBACK(names_on_change), con);
579 w = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
580 gtk_box_pack_start(GTK_BOX(dialog_vbox), w, true, false, 3);
581 controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
582 gtk_widget_set_sensitive(controls, false);
583 gtk_box_pack_start(GTK_BOX(dialog_vbox), controls, true, true, 0);
584 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
585 gtk_box_pack_start(GTK_BOX(controls), vbox, false, false, 0);
586 autoconf = gtk_check_button_new_with_label(
587 _("Automatically configure empty options"));
588 gtk_box_pack_start(GTK_BOX(vbox), autoconf, false, false, 3);
589 table = gtk_table_new(6, 2, false);
590 gtk_box_pack_start(GTK_BOX(controls), table, false, false, 0);
592 #define attach_label(a, b, c, d, e) \
594 gtk_misc_set_alignment(GTK_MISC(a), 0.0, 0.5); \
595 gtk_table_attach(GTK_TABLE(table), a, b, c, d, e, \
596 GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3); \
598 #define attach_entry(a, b, c, d, e) \
599 gtk_table_attach(GTK_TABLE(table), a, b, c, d, e, \
600 GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3);
602 w = gtk_label_new(_("IP Address:"));
603 address = gtk_entry_new();
604 gtk_entry_set_max_length(GTK_ENTRY(address), 18);
605 g_signal_connect(G_OBJECT(address), "focus-out-event",
606 G_CALLBACK(address_lost_focus), NULL);
607 attach_label(w, 0, 1, 0, 1);
608 attach_entry(address, 1, 2, 0, 1);
610 w = gtk_label_new(_("Router:"));
611 router = gtk_entry_new();
612 gtk_entry_set_max_length(GTK_ENTRY(router), 15);
613 g_signal_connect(G_OBJECT(router), "focus-out-event",
614 G_CALLBACK(entry_lost_focus), NULL);
615 attach_label(w, 0, 1, 2, 3);
616 attach_entry(router, 1, 2, 2, 3);
618 w = gtk_label_new(_("DNS Servers:"));
619 dns_servers = gtk_entry_new();
620 g_signal_connect(G_OBJECT(dns_servers), "focus-out-event",
621 G_CALLBACK(entry_lost_focus), NULL);
622 attach_label(w, 0, 1, 3, 4);
623 attach_entry(dns_servers, 1, 2, 3, 4);
625 w = gtk_label_new(_("DNS Search:"));
626 dns_search = gtk_entry_new();
627 attach_label(w, 0, 1, 4, 5);
628 attach_entry(dns_search, 1, 2, 4, 5);
630 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
631 gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, true, true, 3);
632 clear = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
633 gtk_widget_set_sensitive(clear, false);
634 gtk_box_pack_start(GTK_BOX(hbox), clear, false, false, 0);
635 g_signal_connect(G_OBJECT(clear), "clicked",
636 G_CALLBACK(on_clear), con);
637 rebind = gtk_button_new_with_mnemonic(_("_Rebind"));
638 gtk_widget_set_sensitive(rebind, false);
639 w = gtk_image_new_from_stock(GTK_STOCK_EXECUTE,
640 GTK_ICON_SIZE_BUTTON);
641 gtk_button_set_image(GTK_BUTTON(rebind), w);
642 gtk_box_pack_start(GTK_BOX(hbox), rebind, false, false, 0);
643 g_signal_connect(G_OBJECT(rebind), "clicked",
644 G_CALLBACK(on_rebind), con);
645 w = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
646 gtk_box_pack_end(GTK_BOX(hbox), w, false, false, 0);
647 g_signal_connect(G_OBJECT(w), "clicked",
648 G_CALLBACK(prefs_close), NULL);
650 blocks_on_change(blocks, con);
652 gtk_widget_show_all(dialog);