/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Copyright 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 "config.h" #include #include #include #include #include #include "nm-editor-bindings.h" #include "nm-gvaluearray-compat.h" 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, (gint) 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_string_parse (const char *text, int family, gpointer addr, guint32 *prefix) { const char *slash; char *addrstr, *end; gboolean valid; slash = strchr (text, '/'); if (slash) { if (!prefix) return FALSE; addrstr = g_strndup (text, slash - text); } else addrstr = g_strdup (text); valid = (inet_pton (family, addrstr, addr) == 1); g_free (addrstr); if (!valid) return FALSE; if (slash) { *prefix = strtoul (slash + 1, &end, 10); if ( *end || *prefix == 0 || (family == AF_INET && *prefix > 32) || (family == AF_INET6 && *prefix > 128)) valid = FALSE; } else if (prefix) { if (family == AF_INET) *prefix = 32; else *prefix = 128; } return valid; } static gboolean ip4_addresses_with_prefix_to_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; GArray *addr; guint32 addrbytes, prefix; char buf[INET_ADDRSTRLEN], **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]; addrbytes = g_array_index (addr, guint32, 0); prefix = g_array_index (addr, guint32, 1); if (addrbytes) { strings[i] = g_strdup_printf ("%s/%d", inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)), (int) prefix); } else strings[i] = g_strdup (""); } g_value_take_boxed (target_value, strings); return TRUE; } static gboolean ip4_addresses_with_prefix_from_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { char **strings; GPtrArray *addrs; GArray *addr; guint32 *addrvals; int i; strings = g_value_get_boxed (source_value); /* Fetch the original property value, so as to preserve the gateway elements */ 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) { guint32 val; addr = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 3); val = 0; g_array_append_val (addr, val); val = 32; g_array_append_val (addr, val); val = 0; g_array_append_val (addr, val); g_ptr_array_add (addrs, addr); } else addr = addrs->pdata[i]; addrvals = (guint32 *)addr->data; if (!ip_string_parse (strings[i], AF_INET, &addrvals[0], &addrvals[1])) { g_ptr_array_unref (addrs); return FALSE; } } g_ptr_array_set_size (addrs, i); g_value_take_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip4_addresses_with_prefix_to_strv: * @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 %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property * @source_property on @source to the %G_TYPE_STRV property * @target_property on @target. * * Each address/prefix/gateway triplet in @source_property will be * converted to a string of the form "ip.ad.dr.ess/prefix" in * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is * specified. The "gateway" fields in @source_property are ignored * when converting to strings, and unmodified when converting from * strings. */ void nm_editor_bind_ip4_addresses_with_prefix_to_strv (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip4_addresses_with_prefix_to_strv, ip4_addresses_with_prefix_from_strv, NULL, NULL); } static gboolean ip4_addresses_to_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GArray *addrs; guint32 addrbytes; char buf[INET_ADDRSTRLEN], **strings; int i; addrs = g_value_get_boxed (source_value); strings = g_new0 (char *, addrs->len + 1); for (i = 0; i < addrs->len; i++) { addrbytes = g_array_index (addrs, guint32, i); if (addrbytes) inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)); else buf[0] = '\0'; strings[i] = g_strdup (buf); } g_value_take_boxed (target_value, strings); return TRUE; } static gboolean ip4_addresses_from_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { char **strings; GArray *addrs; guint32 addr; int i; strings = g_value_get_boxed (source_value); addrs = g_array_new (FALSE, FALSE, sizeof (guint32)); for (i = 0; strings[i]; i++) { if (!ip_string_parse (strings[i], AF_INET, &addr, NULL)) { g_array_unref (addrs); return FALSE; } g_array_append_val (addrs, addr); } g_value_take_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip4_addresses_to_strv: * @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 %DBUS_TYPE_G_UINT_ARRAY property @source_property on * @source to the %G_TYPE_STRV property @target_property on @target. * * Each address in @source_property will be converted to a string of * the form "ip.ad.dr.ess" in @target_property (and vice versa if * %G_BINDING_BIDIRECTIONAL) is specified. */ void nm_editor_bind_ip4_addresses_to_strv (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip4_addresses_to_strv, ip4_addresses_from_strv, NULL, NULL); } static gboolean ip4_gateway_to_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; GArray *addr; guint32 gateway = 0; const char *str; char buf[INET_ADDRSTRLEN]; int i; addrs = g_value_get_boxed (source_value); for (i = 0; i < addrs->len; i++) { addr = addrs->pdata[i]; gateway = g_array_index (addr, guint32, 2); if (gateway) break; } if (gateway) str = inet_ntop (AF_INET, &gateway, buf, sizeof (buf)); else str = ""; g_value_set_string (target_value, str); return TRUE; } static gboolean ip4_gateway_from_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { const char *text; GPtrArray *addrs; GArray *addr; guint32 addrbytes, *addrvals; int i; text = g_value_get_string (source_value); if (!ip_string_parse (text, AF_INET, &addrbytes, NULL)) return FALSE; /* Fetch the original property value, so as to preserve the IP address elements */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &addrs, NULL); if (!addrs->len) { g_ptr_array_unref (addrs); return FALSE; } addr = addrs->pdata[0]; addrvals = (guint32 *)addr->data; if (addrbytes == addrvals[2]) { g_ptr_array_unref (addrs); return FALSE; } addrvals[2] = addrbytes; for (i = 1; i < addrs->len; i++) { addr = addrs->pdata[i]; addrvals = (guint32 *)addr->data; addrvals[2] = 0; } g_value_take_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip4_gateway_to_string: * @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 #NmtNewtEntry) * @target_property: the property on @target to bind * (eg, "text") * @flags: %GBindingFlags * * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property * @source_property on @source to the %G_TYPE_STRING property * @target_property on @target. * * Specifically, this binds the "gateway" field of the first address * in @source_property; all other addresses in @source_property are * ignored, and its "address" and "prefix" fields are unmodified. */ void nm_editor_bind_ip4_gateway_to_string (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip4_gateway_to_string, ip4_gateway_from_string, NULL, NULL); } static gboolean ip4_route_transform_to_dest_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; char buf[INET_ADDRSTRLEN], *string; guint32 addrbytes; route = g_value_get_boxed (source_value); if (route) addrbytes = nm_ip4_route_get_dest (route); else addrbytes = 0; if (addrbytes) { string = g_strdup_printf ("%s/%d", inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)), (int) nm_ip4_route_get_prefix (route)); g_value_take_string (target_value, string); } else g_value_set_string (target_value, ""); return TRUE; } static gboolean ip4_route_transform_to_next_hop_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; char buf[INET_ADDRSTRLEN]; guint32 addrbytes; route = g_value_get_boxed (source_value); if (route) addrbytes = nm_ip4_route_get_next_hop (route); else addrbytes = 0; if (addrbytes) inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)); else buf[0] = '\0'; g_value_set_string (target_value, buf); return TRUE; } static gboolean ip4_route_transform_to_metric_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; char *string; route = g_value_get_boxed (source_value); if (route && nm_ip4_route_get_dest (route)) { string = g_strdup_printf ("%lu", (gulong) nm_ip4_route_get_metric (route)); g_value_take_string (target_value, string); } else g_value_set_string (target_value, ""); return TRUE; } static gboolean ip4_route_transform_from_dest_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; const char *text; guint32 addrbytes, prefix; text = g_value_get_string (source_value); if (!ip_string_parse (text, AF_INET, &addrbytes, &prefix)) return FALSE; /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip4_route_set_dest (route, addrbytes); nm_ip4_route_set_prefix (route, prefix); g_value_take_boxed (target_value, route); return TRUE; } static gboolean ip4_route_transform_from_next_hop_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; const char *text; guint32 addrbytes; text = g_value_get_string (source_value); if (*text) { if (!ip_string_parse (text, AF_INET, &addrbytes, NULL)) return FALSE; } else addrbytes = 0; /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip4_route_set_next_hop (route, addrbytes); g_value_take_boxed (target_value, route); return TRUE; } static gboolean ip4_route_transform_from_metric_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP4Route *route; const char *text; guint32 metric; text = g_value_get_string (source_value); metric = strtoul (text, NULL, 10); /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip4_route_set_metric (route, metric); g_value_take_boxed (target_value, route); return TRUE; } /** * nm_editor_bind_ip4_route_to_strings: * @source: the source object * @source_property: the source property * @dest_target: the target object for the route's destionation * @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 #NMIP4Route-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 * is a plain IP address, and @metric_target is a number. */ void nm_editor_bind_ip4_route_to_strings (gpointer source, const gchar *source_property, gpointer dest_target, const gchar *dest_target_property, gpointer next_hop_target, const gchar *next_hop_target_property, gpointer metric_target, const gchar *metric_target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, dest_target, dest_target_property, flags, ip4_route_transform_to_dest_string, ip4_route_transform_from_dest_string, NULL, NULL); g_object_bind_property_full (source, source_property, next_hop_target, next_hop_target_property, flags, ip4_route_transform_to_next_hop_string, ip4_route_transform_from_next_hop_string, NULL, NULL); g_object_bind_property_full (source, source_property, metric_target, metric_target_property, flags, ip4_route_transform_to_metric_string, ip4_route_transform_from_metric_string, NULL, NULL); } #define IP6_ADDRESS_SET(addr) ( addr \ && addr->len == sizeof (struct in6_addr) \ && memcmp (addr->data, &in6addr_any, addr->len) != 0) static gboolean ip6_addresses_with_prefix_to_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; GValueArray *addr; GValue *val; GByteArray *addrbytes; guint prefix; char **strings, buf[INET6_ADDRSTRLEN]; 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]; val = g_value_array_get_nth (addr, 0); addrbytes = g_value_get_boxed (val); val = g_value_array_get_nth (addr, 1); prefix = g_value_get_uint (val); if (IP6_ADDRESS_SET (addrbytes)) { strings[i] = g_strdup_printf ("%s/%d", inet_ntop (AF_INET6, addrbytes->data, buf, sizeof (buf)), prefix); } else strings[i] = g_strdup (""); } g_value_take_boxed (target_value, strings); return TRUE; } static gboolean ip6_addresses_with_prefix_from_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { char **strings; GPtrArray *addrs; GValueArray *addr; guint32 prefix; GValue val = G_VALUE_INIT, *valp; GByteArray *ba; int i; strings = g_value_get_boxed (source_value); /* Fetch the original property value, so as to preserve the gateway elements */ 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) { addr = g_value_array_new (3); g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY); ba = g_byte_array_sized_new (sizeof (struct in6_addr)); g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr)); g_value_take_boxed (&val, ba); g_value_array_append (addr, &val); g_value_unset (&val); g_value_init (&val, G_TYPE_UINT); g_value_set_uint (&val, 128); g_value_array_append (addr, &val); g_value_unset (&val); g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY); ba = g_byte_array_sized_new (sizeof (struct in6_addr)); g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr)); g_value_take_boxed (&val, ba); g_value_array_append (addr, &val); g_value_unset (&val); g_ptr_array_add (addrs, addr); } else addr = addrs->pdata[i]; valp = g_value_array_get_nth (addr, 0); ba = g_value_get_boxed (valp); g_assert (ba->len == sizeof (struct in6_addr)); if (!ip_string_parse (strings[i], AF_INET6, ba->data, &prefix)) { g_ptr_array_unref (addrs); return FALSE; } valp = g_value_array_get_nth (addr, 1); g_value_set_uint (valp, prefix); } g_ptr_array_set_size (addrs, i); g_value_set_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip6_addresses_with_prefix_to_strv: * @source: the source object (eg, an #NMSettingIP6Config) * @source_property: the property on @source to bind (eg, * %NM_SETTING_IP6_CONFIG_ADDRESSES) * @target: the target object (eg, an #NmtAddressList) * @target_property: the property on @target to bind * (eg, "strings") * @flags: %GBindingFlags * * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property * @source_property on @source to the %G_TYPE_STRV property * @target_property on @target. * * Each address/prefix/gateway triplet in @source_property will be * converted to a string of the form "ip::ad:dr:ess/prefix" in * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is * specified. The "gateway" fields in @source_property are ignored * when converting to strings, and unmodified when converting from * strings. */ void nm_editor_bind_ip6_addresses_with_prefix_to_strv (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip6_addresses_with_prefix_to_strv, ip6_addresses_with_prefix_from_strv, NULL, NULL); } static gboolean ip6_addresses_to_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; GByteArray *addrbytes; char buf[INET6_ADDRSTRLEN], **strings; int i; addrs = g_value_get_boxed (source_value); strings = g_new0 (char *, addrs->len + 1); for (i = 0; i < addrs->len; i++) { addrbytes = addrs->pdata[i]; if (IP6_ADDRESS_SET (addrbytes)) inet_ntop (AF_INET6, addrbytes->data, buf, sizeof (buf)); else buf[0] = '\0'; strings[i] = g_strdup (buf); } g_value_take_boxed (target_value, strings); return TRUE; } static gboolean ip6_addresses_from_strv (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { char **strings; GPtrArray *addrs; GByteArray *addr; struct in6_addr addrbytes; int i; strings = g_value_get_boxed (source_value); addrs = g_ptr_array_new (); for (i = 0; strings[i]; i++) { if (!ip_string_parse (strings[i], AF_INET6, &addrbytes, NULL)) { while (i--) g_byte_array_unref (addrs->pdata[i]); g_ptr_array_unref (addrs); return FALSE; } addr = g_byte_array_sized_new (sizeof (addrbytes)); g_byte_array_append (addr, (guint8 *)&addrbytes, sizeof (addrbytes)); g_ptr_array_add (addrs, addr); } g_value_take_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip6_addresses_to_strv: * @source: the source object (eg, an #NMSettingIP6Config) * @source_property: the property on @source to bind (eg, * %NM_SETTING_IP6_CONFIG_DNS) * @target: the target object (eg, an #NmtAddressList) * @target_property: the property on @target to bind * (eg, "strings") * @flags: %GBindingFlags * * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR property * @source_property on @source to the %G_TYPE_STRV property * @target_property on @target. * * Each address in @source_property will be converted to a string of * the form "ip::ad:dr:ess" in @target_property (and vice versa if * %G_BINDING_BIDIRECTIONAL) is specified. */ void nm_editor_bind_ip6_addresses_to_strv (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip6_addresses_to_strv, ip6_addresses_from_strv, NULL, NULL); } static gboolean ip6_gateway_to_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; GValueArray *addr; GValue *val; GByteArray *gateway; char buf[INET6_ADDRSTRLEN]; const char *str; addrs = g_value_get_boxed (source_value); if (addrs->len == 0) return FALSE; addr = addrs->pdata[0]; val = g_value_array_get_nth (addr, 2); gateway = g_value_get_boxed (val); if (IP6_ADDRESS_SET (gateway)) str = inet_ntop (AF_INET6, gateway->data, buf, sizeof (buf)); else str = ""; g_value_set_string (target_value, str); return TRUE; } static gboolean ip6_gateway_from_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GPtrArray *addrs; const char *text; GValueArray *addr; struct in6_addr gateway; GValue *val; GByteArray *ba; int i; text = g_value_get_string (source_value); if (!ip_string_parse (text, AF_INET6, &gateway, NULL)) return FALSE; /* Fetch the original property value, so as to preserve the IP address elements */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &addrs, NULL); if (!addrs->len) { g_ptr_array_unref (addrs); return FALSE; } addr = addrs->pdata[0]; ba = g_byte_array_sized_new (sizeof (gateway)); g_byte_array_append (ba, (guint8 *) &gateway, sizeof (gateway)); val = g_value_array_get_nth (addr, 2); g_value_take_boxed (val, ba); for (i = 1; i < addrs->len; i++) { addr = addrs->pdata[i]; val = g_value_array_get_nth (addr, 2); ba = g_value_get_boxed (val); if (ba) memset (ba->data, 0, ba->len); } g_value_take_boxed (target_value, addrs); return TRUE; } /** * nm_editor_bind_ip6_gateway_to_string: * @source: the source object (eg, an #NMSettingIP6Config) * @source_property: the property on @source to bind (eg, * %NM_SETTING_IP6_CONFIG_ADDRESSES) * @target: the target object (eg, an #NmtNewtEntry) * @target_property: the property on @target to bind * (eg, "text") * @flags: %GBindingFlags * * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property * @source_property on @source to the %G_TYPE_STRING property * @target_property on @target. * * Specifically, this binds the "gateway" field of the first address * in @source_property; all other addresses in @source_property are * ignored, and its "address" and "prefix" fields are unmodified. */ void nm_editor_bind_ip6_gateway_to_string (gpointer source, const gchar *source_property, gpointer target, const gchar *target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, target, target_property, flags, ip6_gateway_to_string, ip6_gateway_from_string, NULL, NULL); } #define IN6_ADDR_SET(bytes) (memcmp (bytes, &in6addr_any, sizeof (struct in6_addr)) != 0) static gboolean ip6_route_transform_to_dest_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; char buf[INET6_ADDRSTRLEN], *string; const struct in6_addr *addrbytes; route = g_value_get_boxed (source_value); if (route) addrbytes = nm_ip6_route_get_dest (route); else addrbytes = &in6addr_any; if (IN6_ADDR_SET (addrbytes)) { string = g_strdup_printf ("%s/%d", inet_ntop (AF_INET6, addrbytes, buf, sizeof (buf)), (int) nm_ip6_route_get_prefix (route)); g_value_take_string (target_value, string); } else g_value_set_string (target_value, ""); return TRUE; } static gboolean ip6_route_transform_to_next_hop_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; char buf[INET6_ADDRSTRLEN]; const struct in6_addr *addrbytes; route = g_value_get_boxed (source_value); if (route) addrbytes = nm_ip6_route_get_next_hop (route); else addrbytes = &in6addr_any; if (IN6_ADDR_SET (addrbytes)) inet_ntop (AF_INET6, addrbytes, buf, sizeof (buf)); else buf[0] = '\0'; g_value_set_string (target_value, buf); return TRUE; } static gboolean ip6_route_transform_to_metric_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; char *string; route = g_value_get_boxed (source_value); if (route && IN6_ADDR_SET (nm_ip6_route_get_dest (route))) { string = g_strdup_printf ("%lu", (gulong) nm_ip6_route_get_metric (route)); g_value_take_string (target_value, string); } else g_value_set_string (target_value, ""); return TRUE; } static gboolean ip6_route_transform_from_dest_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; const char *text; struct in6_addr addrbytes; guint32 prefix; text = g_value_get_string (source_value); if (!ip_string_parse (text, AF_INET6, &addrbytes, &prefix)) return FALSE; /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip6_route_set_dest (route, &addrbytes); nm_ip6_route_set_prefix (route, prefix); g_value_take_boxed (target_value, route); return TRUE; } static gboolean ip6_route_transform_from_next_hop_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; const char *text; struct in6_addr addrbytes; text = g_value_get_string (source_value); if (*text) { if (!ip_string_parse (text, AF_INET6, &addrbytes, NULL)) return FALSE; } else addrbytes = in6addr_any; /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip6_route_set_next_hop (route, &addrbytes); g_value_take_boxed (target_value, route); return TRUE; } static gboolean ip6_route_transform_from_metric_string (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { NMIP6Route *route; const char *text; guint32 metric; text = g_value_get_string (source_value); metric = strtoul (text, NULL, 10); /* Fetch the original property value */ g_object_get (g_binding_get_source (binding), g_binding_get_source_property (binding), &route, NULL); nm_ip6_route_set_metric (route, metric); g_value_take_boxed (target_value, route); return TRUE; } /** * nm_editor_bind_ip6_route_to_strings: * @source: the source object * @source_property: the source property * @dest_target: the target object for the route's destionation * @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 #NMIP6Route-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_ip6_addresses_with_prefix_to_strv(). @next_hop_target * is a plain IP address, and @metric_target is a number. */ void nm_editor_bind_ip6_route_to_strings (gpointer source, const gchar *source_property, gpointer dest_target, const gchar *dest_target_property, gpointer next_hop_target, const gchar *next_hop_target_property, gpointer metric_target, const gchar *metric_target_property, GBindingFlags flags) { g_object_bind_property_full (source, source_property, dest_target, dest_target_property, flags, ip6_route_transform_to_dest_string, ip6_route_transform_from_dest_string, NULL, NULL); g_object_bind_property_full (source, source_property, next_hop_target, next_hop_target_property, flags, ip6_route_transform_to_next_hop_string, ip6_route_transform_from_next_hop_string, NULL, NULL); g_object_bind_property_full (source, source_property, metric_target, metric_target_property, flags, ip6_route_transform_to_metric_string, ip6_route_transform_from_metric_string, NULL, 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-none") || !strcmp (key_mgmt, "wpa-psk")) return "wpa-personal"; if (!strcmp (key_mgmt, "wpa-eap")) return "wpa-enterprise"; 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, "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; 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_vlan_get_interface_name (binding->s_vlan); 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) { NMEditorVlanWidgetBinding *binding; const char *ifname; binding = g_slice_new0 (NMEditorVlanWidgetBinding); binding->s_vlan = s_vlan; g_signal_connect (s_vlan, "notify::" NM_SETTING_VLAN_INTERFACE_NAME, G_CALLBACK (vlan_settings_changed), binding); g_object_weak_ref (G_OBJECT (s_vlan), vlan_target_destroyed, binding); ifname = nm_setting_vlan_get_interface_name (s_vlan); if (!parse_interface_name (ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) { binding->last_ifname_parent = NULL; binding->last_ifname_id = 0; } }