/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2013 Red Hat, Inc. */ /** * SECTION:nm-editor-bindings * @short_description: #GBinding-based NM connection editor helpers * * nm-editor-bindings contains helper functions to bind NMSettings objects * to connection editing widgets. The goal is that this should eventually be * shared between nmtui, nm-connection-editor, and gnome-control-center. */ #include "libnm/nm-default-client.h" #include "nm-editor-bindings.h" #include #include #include static void value_transform_string_int(const GValue *src_value, GValue *dest_value) { long val; char *end; val = strtol(g_value_get_string(src_value), &end, 10); if (val < G_MININT || val > G_MAXINT || *end) return; g_value_set_int(dest_value, (int) val); } static void value_transform_string_uint(const GValue *src_value, GValue *dest_value) { long val; char *end; val = strtol(g_value_get_string(src_value), &end, 10); if (val < 0 || val > G_MAXUINT || *end) return; g_value_set_uint(dest_value, (int) val); } void nm_editor_bindings_init(void) { /* glib registers number -> string, but not string -> number */ g_value_register_transform_func(G_TYPE_STRING, G_TYPE_INT, value_transform_string_int); g_value_register_transform_func(G_TYPE_STRING, G_TYPE_UINT, value_transform_string_uint); } static gboolean ip_addresses_with_prefix_to_strv(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { GPtrArray * addrs; NMIPAddress *addr; const char * addrstr; guint32 prefix; char ** strings; int i; addrs = g_value_get_boxed(source_value); strings = g_new0(char *, addrs->len + 1); for (i = 0; i < addrs->len; i++) { addr = addrs->pdata[i]; addrstr = nm_ip_address_get_address(addr); prefix = nm_ip_address_get_prefix(addr); if (addrstr) strings[i] = g_strdup_printf("%s/%d", addrstr, (int) prefix); else strings[i] = g_strdup(""); } g_value_take_boxed(target_value, strings); return TRUE; } static gboolean ip_addresses_with_prefix_from_strv(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { int addr_family = GPOINTER_TO_INT(user_data); char ** strings; GPtrArray * addrs; NMIPAddress *addr; char * addrstr; int prefix; int i; strings = g_value_get_boxed(source_value); /* Fetch the original property value, so as to preserve their extra attributes */ g_object_get(g_binding_get_source(binding), g_binding_get_source_property(binding), &addrs, NULL); for (i = 0; strings[i]; i++) { if (i >= addrs->len) { if (addr_family == AF_INET) addr = nm_ip_address_new(AF_INET, "0.0.0.0", 32, NULL); else addr = nm_ip_address_new(AF_INET6, "::", 128, NULL); g_ptr_array_add(addrs, addr); } else addr = addrs->pdata[i]; if (!nm_utils_parse_inaddr_prefix(addr_family, strings[i], &addrstr, &prefix)) { g_ptr_array_unref(addrs); return FALSE; } if (prefix == -1) { if (addr_family == AF_INET) { in_addr_t v4; inet_pton(addr_family, addrstr, &v4); if (nm_utils_ip_is_site_local(AF_INET, &v4)) prefix = nm_utils_ip4_get_default_prefix(v4); else prefix = 32; } else prefix = 64; } nm_ip_address_set_address(addr, addrstr); nm_ip_address_set_prefix(addr, prefix); g_free(addrstr); } g_ptr_array_set_size(addrs, i); g_value_take_boxed(target_value, addrs); return TRUE; } /** * nm_editor_bind_ip_addresses_with_prefix_to_strv: * @addr_family: the IP address family * @source: the source object (eg, an #NMSettingIP4Config) * @source_property: the property on @source to bind (eg, * %NM_SETTING_IP4_CONFIG_ADDRESSES) * @target: the target object (eg, an #NmtAddressList) * @target_property: the property on @target to bind * (eg, "strings") * @flags: %GBindingFlags * * Binds the #GPtrArray-of-#NMIPAddress property @source_property on @source to * the %G_TYPE_STRV property @target_property on @target. * * Each #NMIPAddress in @source_property will be converted to a string of the * form "ip.ad.dr.ess/prefix" or "ip:ad:dr:ess/prefix" in @target_property (and * vice versa if %G_BINDING_BIDIRECTIONAL) is specified. */ void nm_editor_bind_ip_addresses_with_prefix_to_strv(int addr_family, gpointer source, const char * source_property, gpointer target, const char * target_property, GBindingFlags flags) { g_object_bind_property_full(source, source_property, target, target_property, flags, ip_addresses_with_prefix_to_strv, ip_addresses_with_prefix_from_strv, GINT_TO_POINTER(addr_family), NULL); } static gboolean ip_addresses_check_and_copy(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { int addr_family = GPOINTER_TO_INT(user_data); char **strings; int i; strings = g_value_get_boxed(source_value); for (i = 0; strings[i]; i++) { if (!nm_utils_ipaddr_is_valid(addr_family, strings[i])) return FALSE; } g_value_set_boxed(target_value, strings); return TRUE; } /** * nm_editor_bind_ip_addresses_to_strv: * @addr_family: the IP address family * @source: the source object (eg, an #NMSettingIP4Config) * @source_property: the property on @source to bind (eg, * %NM_SETTING_IP4_CONFIG_DNS) * @target: the target object (eg, an #NmtAddressList) * @target_property: the property on @target to bind * (eg, "strings") * @flags: %GBindingFlags * * Binds the %G_TYPE_STRV property @source_property on @source to the * %G_TYPE_STRV property @target_property on @target, verifying that * each string is a valid address of type @addr_family when copying. */ void nm_editor_bind_ip_addresses_to_strv(int addr_family, gpointer source, const char * source_property, gpointer target, const char * target_property, GBindingFlags flags) { g_object_bind_property_full(source, source_property, target, target_property, flags, ip_addresses_check_and_copy, ip_addresses_check_and_copy, GINT_TO_POINTER(addr_family), NULL); } static gboolean ip_gateway_to_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { g_value_set_string(target_value, g_value_get_string(source_value)); return TRUE; } static gboolean ip_gateway_from_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { int addr_family = GPOINTER_TO_INT(user_data); const char *gateway; gateway = g_value_get_string(source_value); if (gateway && !nm_utils_ipaddr_is_valid(addr_family, gateway)) gateway = NULL; g_value_set_string(target_value, gateway); return TRUE; } static gboolean ip_addresses_to_gateway(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { GPtrArray *addrs; addrs = g_value_get_boxed(source_value); if (addrs->len == 0) { g_value_set_string(target_value, NULL); return TRUE; } else return FALSE; } static gboolean ip_addresses_to_sensitivity(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { GPtrArray *addrs; addrs = g_value_get_boxed(source_value); g_value_set_boolean(target_value, addrs->len != 0); return TRUE; } /** * nm_editor_bind_ip_gateway_to_string: * @addr_family: the IP address family * @source: the source #NMSettingIPConfig * @target: the target object (eg, an #NmtIPEntry) * @target_property: the property on @target to bind (eg, "text") * @target_sensitive_property: the "sensitivity" property on @target to bind * @flags: %GBindingFlags * * Binds the #NMSettingIPConfig:gateway property on @source to the * %G_TYPE_STRING property @target_property and %G_TYPE_BOOLEAN property * @target_sensitive_property on @target, also taking the * #NMSettingIPConfig:addresses property on @source into account. * * In particular, if @source has no static IP addresses, then @target_property * will be set to "" and @target_sensitive_property will be set to %FALSE. * * If @source has at least one static IP address, then * @target_sensitive_property will be set to %TRUE, @target_property will be * initialized from @source's #NMSettingIPConfig:gateway, and @source will be * updated with the value of @target_property whenever it contains a valid IP * address. */ void nm_editor_bind_ip_gateway_to_string(int addr_family, NMSettingIPConfig *source, gpointer target, const char * target_property, const char * target_sensitive_property, GBindingFlags flags) { g_object_bind_property_full(source, "gateway", target, target_property, flags, ip_gateway_to_string, ip_gateway_from_string, GINT_TO_POINTER(addr_family), NULL); g_object_bind_property_full(source, "addresses", source, "gateway", (flags & G_BINDING_SYNC_CREATE), ip_addresses_to_gateway, NULL, NULL, NULL); g_object_bind_property_full(source, "addresses", target, target_sensitive_property, (flags & G_BINDING_SYNC_CREATE), ip_addresses_to_sensitivity, NULL, NULL, NULL); } static gboolean ip_route_transform_to_dest_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NMIPRoute * route; const char *addrstr; char * string; route = g_value_get_boxed(source_value); if (route) addrstr = nm_ip_route_get_dest(route); else addrstr = NULL; if (addrstr) { string = g_strdup_printf("%s/%d", addrstr, (int) nm_ip_route_get_prefix(route)); g_value_take_string(target_value, string); } else g_value_set_string(target_value, ""); return TRUE; } static gboolean ip_route_transform_to_next_hop_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NMIPRoute * route; const char *addrstr; route = g_value_get_boxed(source_value); if (route) { addrstr = nm_ip_route_get_next_hop(route); if (!addrstr) addrstr = ""; } else addrstr = ""; g_value_set_string(target_value, addrstr); return TRUE; } static gboolean ip_route_transform_to_metric_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NMIPRoute *route; char * string; route = g_value_get_boxed(source_value); if (route && nm_ip_route_get_dest(route) && nm_ip_route_get_metric(route) != -1) { string = g_strdup_printf("%lu", (gulong) nm_ip_route_get_metric(route)); g_value_take_string(target_value, string); } else g_value_set_string(target_value, ""); return TRUE; } static gboolean ip_route_transform_from_dest_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { int addr_family = GPOINTER_TO_INT(user_data); NMIPRoute * route; const char *text; char * addrstr; int prefix; text = g_value_get_string(source_value); if (!nm_utils_parse_inaddr_prefix(addr_family, text, &addrstr, &prefix)) return FALSE; /* Fetch the original property value */ g_object_get(g_binding_get_source(binding), g_binding_get_source_property(binding), &route, NULL); if (prefix == -1) { if (addr_family == AF_INET) { in_addr_t v4; inet_pton(addr_family, addrstr, &v4); if (nm_utils_ip_is_site_local(AF_INET, &v4)) { prefix = nm_utils_ip4_get_default_prefix(v4); if (v4 & (~nm_utils_ip4_prefix_to_netmask(prefix))) prefix = 32; } else prefix = 32; } else prefix = 64; } nm_ip_route_set_dest(route, addrstr); nm_ip_route_set_prefix(route, prefix); g_free(addrstr); g_value_take_boxed(target_value, route); return TRUE; } static gboolean ip_route_transform_from_next_hop_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { int addr_family = GPOINTER_TO_INT(user_data); NMIPRoute * route; const char *text; text = g_value_get_string(source_value); if (*text) { if (!nm_utils_ipaddr_is_valid(addr_family, text)) return FALSE; } else text = NULL; /* Fetch the original property value */ g_object_get(g_binding_get_source(binding), g_binding_get_source_property(binding), &route, NULL); nm_ip_route_set_next_hop(route, text); g_value_take_boxed(target_value, route); return TRUE; } static gboolean ip_route_transform_from_metric_string(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NMIPRoute * route; const char *text; gint64 metric; text = g_value_get_string(source_value); metric = _nm_utils_ascii_str_to_int64(text, 10, 0, G_MAXUINT32, -1); /* Fetch the original property value */ g_object_get(g_binding_get_source(binding), g_binding_get_source_property(binding), &route, NULL); nm_ip_route_set_metric(route, metric); g_value_take_boxed(target_value, route); return TRUE; } /** * nm_editor_bind_ip_route_to_strings: * @addr_family: the IP address family * @source: the source object * @source_property: the source property * @dest_target: the target object for the route's destination * @dest_target_property: the property on @dest_target * @next_hop_target: the target object for the route's next hop * @next_hop_target_property: the property on @next_hop_target * @metric_target: the target object for the route's metric * @metric_target_property: the property on @metric_target * @flags: %GBindingFlags * * Binds the #NMIPRoute-valued property @source_property on @source to the * three indicated string-valued target properties (and vice versa if * %G_BINDING_BIDIRECTIONAL is specified). * * @dest_target_property should be an "address/prefix" string, as with * nm_editor_bind_ip4_addresses_with_prefix_to_strv(). @next_hop_target_property * is a plain IP address, and @metric_target_property is a number. */ void nm_editor_bind_ip_route_to_strings(int addr_family, gpointer source, const char * source_property, gpointer dest_target, const char * dest_target_property, gpointer next_hop_target, const char * next_hop_target_property, gpointer metric_target, const char * metric_target_property, GBindingFlags flags) { g_object_bind_property_full(source, source_property, dest_target, dest_target_property, flags, ip_route_transform_to_dest_string, ip_route_transform_from_dest_string, GINT_TO_POINTER(addr_family), NULL); g_object_bind_property_full(source, source_property, next_hop_target, next_hop_target_property, flags, ip_route_transform_to_next_hop_string, ip_route_transform_from_next_hop_string, GINT_TO_POINTER(addr_family), NULL); g_object_bind_property_full(source, source_property, metric_target, metric_target_property, flags, ip_route_transform_to_metric_string, ip_route_transform_from_metric_string, GINT_TO_POINTER(addr_family), NULL); } /* Wireless security method binding */ typedef struct { NMConnection * connection; NMSettingWirelessSecurity *s_wsec; gboolean s_wsec_in_use; GObject *target; char * target_property; gboolean updating; } NMEditorWirelessSecurityMethodBinding; static const char * get_security_type(NMEditorWirelessSecurityMethodBinding *binding) { const char *key_mgmt, *auth_alg; if (!binding->s_wsec_in_use) return "none"; key_mgmt = nm_setting_wireless_security_get_key_mgmt(binding->s_wsec); auth_alg = nm_setting_wireless_security_get_auth_alg(binding->s_wsec); /* No IEEE 802.1x */ if (!strcmp(key_mgmt, "none")) { NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type(binding->s_wsec); if (wep_type == NM_WEP_KEY_TYPE_KEY) return "wep-key"; else return "wep-passphrase"; } if (!strcmp(key_mgmt, "ieee8021x")) { if (auth_alg && !strcmp(auth_alg, "leap")) return "leap"; return "dynamic-wep"; } if (!strcmp(key_mgmt, "wpa-psk")) return "wpa-personal"; if (!strcmp(key_mgmt, "sae")) return "wpa3-personal"; if (!strcmp(key_mgmt, "owe")) return "owe"; if (!strcmp(key_mgmt, "wpa-eap")) return "wpa-enterprise"; if (!strcmp(key_mgmt, "wpa-eap-suite-b-192")) return "wpa3-enterprise-suite-b-192"; return NULL; } static void wireless_security_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMEditorWirelessSecurityMethodBinding *binding = user_data; if (binding->updating) return; binding->updating = TRUE; g_object_set(binding->target, binding->target_property, get_security_type(binding), NULL); binding->updating = FALSE; } static void wireless_connection_changed(NMConnection *connection, gpointer user_data) { NMEditorWirelessSecurityMethodBinding *binding = user_data; NMSettingWirelessSecurity * s_wsec; if (binding->updating) return; s_wsec = nm_connection_get_setting_wireless_security(connection); if ((s_wsec && binding->s_wsec_in_use) || (!s_wsec && !binding->s_wsec_in_use)) return; binding->s_wsec_in_use = !binding->s_wsec_in_use; wireless_security_changed(NULL, NULL, binding); } static void wireless_security_target_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMEditorWirelessSecurityMethodBinding *binding = user_data; char * method; if (binding->updating) return; g_object_get(binding->target, binding->target_property, &method, NULL); binding->updating = TRUE; if (!strcmp(method, "none")) { if (!binding->s_wsec_in_use) return; binding->s_wsec_in_use = FALSE; nm_connection_remove_setting(binding->connection, NM_TYPE_SETTING_WIRELESS_SECURITY); binding->updating = FALSE; return; } if (!binding->s_wsec_in_use) { binding->s_wsec_in_use = TRUE; nm_connection_add_setting(binding->connection, NM_SETTING(binding->s_wsec)); } if (!strcmp(method, "wep-key")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, NULL); } else if (!strcmp(method, "wep-passphrase")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_PASSPHRASE, NULL); } else if (!strcmp(method, "leap")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else if (!strcmp(method, "dynamic-wep")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else if (!strcmp(method, "wpa-personal")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else if (!strcmp(method, "wpa3-personal")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else if (!strcmp(method, "owe")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else if (!strcmp(method, "wpa-enterprise")) { g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); } else g_warn_if_reached(); binding->updating = FALSE; } static void wireless_security_target_destroyed(gpointer user_data, GObject *ex_target) { NMEditorWirelessSecurityMethodBinding *binding = user_data; g_signal_handlers_disconnect_by_func(binding->s_wsec, G_CALLBACK(wireless_security_changed), binding); g_object_unref(binding->s_wsec); g_object_unref(binding->connection); g_free(binding->target_property); g_slice_free(NMEditorWirelessSecurityMethodBinding, binding); } /** * nm_editor_bind_wireless_security_method: * @connection: an #NMConnection * @s_wsec: an #NMSettingWirelessSecurity * @target: the target widget * @target_property: the string-valued property on @target to bind * @flags: %GBindingFlags * * Binds the wireless security method on @connection to * @target_property on @target (and vice versa if * %G_BINDING_BIDIRECTIONAL). * * @target_property will be of the values "none", "wpa-personal", * "wpa-enterprise", "wep-key", "wep-passphrase", "dynamic-wep", or * "leap". * * If binding bidirectionally, @s_wsec will be automatically added to * or removed from @connection as needed when @target_property * changes. */ void nm_editor_bind_wireless_security_method(NMConnection * connection, NMSettingWirelessSecurity *s_wsec, gpointer target, const char * target_property, GBindingFlags flags) { NMEditorWirelessSecurityMethodBinding *binding; char * notify; binding = g_slice_new0(NMEditorWirelessSecurityMethodBinding); binding->target = target; binding->target_property = g_strdup(target_property); if (flags & G_BINDING_BIDIRECTIONAL) { notify = g_strdup_printf("notify::%s", target_property); g_signal_connect(target, notify, G_CALLBACK(wireless_security_target_changed), binding); g_free(notify); } g_object_weak_ref(target, wireless_security_target_destroyed, binding); binding->connection = g_object_ref(connection); g_signal_connect(connection, NM_CONNECTION_CHANGED, G_CALLBACK(wireless_connection_changed), binding); binding->s_wsec_in_use = (nm_connection_get_setting_wireless_security(connection) != NULL); binding->s_wsec = g_object_ref(s_wsec); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, G_CALLBACK(wireless_security_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, G_CALLBACK(wireless_security_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, G_CALLBACK(wireless_security_changed), binding); if (flags & G_BINDING_SYNC_CREATE) wireless_security_changed(NULL, NULL, binding); } /* WEP key binding */ typedef struct { NMSettingWirelessSecurity *s_wsec; GObject * entry, *key_selector; char * entry_property, *key_selector_property; gboolean updating; } NMEditorWepKeyBinding; static void wep_key_setting_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMEditorWepKeyBinding *binding = user_data; const char * key; int index; if (binding->updating) return; index = nm_setting_wireless_security_get_wep_tx_keyidx(binding->s_wsec); key = nm_setting_wireless_security_get_wep_key(binding->s_wsec, index); binding->updating = TRUE; g_object_set(binding->key_selector, binding->key_selector_property, index, NULL); g_object_set(binding->entry, binding->entry_property, key, NULL); binding->updating = FALSE; } static void wep_key_ui_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMEditorWepKeyBinding *binding = user_data; char * key; int index; if (binding->updating) return; g_object_get(binding->key_selector, binding->key_selector_property, &index, NULL); g_object_get(binding->entry, binding->entry_property, &key, NULL); binding->updating = TRUE; g_object_set(binding->s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, index, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, index == 0 ? key : NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, index == 1 ? key : NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, index == 2 ? key : NULL, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, index == 3 ? key : NULL, NULL); binding->updating = FALSE; g_free(key); } static void wep_key_target_destroyed(gpointer user_data, GObject *ex_target) { NMEditorWepKeyBinding *binding = user_data; g_signal_handlers_disconnect_by_func(binding->s_wsec, G_CALLBACK(wep_key_setting_changed), binding); if (ex_target != binding->entry) { g_signal_handlers_disconnect_by_func(binding->entry, G_CALLBACK(wep_key_ui_changed), binding); g_object_weak_unref(binding->entry, wep_key_target_destroyed, binding); } else { g_signal_handlers_disconnect_by_func(binding->key_selector, G_CALLBACK(wep_key_ui_changed), binding); g_object_weak_unref(binding->key_selector, wep_key_target_destroyed, binding); } g_object_unref(binding->s_wsec); g_free(binding->entry_property); g_free(binding->key_selector_property); g_slice_free(NMEditorWepKeyBinding, binding); } /** * nm_editor_bind_wireless_security_wep_key: * @s_wsec: an #NMSettingWirelessSecurity * @entry: an entry widget * @entry_property: the string-valued property on @entry to bind * @key_selector: a pop-up widget of some sort * @key_selector_property: the integer-valued property on * @key_selector to bind * @flags: %GBindingFlags * * Binds the "wep-tx-keyidx" property on @s_wsec to * @key_selector_property on @key_selector, and the corresponding * "wep-keyN" property to @entry_property on @entry (and vice versa if * %G_BINDING_BIDIRECTIONAL). */ void nm_editor_bind_wireless_security_wep_key(NMSettingWirelessSecurity *s_wsec, gpointer entry, const char * entry_property, gpointer key_selector, const char * key_selector_property, GBindingFlags flags) { NMEditorWepKeyBinding *binding; char * notify; binding = g_slice_new0(NMEditorWepKeyBinding); binding->s_wsec = g_object_ref(s_wsec); binding->entry = entry; binding->entry_property = g_strdup(entry_property); binding->key_selector = key_selector; binding->key_selector_property = g_strdup(key_selector_property); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, G_CALLBACK(wep_key_setting_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, G_CALLBACK(wep_key_setting_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, G_CALLBACK(wep_key_setting_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, G_CALLBACK(wep_key_setting_changed), binding); g_signal_connect(s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, G_CALLBACK(wep_key_setting_changed), binding); if (flags & G_BINDING_BIDIRECTIONAL) { notify = g_strdup_printf("notify::%s", entry_property); g_signal_connect(entry, notify, G_CALLBACK(wep_key_ui_changed), binding); g_free(notify); notify = g_strdup_printf("notify::%s", key_selector_property); g_signal_connect(key_selector, notify, G_CALLBACK(wep_key_ui_changed), binding); g_free(notify); } g_object_weak_ref(entry, wep_key_target_destroyed, binding); g_object_weak_ref(key_selector, wep_key_target_destroyed, binding); if (flags & G_BINDING_SYNC_CREATE) wep_key_setting_changed(NULL, NULL, binding); } /* VLAN binding */ typedef struct { NMSettingVlan * s_vlan; NMSettingConnection *s_con; char *last_ifname_parent; int last_ifname_id; gboolean updating; } NMEditorVlanWidgetBinding; static gboolean parse_interface_name(const char *ifname, char **parent_ifname, int *id) { const char *ifname_end; char * end; if (!ifname || !*ifname) return FALSE; if (g_str_has_prefix(ifname, "vlan")) { ifname_end = ifname + 4; *id = strtoul(ifname_end, &end, 10); if (*end || end == (char *) ifname_end || *id < 0) return FALSE; *parent_ifname = NULL; return TRUE; } ifname_end = strchr(ifname, '.'); if (ifname_end) { *id = strtoul(ifname_end + 1, &end, 10); if (*end || end == (char *) ifname_end + 1 || *id < 0) return FALSE; *parent_ifname = g_strndup(ifname, ifname_end - ifname); return TRUE; } return FALSE; } static void vlan_settings_changed(GObject *object, GParamSpec *pspec, gpointer user_data) { NMEditorVlanWidgetBinding *binding = user_data; const char * ifname, *parent; char * ifname_parent; int ifname_id, id; if (binding->updating) return; ifname = nm_setting_connection_get_interface_name(binding->s_con); parent = nm_setting_vlan_get_parent(binding->s_vlan); id = nm_setting_vlan_get_id(binding->s_vlan); if (!parse_interface_name(ifname, &ifname_parent, &ifname_id)) return; /* If the id in INTERFACE_NAME changed, and ID is either unset, or was previously * in sync with INTERFACE_NAME, then update ID. */ if (id != ifname_id && (id == binding->last_ifname_id || id == 0)) { binding->updating = TRUE; g_object_set(G_OBJECT(binding->s_vlan), NM_SETTING_VLAN_ID, ifname_id, NULL); binding->updating = FALSE; } /* If the PARENT in INTERFACE_NAME changed, and PARENT is either unset, or was * previously in sync with INTERFACE_NAME, then update PARENT. */ if (g_strcmp0(parent, ifname_parent) != 0 && (g_strcmp0(parent, binding->last_ifname_parent) == 0 || !parent || !*parent)) { binding->updating = TRUE; g_object_set(G_OBJECT(binding->s_vlan), NM_SETTING_VLAN_PARENT, ifname_parent, NULL); binding->updating = FALSE; } g_free(binding->last_ifname_parent); binding->last_ifname_parent = ifname_parent; binding->last_ifname_id = ifname_id; } static void vlan_target_destroyed(gpointer user_data, GObject *ex_target) { NMEditorVlanWidgetBinding *binding = user_data; g_free(binding->last_ifname_parent); g_slice_free(NMEditorVlanWidgetBinding, binding); } /** * nm_editor_bind_vlan_name: * @s_vlan: an #NMSettingVlan * * Binds together several properties on @s_vlan, so that if the * %NM_SETTING_VLAN_INTERFACE_NAME matches %NM_SETTING_VLAN_PARENT * and %NM_SETTING_VLAN_ID in the obvious way, then changes to * %NM_SETTING_VLAN_INTERFACE_NAME will propagate to the other * two properties automatically. */ void nm_editor_bind_vlan_name(NMSettingVlan *s_vlan, NMSettingConnection *s_con) { NMEditorVlanWidgetBinding *binding; const char * ifname; binding = g_slice_new0(NMEditorVlanWidgetBinding); binding->s_vlan = s_vlan; binding->s_con = s_con; g_signal_connect(s_con, "notify::" NM_SETTING_CONNECTION_INTERFACE_NAME, G_CALLBACK(vlan_settings_changed), binding); g_object_weak_ref(G_OBJECT(s_vlan), vlan_target_destroyed, binding); ifname = nm_setting_connection_get_interface_name(s_con); if (!parse_interface_name(ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) { binding->last_ifname_parent = NULL; binding->last_ifname_id = 0; } }