diff options
Diffstat (limited to 'src/nmtui')
81 files changed, 13859 insertions, 0 deletions
diff --git a/src/nmtui/meson.build b/src/nmtui/meson.build new file mode 100644 index 00000000..fe440175 --- /dev/null +++ b/src/nmtui/meson.build @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +executable( + 'nmtui', + files( + 'nm-editor-bindings.c', + 'nm-editor-utils.c', + 'nmt-address-list.c', + 'nmt-connect-connection-list.c', + 'nmt-device-entry.c', + 'nmt-edit-connection-list.c', + 'nmt-editor.c', + 'nmt-editor-grid.c', + 'nmt-editor-page.c', + 'nmt-editor-page-device.c', + 'nmt-editor-section.c', + 'nmt-ip-entry.c', + 'nmt-mac-entry.c', + 'nmt-mtu-entry.c', + 'nmt-page-bond.c', + 'nmt-page-bridge.c', + 'nmt-page-bridge-port.c', + 'nmt-page-dsl.c', + 'nmt-page-ethernet.c', + 'nmt-page-infiniband.c', + 'nmt-page-ip4.c', + 'nmt-page-ip6.c', + 'nmt-page-ip-tunnel.c', + 'nmt-page-ppp.c', + 'nmt-page-team.c', + 'nmt-page-team-port.c', + 'nmt-page-vlan.c', + 'nmt-page-wifi.c', + 'nmt-password-dialog.c', + 'nmt-password-fields.c', + 'nmt-route-editor.c', + 'nmt-route-entry.c', + 'nmt-route-table.c', + 'nmt-slave-list.c', + 'nmtui.c', + 'nmtui-connect.c', + 'nmtui-edit.c', + 'nmtui-hostname.c', + 'nmt-utils.c', + 'nmt-widget-list.c', + ), + dependencies: [ + libnm_dep, + newt_dep, + glib_dep, + ], + link_with: [ + libnmt_newt, + libnmc_setting, + libnmc_base, + libnm_client_aux_extern, + libnm_core_aux_extern, + libnm_core_aux_intern, + libnm_base, + libnm_log_null, + libnm_glib_aux, + libnm_std_aux, + libc_siphash, + ], + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, + install: true, +) diff --git a/src/nmtui/nm-editor-bindings.c b/src/nmtui/nm-editor-bindings.c new file mode 100644 index 00000000..2b1c4748 --- /dev/null +++ b/src/nmtui/nm-editor-bindings.c @@ -0,0 +1,1139 @@ +/* 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-client-aux-extern/nm-default-client.h" + +#include "nm-editor-bindings.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.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, (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; + } +} diff --git a/src/nmtui/nm-editor-bindings.h b/src/nmtui/nm-editor-bindings.h new file mode 100644 index 00000000..027a59a9 --- /dev/null +++ b/src/nmtui/nm-editor-bindings.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_BINDINGS_H +#define NM_EDITOR_BINDINGS_H + +void nm_editor_bindings_init(void); + +void nm_editor_bind_ip_addresses_with_prefix_to_strv(int family, + gpointer source, + const char * source_property, + gpointer target, + const char * target_property, + GBindingFlags flags); +void nm_editor_bind_ip_addresses_to_strv(int family, + gpointer source, + const char * source_property, + gpointer target, + const char * target_property, + GBindingFlags flags); + +void nm_editor_bind_ip_gateway_to_string(int family, + NMSettingIPConfig *source, + gpointer target, + const char * target_property, + const char * target_sensitive_property, + GBindingFlags flags); + +void nm_editor_bind_ip_route_to_strings(int 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); + +void nm_editor_bind_wireless_security_method(NMConnection * connection, + NMSettingWirelessSecurity *s_wsec, + gpointer target, + const char * target_property, + GBindingFlags flags); +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); + +void nm_editor_bind_vlan_name(NMSettingVlan *s_vlan, NMSettingConnection *s_con); + +#endif /* NM_EDITOR_BINDINGS_H */ diff --git a/src/nmtui/nm-editor-utils.c b/src/nmtui/nm-editor-utils.c new file mode 100644 index 00000000..c6b03bd8 --- /dev/null +++ b/src/nmtui/nm-editor-utils.c @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2012, 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-editor-utils + * @short_description: Miscellaneous connection editor utilities + * + * nm-editor-utils contains helper functions for connection editors. + * The goal is that this should eventually be shared between nmtui, + * nm-connection-editor, and gnome-control-center. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nm-editor-utils.h" +#if 0 + #include "libnmc-base/nm-vpn-helpers.h" + +static GSList *vpn_plugins; + +static int +sort_vpn_plugins (gconstpointer a, gconstpointer b) +{ + NMVpnEditorPlugin *aa = NM_VPN_EDITOR_PLUGIN (a); + NMVpnEditorPlugin *bb = NM_VPN_EDITOR_PLUGIN (b); + char *aa_desc = NULL, *bb_desc = NULL; + int ret; + + g_object_get (aa, NM_VPN_EDITOR_PLUGIN_NAME, &aa_desc, NULL); + g_object_get (bb, NM_VPN_EDITOR_PLUGIN_NAME, &bb_desc, NULL); + + ret = g_strcmp0 (aa_desc, bb_desc); + + g_free (aa_desc); + g_free (bb_desc); + + return ret; +} +#endif + +static void +wifi_connection_setup_func(NMConnection *connection, NMSettingConnection *s_con, NMSetting *s_hw) +{ + g_object_set(G_OBJECT(s_hw), NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL); +} + +static void +bond_connection_setup_func(NMConnection *connection, NMSettingConnection *s_con, NMSetting *s_hw) +{ + NMSettingBond * s_bond = NM_SETTING_BOND(s_hw); + guint i; + const char * value; + static const char *const options[] = { + NM_SETTING_BOND_OPTION_MODE, + NM_SETTING_BOND_OPTION_MIIMON, + NM_SETTING_BOND_OPTION_DOWNDELAY, + NM_SETTING_BOND_OPTION_UPDELAY, + }; + + for (i = 0; i < G_N_ELEMENTS(options); i++) { + value = nm_setting_bond_get_option_default(s_bond, options[i]); + if (value) + nm_setting_bond_add_option(s_bond, options[i], value); + } +} + +typedef void (*NMEditorNewConnectionSetupFunc)(NMConnection * connection, + NMSettingConnection *s_con, + NMSetting * s_hw); + +typedef struct { + NMEditorConnectionTypeData data; + + const char * id_format; + NMEditorNewConnectionSetupFunc connection_setup_func; + gboolean no_autoconnect; +} NMEditorConnectionTypeDataReal; + +static int +sort_types(gconstpointer a, gconstpointer b) +{ + NMEditorConnectionTypeData *typea = *(NMEditorConnectionTypeData **) a; + NMEditorConnectionTypeData *typeb = *(NMEditorConnectionTypeData **) b; + + if (typea->virtual && !typeb->virtual) + return 1; + else if (typeb->virtual && !typea->virtual) + return -1; + + if (typea->setting_type == NM_TYPE_SETTING_VPN && typeb->setting_type != NM_TYPE_SETTING_VPN) + return 1; + else if (typeb->setting_type == NM_TYPE_SETTING_VPN + && typea->setting_type != NM_TYPE_SETTING_VPN) + return -1; + + return g_utf8_collate(typea->name, typeb->name); +} + +/** + * nm_editor_utils_get_connection_type_list: + * + * Gets an array of information about supported connection types. The + * array is sorted in a standard presentation order (hardware types + * first, alphabetized, then virtual types, alphabetized, then VPN + * types, alphabetized). + * + * Returns: the array of connection type information + */ +NMEditorConnectionTypeData ** +nm_editor_utils_get_connection_type_list(void) +{ + GPtrArray * array; + NMEditorConnectionTypeDataReal * item; + static NMEditorConnectionTypeData **list; +#if 0 + GHashTable *vpn_plugins_hash; + gboolean have_vpn_plugins; +#endif + + if (list) + return list; + + array = g_ptr_array_new(); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Ethernet"); + item->data.setting_type = NM_TYPE_SETTING_WIRED; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("Ethernet connection %d"); + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Veth"); + item->data.setting_type = NM_TYPE_SETTING_VETH; + item->data.device_type = NM_TYPE_DEVICE_VETH; + item->data.virtual = TRUE; + item->id_format = _("Veth connection %d"); + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Wi-Fi"); + item->data.setting_type = NM_TYPE_SETTING_WIRELESS; + item->data.device_type = NM_TYPE_DEVICE_WIFI; + item->data.virtual = FALSE; + item->id_format = _("Wi-Fi connection %d"); + item->connection_setup_func = wifi_connection_setup_func; + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("InfiniBand"); + item->data.setting_type = NM_TYPE_SETTING_INFINIBAND; + item->data.device_type = NM_TYPE_DEVICE_INFINIBAND; + item->data.virtual = FALSE; + item->id_format = _("InfiniBand connection %d"); + g_ptr_array_add(array, item); + +#if 0 + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Mobile Broadband"); + item->data.setting_type = NM_TYPE_SETTING_GSM; + item->data.virtual = FALSE; + item->id_format = _("Mobile broadband connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); +#endif + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("DSL"); + item->data.setting_type = NM_TYPE_SETTING_PPPOE; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("DSL connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bond"); + item->data.setting_type = NM_TYPE_SETTING_BOND; + item->data.device_type = NM_TYPE_DEVICE_BOND; + item->data.virtual = TRUE; + item->id_format = _("Bond connection %d"); + item->connection_setup_func = bond_connection_setup_func; + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bridge"); + item->data.setting_type = NM_TYPE_SETTING_BRIDGE; + item->data.slave_setting_type = NM_TYPE_SETTING_BRIDGE_PORT; + item->data.device_type = NM_TYPE_DEVICE_BRIDGE; + item->data.virtual = TRUE; + item->id_format = _("Bridge connection %d"); + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Team"); + item->data.setting_type = NM_TYPE_SETTING_TEAM; + item->data.slave_setting_type = NM_TYPE_SETTING_TEAM_PORT; + item->data.device_type = NM_TYPE_DEVICE_TEAM; + item->data.virtual = TRUE; + item->id_format = _("Team connection %d"); + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VLAN"); + item->data.setting_type = NM_TYPE_SETTING_VLAN; + item->data.device_type = NM_TYPE_DEVICE_VLAN; + item->data.virtual = TRUE; + item->id_format = _("VLAN connection %d"); + g_ptr_array_add(array, item); + + item = g_new0(NMEditorConnectionTypeDataReal, 1); + item->data.name = _("IP tunnel"); + item->data.setting_type = NM_TYPE_SETTING_IP_TUNNEL; + item->data.device_type = NM_TYPE_DEVICE_IP_TUNNEL; + item->data.virtual = TRUE; + item->id_format = _("IP tunnel connection %d"); + g_ptr_array_add(array, item); + +#if 0 + /* Add "VPN" only if there are plugins */ + vpn_plugins_hash = nm_vpn_get_plugin_infos (); + have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); + if (have_vpn_plugins) { + GHashTableIter iter; + gpointer name, plugin; + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VPN"); + item->data.setting_type = NM_TYPE_SETTING_VPN; + item->data.virtual = TRUE; + item->id_format = _("VPN connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); + + vpn_plugins = NULL; + g_hash_table_iter_init (&iter, vpn_plugins_hash); + while (g_hash_table_iter_next (&iter, &name, &plugin)) + vpn_plugins = g_slist_prepend (vpn_plugins, plugin); + vpn_plugins = g_slist_sort (vpn_plugins, sort_vpn_plugins); + } +#endif + + g_ptr_array_sort(array, sort_types); + g_ptr_array_add(array, NULL); + + list = (NMEditorConnectionTypeData **) g_ptr_array_free(array, FALSE); + return list; +} + +static gboolean +_assert_format_int(const char *format) +{ + g_assert(format); + format = strchr(format, '%'); + g_assert(format); + format++; + g_assert(!strchr(format, '%')); + g_assert(format[0] == 'd'); + return TRUE; +} + +static char * +get_available_connection_name(const char *format, NMClient *client) +{ + const GPtrArray *conns; + GSList * names = NULL, *iter; + char * cname = NULL; + int i = 0; + + nm_assert(_assert_format_int(format)); + + conns = nm_client_get_connections(client); + for (i = 0; i < conns->len; i++) { + const char *id; + + id = nm_connection_get_id(NM_CONNECTION(conns->pdata[i])); + g_assert(id); + names = g_slist_append(names, (gpointer) id); + } + + /* Find the next available unique connection name */ + for (i = 1; !cname && i < 10000; i++) { + char * temp; + gboolean found = FALSE; + + NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") + temp = g_strdup_printf(format, i); + NM_PRAGMA_WARNING_REENABLE + for (iter = names; iter; iter = g_slist_next(iter)) { + if (!strcmp(iter->data, temp)) { + found = TRUE; + break; + } + } + if (!found) + cname = temp; + else + g_free(temp); + } + + g_slist_free(names); + return cname; +} + +static char * +get_available_iface_name(const char *try_name, NMClient *client) +{ + const GPtrArray *connections; + NMConnection * connection; + char * new_name; + unsigned num = 1; + int i = 0; + const char * ifname = NULL; + + connections = nm_client_get_connections(client); + + new_name = g_strdup(try_name); + while (i < connections->len) { + connection = NM_CONNECTION(connections->pdata[i]); + ifname = nm_connection_get_interface_name(connection); + if (g_strcmp0(new_name, ifname) == 0) { + g_free(new_name); + new_name = g_strdup_printf("%s%d", try_name, num++); + i = 0; + } else + i++; + } + return new_name; +} + +/** + * nm_editor_utils_create_connection: + * @type: the type of the connection's primary #NMSetting + * @master: (allow-none): the connection's master, if any + * @client: an #NMClient + * + * Creates a new #NMConnection of the given type, automatically + * creating a UUID and an appropriate not-currently-in-use connection + * name, setting #NMSettingConnection:autoconnect appropriately for + * the connection type, filling in slave-related information if + * @master is not %NULL, and initializing any other mandatory-to-set + * properties to reasonable initial values. + * + * Returns: a new #NMConnection + */ +NMConnection * +nm_editor_utils_create_connection(GType type, NMConnection *master, NMClient *client) +{ + NMEditorConnectionTypeData ** types; + NMEditorConnectionTypeDataReal *type_data = NULL; + const char * master_setting_type = NULL, *master_uuid = NULL; + GType master_type = G_TYPE_INVALID, slave_setting_type = G_TYPE_INVALID; + NMConnection * connection; + NMSettingConnection *s_con; + NMSetting * s_hw, *s_slave; + char * uuid, *id, *ifname; + int i; + + if (master) { + NMSettingConnection *master_s_con; + + master_s_con = nm_connection_get_setting_connection(master); + master_setting_type = nm_setting_connection_get_connection_type(master_s_con); + master_uuid = nm_setting_connection_get_uuid(master_s_con); + master_type = nm_setting_lookup_type(master_setting_type); + } + + types = nm_editor_utils_get_connection_type_list(); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == type) + type_data = (NMEditorConnectionTypeDataReal *) types[i]; + if (types[i]->setting_type == master_type) + slave_setting_type = types[i]->slave_setting_type; + } + if (!type_data) { + g_return_val_if_reached(NULL); + return NULL; + } + + connection = nm_simple_connection_new(); + + s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + s_hw = g_object_new(type, NULL); + nm_connection_add_setting(connection, s_hw); + + if (type == NM_TYPE_SETTING_BOND) + ifname = get_available_iface_name("nm-bond", client); + else if (type == NM_TYPE_SETTING_TEAM) + ifname = get_available_iface_name("nm-team", client); + else if (type == NM_TYPE_SETTING_BRIDGE) + ifname = get_available_iface_name("nm-bridge", client); + else + ifname = NULL; + + if (slave_setting_type != G_TYPE_INVALID) { + s_slave = g_object_new(slave_setting_type, NULL); + nm_connection_add_setting(connection, s_slave); + } + + uuid = nm_utils_uuid_generate(); + id = get_available_connection_name(type_data->id_format, client); + + g_object_set(s_con, + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_ID, + id, + NM_SETTING_CONNECTION_TYPE, + nm_setting_get_name(s_hw), + NM_SETTING_CONNECTION_AUTOCONNECT, + !type_data->no_autoconnect, + NM_SETTING_CONNECTION_MASTER, + master_uuid, + NM_SETTING_CONNECTION_SLAVE_TYPE, + master_setting_type, + NM_SETTING_CONNECTION_INTERFACE_NAME, + ifname, + NULL); + + g_free(uuid); + g_free(id); + g_free(ifname); + + if (type_data->connection_setup_func) + type_data->connection_setup_func(connection, s_con, s_hw); + + return connection; +} + +/** + * nm_editor_utils_get_connection_type_data: + * @conn: an #NMConnection + * + * Gets the #NMEditorConnectionTypeData corresponding to + * @conn's connection type. + * + * Returns: the #NMEditorConnectionTypeData + */ +NMEditorConnectionTypeData * +nm_editor_utils_get_connection_type_data(NMConnection *conn) +{ + NMSettingConnection * s_con; + const char * conn_type; + GType conn_gtype; + NMEditorConnectionTypeData **types; + int i; + + s_con = nm_connection_get_setting_connection(conn); + g_return_val_if_fail(s_con != NULL, NULL); + + conn_type = nm_setting_connection_get_connection_type(s_con); + conn_gtype = nm_setting_lookup_type(conn_type); + g_return_val_if_fail(conn_gtype != G_TYPE_INVALID, NULL); + + types = nm_editor_utils_get_connection_type_list(); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == conn_gtype) + return types[i]; + } + + return NULL; +} diff --git a/src/nmtui/nm-editor-utils.h b/src/nmtui/nm-editor-utils.h new file mode 100644 index 00000000..5b624f93 --- /dev/null +++ b/src/nmtui/nm-editor-utils.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2012, 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_UTILS_H +#define NM_EDITOR_UTILS_H + +typedef struct { + const char *name; + GType setting_type; + GType slave_setting_type; + GType device_type; + gboolean virtual; +} NMEditorConnectionTypeData; + +NMEditorConnectionTypeData **nm_editor_utils_get_connection_type_list(void); +NMEditorConnectionTypeData * nm_editor_utils_get_connection_type_data(NMConnection *conn); + +NMConnection *nm_editor_utils_create_connection(GType type, NMConnection *master, NMClient *client); + +#endif /* NM_EDITOR_UTILS_H */ diff --git a/src/nmtui/nmt-address-list.c b/src/nmtui/nmt-address-list.c new file mode 100644 index 00000000..265457d6 --- /dev/null +++ b/src/nmtui/nmt-address-list.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-address-list + * @short_description: An editable list of IP addresses or hostnames + * + * #NmtAddressList is a subclass of #NmtWidgetList that contains + * entries displaying IP addresses, address/prefix strings, or + * hostnames. This is designed for binding its #NmtAddressList:strings + * property to an appropriate #NMSettingIP4Config or + * #NMSettingIP6Config property via one of the nm-editor-bindings + * functions. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-address-list.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE(NmtAddressList, nmt_address_list, NMT_TYPE_WIDGET_LIST) + +#define NMT_ADDRESS_LIST_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ADDRESS_LIST, NmtAddressListPrivate)) + +typedef struct { + NmtAddressListType list_type; + char ** strings; +} NmtAddressListPrivate; + +enum { + PROP_0, + PROP_LIST_TYPE, + PROP_STRINGS, + + LAST_PROP +}; + +/** + * NmtAddressListType: + * @NMT_ADDRESS_LIST_IP4_WITH_PREFIX: IPv4 address/prefix strings + * @NMT_ADDRESS_LIST_IP4: IPv4 addresses + * @NMT_ADDRESS_LIST_IP6_WITH_PREFIX: IPv6 address/prefix strings + * @NMT_ADDRESS_LIST_IP6: IPv6 addresses + * @NMT_ADDRESS_LIST_HOSTNAME: hostnames + * + * The type of address in an #NmtAddressList + */ + +/** + * nmt_address_list_new: + * @list_type: the type of address the list will contain + * + * Creates a new #NmtAddressList + * + * Returns: a new #NmtAddressList + */ +NmtNewtWidget * +nmt_address_list_new(NmtAddressListType list_type) +{ + return g_object_new(NMT_TYPE_ADDRESS_LIST, "list-type", list_type, NULL); +} + +static void +nmt_address_list_init(NmtAddressList *list) +{} + +static gboolean +strings_transform_to_entry(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + int n = GPOINTER_TO_INT(user_data); + char **strings; + + strings = g_value_get_boxed(source_value); + if (n >= g_strv_length(strings)) + return FALSE; + + g_value_set_string(target_value, strings[n]); + return TRUE; +} + +static gboolean +strings_transform_from_entry(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + NmtAddressList * list = NMT_ADDRESS_LIST(g_binding_get_source(binding)); + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(list); + int n = GPOINTER_TO_INT(user_data); + + if (n >= g_strv_length(priv->strings)) + return FALSE; + + g_free(priv->strings[n]); + priv->strings[n] = g_value_dup_string(source_value); + + g_value_set_boxed(target_value, priv->strings); + return TRUE; +} + +static gboolean +hostname_filter(NmtNewtEntry *entry, const char *text, int ch, int position, gpointer user_data) +{ + return g_ascii_isalnum(ch) || ch == '.' || ch == '-'; +} + +static NmtNewtWidget * +nmt_address_list_create_widget(NmtWidgetList *list, int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(list); + NmtNewtWidget * entry; + + if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX) + entry = nmt_ip_entry_new(25, AF_INET, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP4) + entry = nmt_ip_entry_new(25, AF_INET, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX) + entry = nmt_ip_entry_new(25, AF_INET6, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6) + entry = nmt_ip_entry_new(25, AF_INET6, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) { + entry = nmt_newt_entry_new(25, NMT_NEWT_ENTRY_NONEMPTY); + nmt_newt_entry_set_filter(NMT_NEWT_ENTRY(entry), hostname_filter, list); + } else + g_assert_not_reached(); + + g_object_bind_property_full(list, + "strings", + entry, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + strings_transform_to_entry, + strings_transform_from_entry, + GINT_TO_POINTER(num), + NULL); + + return entry; +} + +static void +nmt_address_list_add_clicked(NmtWidgetList *list) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(list); + int len; + + len = priv->strings ? g_strv_length(priv->strings) : 0; + priv->strings = g_renew(char *, priv->strings, len + 2); + priv->strings[len] = g_strdup(""); + priv->strings[len + 1] = NULL; + + nmt_widget_list_set_length(list, len + 1); + g_object_notify(G_OBJECT(list), "strings"); +} + +static void +nmt_address_list_remove_clicked(NmtWidgetList *list, int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(list); + int len; + + len = g_strv_length(priv->strings); + g_free(priv->strings[num]); + memmove(priv->strings + num, priv->strings + num + 1, (len - num) * sizeof(char *)); + + nmt_widget_list_set_length(list, len - 1); + g_object_notify(G_OBJECT(list), "strings"); +} + +static void +nmt_address_list_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_LIST_TYPE: + priv->list_type = g_value_get_uint(value); + break; + case PROP_STRINGS: + g_strfreev(priv->strings); + priv->strings = g_value_dup_boxed(value); + if (!priv->strings) + priv->strings = g_new0(char *, 1); + nmt_widget_list_set_length(NMT_WIDGET_LIST(object), g_strv_length(priv->strings)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_LIST_TYPE: + g_value_set_uint(value, priv->list_type); + break; + case PROP_STRINGS: + g_value_set_boxed(value, priv->strings); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_class_init(NmtAddressListClass *list_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(list_class); + NmtWidgetListClass *widget_list_class = NMT_WIDGET_LIST_CLASS(list_class); + + g_type_class_add_private(list_class, sizeof(NmtAddressListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_address_list_set_property; + object_class->get_property = nmt_address_list_get_property; + + widget_list_class->create_widget = nmt_address_list_create_widget; + widget_list_class->add_clicked = nmt_address_list_add_clicked; + widget_list_class->remove_clicked = nmt_address_list_remove_clicked; + + /** + * NmtAddressList:list-type: + * + * The type of address the list holds. + */ + g_object_class_install_property( + object_class, + PROP_LIST_TYPE, + g_param_spec_uint("list-type", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtAddressList:strings: + * + * The strings in the list's entries. + */ + g_object_class_install_property(object_class, + PROP_STRINGS, + g_param_spec_boxed("strings", + "", + "", + G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-address-list.h b/src/nmtui/nmt-address-list.h new file mode 100644 index 00000000..fa356601 --- /dev/null +++ b/src/nmtui/nmt-address-list.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_ADDRESS_LIST_H +#define NMT_ADDRESS_LIST_H + +#include "nmt-widget-list.h" + +#define NMT_TYPE_ADDRESS_LIST (nmt_address_list_get_type()) +#define NMT_ADDRESS_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList)) +#define NMT_ADDRESS_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) +#define NMT_IS_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_ADDRESS_LIST)) +#define NMT_IS_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_ADDRESS_LIST)) +#define NMT_ADDRESS_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) + +typedef struct { + NmtWidgetList parent; + +} NmtAddressList; + +typedef struct { + NmtWidgetListClass parent; + +} NmtAddressListClass; + +GType nmt_address_list_get_type(void); + +typedef enum { + NMT_ADDRESS_LIST_IP4_WITH_PREFIX, + NMT_ADDRESS_LIST_IP4, + NMT_ADDRESS_LIST_IP6_WITH_PREFIX, + NMT_ADDRESS_LIST_IP6, + NMT_ADDRESS_LIST_HOSTNAME +} NmtAddressListType; + +NmtNewtWidget *nmt_address_list_new(NmtAddressListType list_type); + +#endif /* NMT_ADDRESS_LIST_H */ diff --git a/src/nmtui/nmt-connect-connection-list.c b/src/nmtui/nmt-connect-connection-list.c new file mode 100644 index 00000000..4503dc92 --- /dev/null +++ b/src/nmtui/nmt-connect-connection-list.c @@ -0,0 +1,692 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 - 2017 Red Hat, Inc. + */ + +/** + * SECTION:nmt-connect-connection-list + * @short_description: Connection list for "nmtui connect" + * + * #NmtConnectConnectionList is the list of devices, connections, and + * access points displayed by "nmtui connect". + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmtui.h" +#include "nmt-connect-connection-list.h" +#include "libnmc-base/nm-client-utils.h" + +G_DEFINE_TYPE(NmtConnectConnectionList, nmt_connect_connection_list, NMT_TYPE_NEWT_LISTBOX) + +#define NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + NMT_TYPE_CONNECT_CONNECTION_LIST, \ + NmtConnectConnectionListPrivate)) + +typedef struct { + char * name; + NMDevice *device; + + int sort_order; + + GSList *conns; +} NmtConnectDevice; + +typedef struct { + const char *name; + char * ssid; + + NMConnection * conn; + NMAccessPoint * ap; + NMDevice * device; + NMActiveConnection *active; +} NmtConnectConnection; + +typedef struct { + GSList *nmt_devices; +} NmtConnectConnectionListPrivate; + +/** + * nmt_connect_connection_list_new: + * + * Creates a new #NmtConnectConnectionList + * + * Returns: a new #NmtConnectConnectionList + */ +NmtNewtWidget * +nmt_connect_connection_list_new(void) +{ + return g_object_new(NMT_TYPE_CONNECT_CONNECTION_LIST, + "flags", + NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", + TRUE, + NULL); +} + +static void +nmt_connect_connection_list_init(NmtConnectConnectionList *list) +{} + +static void +nmt_connect_connection_free(NmtConnectConnection *nmtconn) +{ + g_clear_object(&nmtconn->conn); + g_clear_object(&nmtconn->ap); + g_clear_object(&nmtconn->active); + g_free(nmtconn->ssid); + g_slice_free(NmtConnectConnection, nmtconn); +} + +static void +nmt_connect_device_free(NmtConnectDevice *nmtdev) +{ + nm_clear_g_free(&nmtdev->name); + g_clear_object(&nmtdev->device); + + g_slist_free_full(nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free); + g_slice_free(NmtConnectDevice, nmtdev); +} + +static const char *device_sort_order[] = {"NMDeviceEthernet", + "NMDeviceVeth", + "NMDeviceInfiniband", + "NMDeviceWifi", + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_IP_TUNNEL_SETTING_NAME, + "NMDeviceModem", + "NMDeviceBt"}; +static const int device_sort_order_len = G_N_ELEMENTS(device_sort_order); + +static int +get_sort_order_for_device(NMDevice *device) +{ + const char *type; + int i; + + type = G_OBJECT_TYPE_NAME(device); + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp(type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +get_sort_order_for_connection(NMConnection *conn) +{ + NMSettingConnection *s_con; + const char * type; + int i; + + s_con = nm_connection_get_setting_connection(conn); + type = nm_setting_connection_get_connection_type(s_con); + + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp(type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +sort_connections(gconstpointer a, gconstpointer b) +{ + NmtConnectConnection *nmta = (NmtConnectConnection *) a; + NmtConnectConnection *nmtb = (NmtConnectConnection *) b; + + /* If nmta and nmtb both have NMConnections, sort them by timestamp */ + if (nmta->conn && nmtb->conn) { + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection(nmta->conn); + s_con_b = nm_connection_get_setting_connection(nmtb->conn); + + time_a = nm_setting_connection_get_timestamp(s_con_a); + time_b = nm_setting_connection_get_timestamp(s_con_b); + + return (int) (time_b - time_a); + } + + /* If one is an NMConnection and the other is an NMAccessPoint, the + * connection comes first. + */ + if (nmta->conn) + return -1; + else if (nmtb->conn) + return 1; + + g_return_val_if_fail(nmta->ap && nmtb->ap, 0); + + /* If both are access points, then sort by strength */ + return nm_access_point_get_strength(nmtb->ap) - nm_access_point_get_strength(nmta->ap); +} + +static void +add_connections_for_device(NmtConnectDevice *nmtdev, const GPtrArray *connections) +{ + int i; + + for (i = 0; i < connections->len; i++) { + NMConnection * conn = connections->pdata[i]; + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection(conn); + if (nm_setting_connection_get_master(s_con)) + continue; + + if (nm_device_connection_valid(nmtdev->device, conn)) { + NmtConnectConnection *nmtconn = g_slice_new0(NmtConnectConnection); + + nmtconn->name = nm_connection_get_id(conn); + nmtconn->device = nmtdev->device; + nmtconn->conn = g_object_ref(conn); + nmtdev->conns = g_slist_prepend(nmtdev->conns, nmtconn); + } + } +} + +/* stolen from nm-applet */ +static char * +hash_ap(NMAccessPoint *ap) +{ + unsigned char input[66]; + GBytes * ssid; + NM80211Mode mode; + guint32 flags; + guint32 wpa_flags; + guint32 rsn_flags; + + memset(&input[0], 0, sizeof(input)); + + ssid = nm_access_point_get_ssid(ap); + if (ssid) + memcpy(input, g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); + + mode = nm_access_point_get_mode(ap); + if (mode == NM_802_11_MODE_INFRA) + input[32] |= (1 << 0); + else if (mode == NM_802_11_MODE_ADHOC) + input[32] |= (1 << 1); + else + input[32] |= (1 << 2); + + /* Separate out no encryption, WEP-only, and WPA-capable */ + flags = nm_access_point_get_flags(ap); + wpa_flags = nm_access_point_get_wpa_flags(ap); + rsn_flags = nm_access_point_get_rsn_flags(ap); + if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 3); + else if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 4); + else if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags != NM_802_11_AP_SEC_NONE) + && (rsn_flags != NM_802_11_AP_SEC_NONE)) + input[32] |= (1 << 5); + else + input[32] |= (1 << 6); + + /* duplicate it */ + memcpy(&input[33], &input[0], 32); + return g_compute_checksum_for_data(G_CHECKSUM_MD5, input, sizeof(input)); +} + +static void +add_connections_for_aps(NmtConnectDevice *nmtdev, const GPtrArray *connections) +{ + NmtConnectConnection *nmtconn; + NMConnection * conn; + NMAccessPoint * ap; + const GPtrArray * aps; + GHashTable * seen_ssids; + GBytes * ssid; + char * ap_hash; + int i, c; + + aps = nm_device_wifi_get_access_points(NM_DEVICE_WIFI(nmtdev->device)); + if (!aps->len) + return; + + seen_ssids = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + for (i = 0; i < aps->len; i++) { + ap = aps->pdata[i]; + + if (!nm_access_point_get_ssid(ap)) + continue; + + ap_hash = hash_ap(ap); + if (g_hash_table_contains(seen_ssids, ap_hash)) { + g_free(ap_hash); + continue; + } + g_hash_table_add(seen_ssids, ap_hash); + + nmtconn = g_slice_new0(NmtConnectConnection); + nmtconn->device = nmtdev->device; + nmtconn->ap = g_object_ref(ap); + ssid = nm_access_point_get_ssid(ap); + if (ssid) + nmtconn->ssid = + nm_utils_ssid_to_utf8(g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); + + for (c = 0; c < connections->len; c++) { + conn = connections->pdata[c]; + if (nm_device_connection_valid(nmtdev->device, conn) + && nm_access_point_connection_valid(ap, conn)) { + nmtconn->name = nm_connection_get_id(conn); + nmtconn->conn = g_object_ref(conn); + break; + } + } + + if (!nmtconn->name) + nmtconn->name = nmtconn->ssid ?: "<unknown>"; + + nmtdev->conns = g_slist_prepend(nmtdev->conns, nmtconn); + } + + g_hash_table_unref(seen_ssids); +} + +static GSList * +append_nmt_devices_for_devices(GSList * nmt_devices, + const GPtrArray *devices, + char ** names, + const GPtrArray *connections) +{ + NmtConnectDevice *nmtdev; + NMDevice * device; + int i, sort_order; + + for (i = 0; i < devices->len; i++) { + device = devices->pdata[i]; + + sort_order = get_sort_order_for_device(device); + if (sort_order == -1) + continue; + + nmtdev = g_slice_new0(NmtConnectDevice); + nmtdev->name = g_strdup(names[i]); + nmtdev->device = g_object_ref(device); + nmtdev->sort_order = sort_order; + + if (NM_IS_DEVICE_WIFI(device)) + add_connections_for_aps(nmtdev, connections); + else + add_connections_for_device(nmtdev, connections); + nmtdev->conns = g_slist_sort(nmtdev->conns, sort_connections); + + nmt_devices = g_slist_prepend(nmt_devices, nmtdev); + } + + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_virtual_devices(GSList *nmt_devices, const GPtrArray *connections) +{ + NmtConnectDevice * nmtdev = NULL; + int i; + GHashTable * devices_by_name; + char * name; + NMConnection * conn; + NmtConnectConnection *nmtconn; + int sort_order; + + devices_by_name = g_hash_table_new(nm_str_hash, g_str_equal); + + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + sort_order = get_sort_order_for_connection(conn); + if (sort_order == -1) + continue; + + name = nm_connection_get_virtual_device_description(conn); + if (name) + nmtdev = g_hash_table_lookup(devices_by_name, name); + if (nmtdev) + g_free(name); + else { + nmtdev = g_slice_new0(NmtConnectDevice); + nmtdev->name = name ?: g_strdup("Unknown"); + nmtdev->sort_order = sort_order; + + g_hash_table_insert(devices_by_name, nmtdev->name, nmtdev); + nmt_devices = g_slist_prepend(nmt_devices, nmtdev); + } + + nmtconn = g_slice_new0(NmtConnectConnection); + nmtconn->name = nm_connection_get_id(conn); + nmtconn->conn = g_object_ref(conn); + + nmtdev->conns = g_slist_insert_sorted(nmtdev->conns, nmtconn, sort_connections); + } + + g_hash_table_destroy(devices_by_name); + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_vpns(GSList *nmt_devices, const GPtrArray *connections) +{ + NmtConnectDevice * nmtdev; + int i; + NMConnection * conn; + NmtConnectConnection *nmtconn; + + nmtdev = g_slice_new0(NmtConnectDevice); + nmtdev->name = g_strdup(_("VPN")); + nmtdev->sort_order = 100; + + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + if (!nm_connection_is_type(conn, NM_SETTING_VPN_SETTING_NAME)) + continue; + + nmtconn = g_slice_new0(NmtConnectConnection); + nmtconn->name = nm_connection_get_id(conn); + nmtconn->conn = g_object_ref(conn); + + nmtdev->conns = g_slist_insert_sorted(nmtdev->conns, nmtconn, sort_connections); + } + + if (nmtdev->conns) + nmt_devices = g_slist_prepend(nmt_devices, nmtdev); + else + nmt_connect_device_free(nmtdev); + + return nmt_devices; +} + +static int +sort_nmt_devices(gconstpointer a, gconstpointer b) +{ + NmtConnectDevice *nmta = (NmtConnectDevice *) a; + NmtConnectDevice *nmtb = (NmtConnectDevice *) b; + + if (nmta->sort_order != nmtb->sort_order) + return nmta->sort_order - nmtb->sort_order; + + return strcmp(nmta->name, nmtb->name); +} + +static NMActiveConnection * +connection_find_ac(NMConnection *conn, const GPtrArray *acs) +{ + NMActiveConnection *ac; + NMRemoteConnection *ac_conn; + int i; + + for (i = 0; i < acs->len; i++) { + ac = acs->pdata[i]; + ac_conn = nm_active_connection_get_connection(ac); + + if (conn == NM_CONNECTION(ac_conn)) + return ac; + } + + return NULL; +} + +static void +nmt_connect_connection_list_rebuild(NmtConnectConnectionList *list) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(list); + NmtNewtListbox * listbox = NMT_NEWT_LISTBOX(list); + const GPtrArray * devices, *acs, *connections; + int max_width; + char ** names, *row, active_col; + const char * strength_col; + GSList * nmt_devices, *diter, *citer; + NmtConnectDevice * nmtdev; + NmtConnectConnection * nmtconn; + + g_slist_free_full(priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + priv->nmt_devices = NULL; + nmt_newt_listbox_clear(listbox); + + devices = nm_client_get_devices(nm_client); + acs = nm_client_get_active_connections(nm_client); + connections = nm_client_get_connections(nm_client); + + nmt_devices = NULL; + + names = nm_device_disambiguate_names((NMDevice **) devices->pdata, devices->len); + nmt_devices = append_nmt_devices_for_devices(nmt_devices, devices, names, connections); + g_strfreev(names); + + nmt_devices = append_nmt_devices_for_virtual_devices(nmt_devices, connections); + nmt_devices = append_nmt_devices_for_vpns(nmt_devices, connections); + + nmt_devices = g_slist_sort(nmt_devices, sort_nmt_devices); + + max_width = 0; + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + max_width = MAX(max_width, nmt_newt_text_width(nmtconn->name)); + } + } + + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + + if (nmtdev->conns) { + if (diter != nmt_devices) + nmt_newt_listbox_append(listbox, "", NULL); + nmt_newt_listbox_append(listbox, nmtdev->name, NULL); + } + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + if (nmtconn->conn) + nmtconn->active = connection_find_ac(nmtconn->conn, acs); + if (nmtconn->active) { + g_object_ref(nmtconn->active); + active_col = '*'; + } else + active_col = ' '; + + if (nmtconn->ap) { + guint8 strength = nm_access_point_get_strength(nmtconn->ap); + + strength_col = nmc_wifi_strength_bars(strength); + } else + strength_col = NULL; + + row = g_strdup_printf("%c %s%-*s%s%s", + active_col, + nmtconn->name, + (int) (max_width - nmt_newt_text_width(nmtconn->name)), + "", + strength_col ? " " : "", + strength_col ?: ""); + + nmt_newt_listbox_append(listbox, row, nmtconn); + g_free(row); + } + } + + priv->nmt_devices = nmt_devices; + + g_object_notify(G_OBJECT(listbox), "active"); + g_object_notify(G_OBJECT(listbox), "active-key"); +} + +static void +rebuild_on_property_changed(GObject *object, GParamSpec *spec, gpointer list) +{ + nmt_connect_connection_list_rebuild(list); +} + +static void +nmt_connect_connection_list_constructed(GObject *object) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST(object); + + g_signal_connect(nm_client, + "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK(rebuild_on_property_changed), + list); + g_signal_connect(nm_client, + "notify::" NM_CLIENT_CONNECTIONS, + G_CALLBACK(rebuild_on_property_changed), + list); + g_signal_connect(nm_client, + "notify::" NM_CLIENT_DEVICES, + G_CALLBACK(rebuild_on_property_changed), + list); + + nmt_connect_connection_list_rebuild(list); + + G_OBJECT_CLASS(nmt_connect_connection_list_parent_class)->constructed(object); +} + +static void +nmt_connect_connection_list_finalize(GObject *object) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(object); + + g_slist_free_full(priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + + g_signal_handlers_disconnect_by_func(nm_client, + G_CALLBACK(rebuild_on_property_changed), + object); + + G_OBJECT_CLASS(nmt_connect_connection_list_parent_class)->finalize(object); +} + +static void +nmt_connect_connection_list_class_init(NmtConnectConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(list_class); + + g_type_class_add_private(list_class, sizeof(NmtConnectConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_connect_connection_list_constructed; + object_class->finalize = nmt_connect_connection_list_finalize; +} + +/** + * nmt_connect_connection_list_get_connection: + * @list: an #NmtConnectConnectionList + * @identifier: a connection ID or UUID, or device name + * @connection: (out) (transfer none): the #NMConnection to be activated + * @device: (out) (transfer none): the #NMDevice to activate @connection on + * @specific_object: (out) (transfer none): the "specific object" to connect to + * @active: (out) (transfer none): the #NMActiveConnection corresponding + * to the selection, if any. + * + * Gets information about the indicated connection. + * + * Returns: %TRUE if there was a match, %FALSE if not. + */ +gboolean +nmt_connect_connection_list_get_connection(NmtConnectConnectionList *list, + const char * identifier, + NMConnection ** connection, + NMDevice ** device, + NMObject ** specific_object, + NMActiveConnection ** active) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(list); + GSList * diter, *citer; + NmtConnectDevice * nmtdev; + NmtConnectConnection * nmtconn = NULL; + NMConnection * conn = NULL; + + g_return_val_if_fail(identifier, FALSE); + + if (nm_utils_is_uuid(identifier)) + conn = NM_CONNECTION(nm_client_get_connection_by_uuid(nm_client, identifier)); + if (!conn) + conn = NM_CONNECTION(nm_client_get_connection_by_id(nm_client, identifier)); + + for (diter = priv->nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + if (!nmtdev->conns) + continue; + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + if (conn) { + if (conn == nmtconn->conn) + goto found; + } else if (nm_streq0(identifier, nmtconn->ssid)) + goto found; + } + + if (!conn && nmtdev->device + && nm_streq0(identifier, nm_device_get_ip_iface(nmtdev->device))) { + nmtconn = nmtdev->conns->data; + goto found; + } + } + + return FALSE; + +found: + if (connection) + *connection = nmtconn->conn; + if (device) + *device = nmtconn->device; + if (specific_object) + *specific_object = NM_OBJECT(nmtconn->ap); + if (active) + *active = nmtconn->active; + + return TRUE; +} + +/** + * nmt_connect_connection_list_get_selection: + * @list: an #NmtConnectConnectionList + * @connection: (out) (transfer none): the #NMConnection to be activated + * @device: (out) (transfer none): the #NMDevice to activate @connection on + * @specific_object: (out) (transfer none): the "specific object" to connect to + * @active: (out) (transfer none): the #NMActiveConnection corresponding + * to the selection, if any. + * + * Gets information about the selected row. + * + * Returns: %TRUE if there is a selection, %FALSE if not. + */ +gboolean +nmt_connect_connection_list_get_selection(NmtConnectConnectionList *list, + NMConnection ** connection, + NMDevice ** device, + NMObject ** specific_object, + NMActiveConnection ** active) +{ + NmtConnectConnection *nmtconn; + + nmtconn = nmt_newt_listbox_get_active_key(NMT_NEWT_LISTBOX(list)); + if (!nmtconn) + return FALSE; + + if (connection) + *connection = nmtconn->conn; + if (device) + *device = nmtconn->device; + if (specific_object) + *specific_object = NM_OBJECT(nmtconn->ap); + if (active) + *active = nmtconn->active; + + return TRUE; +} diff --git a/src/nmtui/nmt-connect-connection-list.h b/src/nmtui/nmt-connect-connection-list.h new file mode 100644 index 00000000..15a319b1 --- /dev/null +++ b/src/nmtui/nmt-connect-connection-list.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_CONNECT_CONNECTION_LIST_H +#define NMT_CONNECT_CONNECTION_LIST_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_CONNECT_CONNECTION_LIST (nmt_connect_connection_list_get_type()) +#define NMT_CONNECT_CONNECTION_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList)) +#define NMT_CONNECT_CONNECTION_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + NMT_TYPE_CONNECT_CONNECTION_LIST, \ + NmtConnectConnectionListClass)) +#define NMT_IS_CONNECT_CONNECTION_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_IS_CONNECT_CONNECTION_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_CONNECT_CONNECTION_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + NMT_TYPE_CONNECT_CONNECTION_LIST, \ + NmtConnectConnectionListClass)) + +typedef struct { + NmtNewtListbox parent; + +} NmtConnectConnectionList; + +typedef struct { + NmtNewtListboxClass parent; + +} NmtConnectConnectionListClass; + +GType nmt_connect_connection_list_get_type(void); + +NmtNewtWidget *nmt_connect_connection_list_new(void); + +gboolean nmt_connect_connection_list_get_connection(NmtConnectConnectionList *list, + const char * identifier, + NMConnection ** connection, + NMDevice ** device, + NMObject ** specific_object, + NMActiveConnection ** active); +gboolean nmt_connect_connection_list_get_selection(NmtConnectConnectionList *list, + NMConnection ** connection, + NMDevice ** device, + NMObject ** specific_object, + NMActiveConnection ** active); + +#endif /* NMT_CONNECT_CONNECTION_LIST_H */ diff --git a/src/nmtui/nmt-device-entry.c b/src/nmtui/nmt-device-entry.c new file mode 100644 index 00000000..e831f4e4 --- /dev/null +++ b/src/nmtui/nmt-device-entry.c @@ -0,0 +1,555 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-device-entry + * @short_description: #NmtNewtEntry for identifying a device + * + * #NmtDeviceEntry provides a widget for identifying a device, either + * by interface name or by hardware address. The user can enter either + * value, and the entry's #NmtDeviceEntry:interface-name or + * #NmtDeviceEntry:mac-address property will be set accordingly. If + * the entry recognizes the interface name or mac address typed in as + * matching a known #NMDevice, then it will also display the other + * property in parentheses. + * + * FIXME: #NmtDeviceEntry is currently an #NmtEditorGrid object, so that + * we can possibly eventually add a button to its "extra" field, that + * would pop up a form for selecting a device. But if we're not going + * to implement that then we should make it just an #NmtNewtEntry. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-device-entry.h" + +#include <sys/socket.h> +#include <linux/if_arp.h> + +#include "nmtui.h" + +G_DEFINE_TYPE(NmtDeviceEntry, nmt_device_entry, NMT_TYPE_EDITOR_GRID) + +#define NMT_DEVICE_ENTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryPrivate)) + +typedef struct { + GType hardware_type; + NmtDeviceEntryDeviceFilter device_filter; + gpointer device_filter_data; + int arptype; + + char *interface_name; + char *mac_address; + + char * label; + NmtNewtEntry * entry; + NmtNewtWidget *button; + + gboolean updating; +} NmtDeviceEntryPrivate; + +enum { + PROP_0, + PROP_LABEL, + PROP_WIDTH, + PROP_HARDWARE_TYPE, + PROP_INTERFACE_NAME, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nmt_device_entry_new: + * @label: the label for the entry + * @width: the width of the entry + * @hardware_type: the type of #NMDevice to be selected, or + * %G_TYPE_NONE if this is for a virtual device type. + * + * Creates a new #NmtDeviceEntry, for identifying a device of type + * @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not + * set a #NmtDeviceEntryDeviceFilter), then this will only allow + * specifying an interface name, not a hardware address. + * + * Returns: a new #NmtDeviceEntry. + */ +NmtNewtWidget * +nmt_device_entry_new(const char *label, int width, GType hardware_type) +{ + return g_object_new(NMT_TYPE_DEVICE_ENTRY, + "label", + label, + "width", + width, + "hardware-type", + hardware_type, + NULL); +} + +static gboolean +device_entry_parse(NmtDeviceEntry *deventry, + const char * text, + char ** interface_name, + char ** mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + char ** words; + int len; + + *interface_name = *mac_address = NULL; + if (!*text) + return TRUE; + + if (priv->hardware_type == G_TYPE_NONE && !priv->device_filter) { + if (nm_utils_ifname_valid_kernel(text, NULL)) { + *interface_name = g_strdup(text); + return TRUE; + } else + return FALSE; + } + + words = g_strsplit(text, " ", -1); + if (g_strv_length(words) > 2) { + g_strfreev(words); + return FALSE; + } + + if (words[1]) { + len = strlen(words[1]); + if (len < 3 || words[1][0] != '(' || words[1][len - 1] != ')') + goto fail; + + memmove(words[1], words[1] + 1, len - 2); + words[1][len - 2] = '\0'; + } + + len = nm_utils_hwaddr_len(priv->arptype); + if (nm_utils_hwaddr_aton(words[0], buf, len) + && (!words[1] || nm_utils_ifname_valid_kernel(words[1], NULL))) { + *mac_address = words[0]; + *interface_name = NULL; + g_free(words); + return TRUE; + } else if (nm_utils_ifname_valid_kernel(words[0], NULL) + && (!words[1] || nm_utils_hwaddr_aton(words[1], buf, len))) { + *interface_name = words[0]; + *mac_address = NULL; + g_free(words); + return TRUE; + } + +fail: + g_strfreev(words); + return FALSE; +} + +static gboolean +device_entry_validate(NmtNewtEntry *entry, const char *text, gpointer user_data) +{ + NmtDeviceEntry *deventry = user_data; + char * ifname, *mac; + + if (!device_entry_parse(deventry, text, &ifname, &mac)) + return FALSE; + + g_free(ifname); + g_free(mac); + return TRUE; +} + +static NMDevice * +find_device_by_interface_name(NmtDeviceEntry *deventry, const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + const GPtrArray * devices; + NMDevice * device = NULL; + int i; + + devices = nm_client_get_devices(nm_client); + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + + if (priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE(candidate, priv->hardware_type)) + continue; + + if (priv->device_filter + && !priv->device_filter(deventry, candidate, priv->device_filter_data)) + continue; + + if (!g_strcmp0(interface_name, nm_device_get_iface(candidate))) + device = candidate; + } + + return device; +} + +static NMDevice * +find_device_by_mac_address(NmtDeviceEntry *deventry, const char *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + const GPtrArray * devices; + NMDevice * device = NULL; + int i; + + devices = nm_client_get_devices(nm_client); + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + char * hwaddr; + + if (priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE(candidate, priv->hardware_type)) + continue; + + if (priv->device_filter + && !priv->device_filter(deventry, candidate, priv->device_filter_data)) + continue; + + g_object_get(G_OBJECT(candidate), "hw-address", &hwaddr, NULL); + if (hwaddr && !g_ascii_strcasecmp(mac_address, hwaddr)) + device = candidate; + g_free(hwaddr); + } + + return device; +} + +static void +update_entry(NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + const char * ifname; + char * mac, *text; + NMDevice * ifname_device, *mac_device; + + if (priv->interface_name) { + ifname = priv->interface_name; + ifname_device = find_device_by_interface_name(deventry, priv->interface_name); + } else { + ifname = NULL; + ifname_device = NULL; + } + + if (priv->mac_address) { + mac = g_strdup(priv->mac_address); + mac_device = find_device_by_mac_address(deventry, priv->mac_address); + } else { + mac = NULL; + mac_device = NULL; + } + + if (!ifname && mac_device) + ifname = nm_device_get_iface(mac_device); + if (!mac && ifname_device && (priv->hardware_type != G_TYPE_NONE)) + g_object_get(G_OBJECT(ifname_device), "hw-address", &mac, NULL); + + if (ifname_device && mac_device && ifname_device != mac_device) { + /* Mismatch! */ + text = g_strdup_printf("%s != %s", priv->interface_name, mac); + } else if (ifname && mac) { + if (ifname_device) + text = g_strdup_printf("%s (%s)", ifname, mac); + else + text = g_strdup_printf("%s (%s)", mac, ifname); + } else if (ifname) + text = g_strdup(ifname); + else if (mac) + text = g_strdup(mac); + else + text = g_strdup(""); + + priv->updating = TRUE; + g_object_set(G_OBJECT(priv->entry), "text", text, NULL); + priv->updating = FALSE; + g_free(text); + + g_free(mac); +} + +static gboolean +nmt_device_entry_set_interface_name(NmtDeviceEntry *deventry, const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + + if (g_strcmp0(interface_name, priv->interface_name) != 0) { + g_free(priv->interface_name); + priv->interface_name = g_strdup(interface_name); + + g_object_notify(G_OBJECT(deventry), "interface-name"); + return TRUE; + } else + return FALSE; +} + +static gboolean +nmt_device_entry_set_mac_address(NmtDeviceEntry *deventry, const char *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + gboolean changed; + + if (mac_address && !priv->mac_address) { + priv->mac_address = g_strdup(mac_address); + changed = TRUE; + } else if (!mac_address && priv->mac_address) { + nm_clear_g_free(&priv->mac_address); + changed = TRUE; + } else if (mac_address && priv->mac_address + && !nm_utils_hwaddr_matches(mac_address, -1, priv->mac_address, -1)) { + g_free(priv->mac_address); + priv->mac_address = g_strdup(mac_address); + changed = TRUE; + } else + changed = FALSE; + + if (changed) + g_object_notify(G_OBJECT(deventry), "mac-address"); + return changed; +} + +static void +entry_text_changed(GObject *object, GParamSpec *pspec, gpointer deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + const char * text; + char * ifname, *mac; + + if (priv->updating) + return; + + text = nmt_newt_entry_get_text(priv->entry); + if (!device_entry_parse(deventry, text, &ifname, &mac)) + return; + + nmt_device_entry_set_interface_name(deventry, ifname); + g_free(ifname); + + nmt_device_entry_set_mac_address(deventry, mac); + g_free(mac); +} + +static void +nmt_device_entry_init(NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + NmtNewtWidget * entry; + + priv->hardware_type = G_TYPE_NONE; + + entry = nmt_newt_entry_new(-1, 0); + priv->entry = NMT_NEWT_ENTRY(entry); + nmt_newt_entry_set_validator(priv->entry, device_entry_validate, deventry); + g_signal_connect(priv->entry, "notify::text", G_CALLBACK(entry_text_changed), deventry); + +#if 0 + priv->button = nmt_newt_button_new (_("Select...")); + g_signal_connect (priv->button, "clicked", + G_CALLBACK (do_select_dialog), deventry); +#endif +} + +static void +nmt_device_entry_constructed(GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(object); + + nmt_editor_grid_append(NMT_EDITOR_GRID(object), + priv->label, + NMT_NEWT_WIDGET(priv->entry), + NULL); + + G_OBJECT_CLASS(nmt_device_entry_parent_class)->constructed(object); +} + +static void +nmt_device_entry_finalize(GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(object); + + g_free(priv->interface_name); + g_free(priv->mac_address); + + G_OBJECT_CLASS(nmt_device_entry_parent_class)->finalize(object); +} + +/** + * NmtDeviceEntryDeviceFilter: + * @deventry: the #NmtDeviceEntry + * @device: an #NMDevice + * @user_data: user data + * + * Filter function for determining which devices can be specified + * on an entry. + * + * Returns: %TRUE if @device is acceptable for @deventry + */ + +/** + * nmt_device_entry_set_device_filter: + * @deventry: the #NmtDeviceEntry + * @filter: the filter + * @user_data: data for @filter + * + * Sets a device filter on @deventry. Only devices that pass @filter + * will be recognized by @deventry. + * + * If the entry's #NmtDeviceEntry:hardware-type is not %G_TYPE_NONE, + * then only devices that both match the hardware type and are + * accepted by the filter will be allowed. + */ +void +nmt_device_entry_set_device_filter(NmtDeviceEntry * deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + + priv->device_filter = filter; + priv->device_filter_data = user_data; +} + +static void +nmt_device_entry_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtDeviceEntry * deventry = NMT_DEVICE_ENTRY(object); + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + const char * interface_name; + const char * mac_address; + + switch (prop_id) { + case PROP_LABEL: + priv->label = g_value_dup_string(value); + break; + case PROP_WIDTH: + nmt_newt_entry_set_width(priv->entry, g_value_get_int(value)); + break; + case PROP_HARDWARE_TYPE: + priv->hardware_type = g_value_get_gtype(value); + priv->arptype = + (priv->hardware_type == NM_TYPE_DEVICE_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER; + break; + case PROP_INTERFACE_NAME: + interface_name = g_value_get_string(value); + if (nmt_device_entry_set_interface_name(deventry, interface_name)) + update_entry(deventry); + break; + case PROP_MAC_ADDRESS: + mac_address = g_value_get_string(value); + if (nmt_device_entry_set_mac_address(deventry, mac_address)) + update_entry(deventry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string(value, priv->label); + break; + case PROP_WIDTH: + g_value_set_int(value, nmt_newt_entry_get_width(priv->entry)); + break; + case PROP_HARDWARE_TYPE: + g_value_set_gtype(value, priv->hardware_type); + break; + case PROP_INTERFACE_NAME: + g_value_set_string(value, priv->interface_name); + break; + case PROP_MAC_ADDRESS: + g_value_set_string(value, priv->mac_address); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_class_init(NmtDeviceEntryClass *deventry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(deventry_class); + + g_type_class_add_private(deventry_class, sizeof(NmtDeviceEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_device_entry_constructed; + object_class->set_property = nmt_device_entry_set_property; + object_class->get_property = nmt_device_entry_get_property; + object_class->finalize = nmt_device_entry_finalize; + + /** + * NmtDeviceEntry:label: + * + * The entry's label + */ + g_object_class_install_property( + object_class, + PROP_LABEL, + g_param_spec_string("label", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:width: + * + * The entry's width in characters + */ + g_object_class_install_property( + object_class, + PROP_WIDTH, + g_param_spec_int("width", "", "", -1, 80, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:hardware-type: + * + * The type of #NMDevice to limit the entry to, or %G_TYPE_NONE + * if the entry is for a virtual device and should not accept + * hardware addresses. + */ + g_object_class_install_property(object_class, + PROP_HARDWARE_TYPE, + g_param_spec_gtype("hardware-type", + "", + "", + G_TYPE_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:interface-name: + * + * The interface name of the device identified by the entry. + */ + g_object_class_install_property( + object_class, + PROP_INTERFACE_NAME, + g_param_spec_string("interface-name", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:mac-address: + * + * The hardware address of the device identified by the entry. + */ + g_object_class_install_property( + object_class, + PROP_MAC_ADDRESS, + g_param_spec_string("mac-address", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-device-entry.h b/src/nmtui/nmt-device-entry.h new file mode 100644 index 00000000..13f82bc8 --- /dev/null +++ b/src/nmtui/nmt-device-entry.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_DEVICE_ENTRY_H +#define NMT_DEVICE_ENTRY_H + +#include "nmt-editor-grid.h" + +#define NMT_TYPE_DEVICE_ENTRY (nmt_device_entry_get_type()) +#define NMT_DEVICE_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntry)) +#define NMT_DEVICE_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) +#define NMT_IS_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_IS_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_DEVICE_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) + +typedef struct { + NmtEditorGrid parent; + +} NmtDeviceEntry; + +typedef struct { + NmtEditorGridClass parent; + +} NmtDeviceEntryClass; + +GType nmt_device_entry_get_type(void); + +NmtNewtWidget *nmt_device_entry_new(const char *label, int width, GType hardware_type); + +typedef gboolean (*NmtDeviceEntryDeviceFilter)(NmtDeviceEntry *deventry, + NMDevice * device, + gpointer user_data); +void nmt_device_entry_set_device_filter(NmtDeviceEntry * deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data); + +#endif /* NMT_DEVICE_ENTRY_H */ diff --git a/src/nmtui/nmt-edit-connection-list.c b/src/nmtui/nmt-edit-connection-list.c new file mode 100644 index 00000000..9b31e06f --- /dev/null +++ b/src/nmtui/nmt-edit-connection-list.c @@ -0,0 +1,584 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-edit-connection-list + * @short_description: Connection list for "nmtui edit" + * + * #NmtEditConnectionList is the list of connections displayed by + * "nmtui edit". + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" + +#include "nm-editor-utils.h" + +G_DEFINE_TYPE(NmtEditConnectionList, nmt_edit_connection_list, NMT_TYPE_NEWT_GRID) + +#define NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListPrivate)) + +typedef struct { + GSList *connections; + + gboolean grouped; + NmtEditConnectionListFilter connection_filter; + gpointer connection_filter_data; + + NmtNewtListbox * listbox; + NmtNewtButtonBox *buttons; + + NmtNewtWidget *add; + NmtNewtWidget *edit; + NmtNewtWidget *delete; + NmtNewtWidget *extra; +} NmtEditConnectionListPrivate; + +enum { + PROP_0, + + PROP_GROUPED, + PROP_CONNECTION_FILTER, + PROP_CONNECTION_FILTER_DATA, + PROP_EXTRA_WIDGET, + PROP_CONNECTIONS, + PROP_NUM_CONNECTIONS, + + LAST_PROP +}; + +enum { + ADD_CONNECTION, + EDIT_CONNECTION, + REMOVE_CONNECTION, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void add_clicked(NmtNewtButton *button, gpointer list); +static void edit_clicked(NmtNewtButton *button, gpointer list); +static void delete_clicked(NmtNewtButton *button, gpointer list); +static void listbox_activated(NmtNewtWidget *listbox, gpointer list); + +static void +nmt_edit_connection_list_init(NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + NmtNewtWidget * listbox, *buttons; + NmtNewtGrid * grid = NMT_NEWT_GRID(list); + + listbox = g_object_new(NMT_TYPE_NEWT_LISTBOX, + "flags", + NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", + TRUE, + NULL); + priv->listbox = NMT_NEWT_LISTBOX(listbox); + nmt_newt_grid_add(grid, listbox, 0, 0); + nmt_newt_grid_set_flags(grid, + listbox, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | NMT_NEWT_GRID_EXPAND_X + | NMT_NEWT_GRID_EXPAND_Y); + g_signal_connect(priv->listbox, "activated", G_CALLBACK(listbox_activated), list); + + buttons = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_VERTICAL); + priv->buttons = NMT_NEWT_BUTTON_BOX(buttons); + nmt_newt_grid_add(grid, buttons, 1, 0); + nmt_newt_widget_set_padding(buttons, 1, 1, 0, 1); + nmt_newt_grid_set_flags(grid, + buttons, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | NMT_NEWT_GRID_EXPAND_Y); + + priv->add = nmt_newt_button_box_add_start(priv->buttons, _("Add")); + g_signal_connect(priv->add, "clicked", G_CALLBACK(add_clicked), list); + + priv->edit = nmt_newt_button_box_add_start(priv->buttons, _("Edit...")); + g_signal_connect(priv->edit, "clicked", G_CALLBACK(edit_clicked), list); + + priv->delete = nmt_newt_button_box_add_start(priv->buttons, _("Delete")); + g_signal_connect(priv->delete, "clicked", G_CALLBACK(delete_clicked), list); +} + +static int +sort_by_timestamp(gconstpointer a, gconstpointer b) +{ + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection((NMConnection *) a); + s_con_b = nm_connection_get_setting_connection((NMConnection *) b); + + time_a = nm_setting_connection_get_timestamp(s_con_a); + time_b = nm_setting_connection_get_timestamp(s_con_b); + + return (int) (time_b - time_a); +} + +static void nmt_edit_connection_list_rebuild(NmtEditConnectionList *list); + +static void +rebuild_on_connection_changed(NMRemoteConnection *connection, gpointer list) +{ + nmt_edit_connection_list_rebuild(list); +} + +static void +free_connections(NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + NMConnection * conn; + GSList * iter; + + for (iter = priv->connections; iter; iter = iter->next) { + conn = iter->data; + + g_signal_handlers_disconnect_by_func(conn, G_CALLBACK(rebuild_on_connection_changed), list); + g_object_unref(conn); + } + g_slist_free(priv->connections); + priv->connections = NULL; +} + +static void +nmt_edit_connection_list_rebuild(NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + const GPtrArray * connections; + GSList * iter; + gboolean did_header = FALSE, did_vpn = FALSE; + NMEditorConnectionTypeData ** types; + NMConnection * conn, *selected_conn; + int i, row, selected_row; + + selected_row = nmt_newt_listbox_get_active(priv->listbox); + selected_conn = nmt_newt_listbox_get_active_key(priv->listbox); + + free_connections(list); + connections = nm_client_get_connections(nm_client); + for (i = 0; i < connections->len; i++) { + conn = connections->pdata[i]; + + if (priv->connection_filter + && !priv->connection_filter(list, conn, priv->connection_filter_data)) + continue; + + g_signal_connect(conn, + NM_CONNECTION_CHANGED, + G_CALLBACK(rebuild_on_connection_changed), + list); + priv->connections = g_slist_prepend(priv->connections, g_object_ref(conn)); + } + priv->connections = g_slist_sort(priv->connections, sort_by_timestamp); + g_object_notify(G_OBJECT(list), "connections"); + g_object_notify(G_OBJECT(list), "num-connections"); + + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->edit), priv->connections != NULL); + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->delete), priv->connections != NULL); + + nmt_newt_listbox_clear(priv->listbox); + + if (!priv->grouped) { + /* Just add the connections in order */ + for (iter = priv->connections, row = 0; iter; iter = iter->next, row++) { + conn = iter->data; + nmt_newt_listbox_append(priv->listbox, nm_connection_get_id(conn), conn); + if (conn == selected_conn) + selected_row = row; + } + if (selected_row >= row) + selected_row = row - 1; + nmt_newt_listbox_set_active(priv->listbox, selected_row); + + return; + } + + types = nm_editor_utils_get_connection_type_list(); + for (i = row = 0; types[i]; i++) { + if (types[i]->setting_type == NM_TYPE_SETTING_VPN) { + if (did_vpn) + continue; + did_vpn = TRUE; + } + + did_header = FALSE; + + for (iter = priv->connections; iter; iter = iter->next) { + NMSetting *setting; + char * indented; + + conn = iter->data; + setting = nm_connection_get_setting(conn, types[i]->setting_type); + if (!setting) + continue; + if (!nm_connection_is_type(conn, nm_setting_get_name(setting))) + continue; + + if (!did_header) { + nmt_newt_listbox_append(priv->listbox, types[i]->name, NULL); + if (row == selected_row) + selected_row++; + row++; + did_header = TRUE; + } + + indented = g_strdup_printf(" %s", nm_connection_get_id(conn)); + nmt_newt_listbox_append(priv->listbox, indented, conn); + g_free(indented); + + if (conn == selected_conn) + selected_row = row; + row++; + } + } + + if (selected_row >= row) + selected_row = row - 1; + nmt_newt_listbox_set_active(priv->listbox, selected_row); +} + +static void +rebuild_on_connections_changed(GObject *object, GParamSpec *pspec, gpointer list) +{ + nmt_edit_connection_list_rebuild(list); +} + +static void +nmt_edit_connection_list_constructed(GObject *object) +{ + NmtEditConnectionList * list = NMT_EDIT_CONNECTION_LIST(object); + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + + if (priv->extra) + nmt_newt_button_box_add_widget_end(priv->buttons, priv->extra); + + g_signal_connect(nm_client, + "notify::" NM_CLIENT_CONNECTIONS, + G_CALLBACK(rebuild_on_connections_changed), + list); + + nmt_edit_connection_list_rebuild(list); + + G_OBJECT_CLASS(nmt_edit_connection_list_parent_class)->constructed(object); +} + +static void +add_clicked(NmtNewtButton *button, gpointer list) +{ + g_signal_emit(list, signals[ADD_CONNECTION], 0); +} + +static void +edit_clicked(NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + NMConnection * connection; + + connection = nmt_newt_listbox_get_active_key(priv->listbox); + g_return_if_fail(connection != NULL); + + g_signal_emit(list, signals[EDIT_CONNECTION], 0, connection); +} + +static void +delete_clicked(NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + NMRemoteConnection * connection; + + connection = nmt_newt_listbox_get_active_key(priv->listbox); + g_return_if_fail(connection != NULL); + + g_signal_emit(list, signals[REMOVE_CONNECTION], 0, connection); +} + +static void +listbox_activated(NmtNewtWidget *listbox, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + + edit_clicked(NMT_NEWT_BUTTON(priv->edit), list); +} + +static void +connection_saved(GObject *conn, GAsyncResult *result, gpointer user_data) +{ + nm_remote_connection_save_finish(NM_REMOTE_CONNECTION(conn), result, NULL); +} + +void +nmt_edit_connection_list_recommit(NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(list); + NMConnection * conn; + GSList * iter; + + for (iter = priv->connections; iter; iter = iter->next) { + conn = iter->data; + + if (NM_IS_REMOTE_CONNECTION(conn) + && (nm_remote_connection_get_unsaved(NM_REMOTE_CONNECTION(conn)) == FALSE)) { + nm_remote_connection_save_async(NM_REMOTE_CONNECTION(conn), + NULL, + connection_saved, + NULL); + } + } +} + +static void +nmt_edit_connection_list_finalize(GObject *object) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(object); + + free_connections(NMT_EDIT_CONNECTION_LIST(object)); + g_clear_object(&priv->extra); + + G_OBJECT_CLASS(nmt_edit_connection_list_parent_class)->finalize(object); +} + +static void +nmt_edit_connection_list_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_GROUPED: + priv->grouped = g_value_get_boolean(value); + break; + case PROP_CONNECTION_FILTER: + priv->connection_filter = g_value_get_pointer(value); + break; + case PROP_CONNECTION_FILTER_DATA: + priv->connection_filter_data = g_value_get_pointer(value); + break; + case PROP_EXTRA_WIDGET: + priv->extra = g_value_get_object(value); + if (priv->extra) + g_object_ref_sink(priv->extra); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_get_property(GObject * object, + guint prop_id, + GValue * value, + GParamSpec *pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(object); + GPtrArray * connections; + GSList * iter; + + switch (prop_id) { + case PROP_GROUPED: + g_value_set_boolean(value, priv->grouped); + break; + case PROP_CONNECTION_FILTER: + g_value_set_pointer(value, priv->connection_filter); + break; + case PROP_CONNECTION_FILTER_DATA: + g_value_set_pointer(value, priv->connection_filter_data); + break; + case PROP_EXTRA_WIDGET: + g_value_set_object(value, priv->extra); + break; + case PROP_CONNECTIONS: + connections = g_ptr_array_new_with_free_func(g_object_unref); + for (iter = priv->connections; iter; iter = iter->next) + g_ptr_array_add(connections, g_object_ref(iter->data)); + g_value_take_boxed(value, connections); + break; + case PROP_NUM_CONNECTIONS: + g_value_set_int(value, g_slist_length(priv->connections)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_class_init(NmtEditConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(list_class); + + g_type_class_add_private(list_class, sizeof(NmtEditConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_edit_connection_list_constructed; + object_class->set_property = nmt_edit_connection_list_set_property; + object_class->get_property = nmt_edit_connection_list_get_property; + object_class->finalize = nmt_edit_connection_list_finalize; + + /* signals */ + + /** + * NmtEditConnectionList::add-connection: + * @list: the #NmtEditConnectionList + * + * Emitted when the user clicks the list's "Add" button. + */ + signals[ADD_CONNECTION] = + g_signal_new("add-connection", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtEditConnectionListClass, add_connection), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /** + * NmtEditConnectionList::edit-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to edit + * + * Emitted when the user clicks the list's "Edit" button, or + * hits "Return" on the listbox. + */ + signals[EDIT_CONNECTION] = + g_signal_new("edit-connection", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtEditConnectionListClass, edit_connection), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_CONNECTION); + + /** + * NmtEditConnectionList::remove-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to remove + * + * Emitted when the user clicks the list's "Delete" button. + */ + signals[REMOVE_CONNECTION] = + g_signal_new("remove-connection", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtEditConnectionListClass, remove_connection), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + NM_TYPE_CONNECTION); + + /* properties */ + + /** + * NmtEditConnectionList:grouped: + * + * If %TRUE, connections should be grouped by type, with headers + * indicating the types (as in the main connection list). If %FALSE, + * they will not be grouped (as in slave connection lists). + */ + g_object_class_install_property( + object_class, + PROP_GROUPED, + g_param_spec_boolean("grouped", + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionListFilter: + * @list: the #NmtEditConnectionList + * @connection: an #NMConnection + * @user_data: the user data + * + * Decides whether @connection should be displayed in @list. + * + * Returns: %TRUE or %FALSE + */ + /** + * NmtEditConnectionList:connection-filter: + * + * A callback function for filtering which connections appear in + * the list. + */ + g_object_class_install_property( + object_class, + PROP_CONNECTION_FILTER, + g_param_spec_pointer("connection-filter", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtEditConnectionList:connection-filter-data: + * + * Data for the #NmtEditConnectionList:connection-filter. + */ + g_object_class_install_property( + object_class, + PROP_CONNECTION_FILTER_DATA, + g_param_spec_pointer("connection-filter-data", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:extra-widget: + * + * An extra button widget to display at the bottom of the button + * box. + */ + g_object_class_install_property( + object_class, + PROP_EXTRA_WIDGET, + g_param_spec_object("extra-widget", + "", + "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:connections: + * + * The list of connections in the widget. + * + * Element-Type: #NMConnection + */ + g_object_class_install_property(object_class, + PROP_CONNECTIONS, + g_param_spec_boxed("connections", + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:num-connections: + * + * The number of connections in the widget. + */ + g_object_class_install_property(object_class, + PROP_NUM_CONNECTIONS, + g_param_spec_int("num-connections", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-edit-connection-list.h b/src/nmtui/nmt-edit-connection-list.h new file mode 100644 index 00000000..824f767a --- /dev/null +++ b/src/nmtui/nmt-edit-connection-list.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDIT_CONNECTION_LIST_H +#define NMT_EDIT_CONNECTION_LIST_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_EDIT_CONNECTION_LIST (nmt_edit_connection_list_get_type()) +#define NMT_EDIT_CONNECTION_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionList)) +#define NMT_EDIT_CONNECTION_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) +#define NMT_IS_EDIT_CONNECTION_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_IS_EDIT_CONNECTION_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_EDIT_CONNECTION_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtEditConnectionList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_connection)(NmtEditConnectionList *list); + void (*edit_connection)(NmtEditConnectionList *list, NMConnection *connection); + void (*remove_connection)(NmtEditConnectionList *list, NMRemoteConnection *connection); +} NmtEditConnectionListClass; + +GType nmt_edit_connection_list_get_type(void); + +typedef gboolean (*NmtEditConnectionListFilter)(NmtEditConnectionList *list, + NMConnection * connection, + gpointer user_data); + +void nmt_edit_connection_list_recommit(NmtEditConnectionList *list); + +#endif /* NMT_EDIT_CONNECTION_LIST_H */ diff --git a/src/nmtui/nmt-editor-grid.c b/src/nmtui/nmt-editor-grid.c new file mode 100644 index 00000000..8c79e8d9 --- /dev/null +++ b/src/nmtui/nmt-editor-grid.c @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-grid + * @short_description: Grid widget for #NmtEditorPages + * + * #NmtEditorGrid is the layout grid used by #NmtEditorPages. It + * consists of a number of rows, each containing either a single + * widget that spans the entire width of the row, or else containing a + * label, a widget, and an optional extra widget. + * + * Each row of the grid can take up multiple on-screen rows, if + * its main widget is multiple rows high. The label and extra widgets + * will be top-aligned if the row is taller than they are. + * + * The #NmtEditorGrids in a form behave as though they are all in a + * "size group" together; they will all use the same column widths, + * which will be wide enough for the widest labels/widgets in any of + * the grids. #NmtEditorGrid is also specially aware of #NmtNewtSection, + * and grids inside sections will automatically take the size of the + * section border into account as well. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-editor-grid.h" + +G_DEFINE_TYPE(NmtEditorGrid, nmt_editor_grid, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_EDITOR_GRID_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR_GRID, NmtEditorGridPrivate)) + +typedef struct { + GArray *rows; + int * row_heights; + int indent; +} NmtEditorGridPrivate; + +typedef struct { + NmtNewtWidget * label; + NmtNewtWidget * widget; + NmtNewtWidget * extra; + NmtEditorGridRowFlags flags; +} NmtEditorGridRow; + +typedef struct { + int col_widths[3]; +} NmtEditorGridFormState; + +/** + * nmt_editor_grid_new: + * + * Creates a new #NmtEditorGrid + * + * Returns: a new #NmtEditorGrid + */ +NmtNewtWidget * +nmt_editor_grid_new(void) +{ + return g_object_new(NMT_TYPE_EDITOR_GRID, NULL); +} + +static void +nmt_editor_grid_init(NmtEditorGrid *grid) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid); + + priv->rows = g_array_new(FALSE, TRUE, sizeof(NmtEditorGridRow)); +} + +static void +nmt_editor_grid_finalize(GObject *object) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(object); + + g_array_unref(priv->rows); + nm_clear_g_free(&priv->row_heights); + + G_OBJECT_CLASS(nmt_editor_grid_parent_class)->finalize(object); +} + +/** + * nmt_editor_grid_append: + * @grid: the #NmtEditorGrid + * @label: (allow-none): the label text for @widget, or %NULL + * @widget: (allow-none): the (main) widget + * @extra: (allow-none): optional extra widget + * + * Adds a row to @grid. + * + * If @label and @widget are both non-%NULL, this will add a three-column row, + * containing a right-aligned #NmtNewtLabel in the first column, @widget in the + * second column, and @extra (if non-%NULL) in the third column. + * + * If either @label or @widget is %NULL, then the other column will expand into + * it. + * + * See also nmt_editor_grid_set_row_flags(). + */ +void +nmt_editor_grid_append(NmtEditorGrid *grid, + const char * label, + NmtNewtWidget *widget, + NmtNewtWidget *extra) +{ + NmtEditorGridPrivate * priv = NMT_EDITOR_GRID_GET_PRIVATE(grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_editor_grid_parent_class); + NmtNewtContainer * container = NMT_NEWT_CONTAINER(grid); + NmtEditorGridRow row; + + g_return_if_fail(label != NULL || widget != NULL); + + memset(&row, 0, sizeof(row)); + + if (label && !widget) { + widget = nmt_newt_label_new(label); + label = NULL; + } + + if (label) { + row.label = nmt_newt_label_new(label); + parent_class->add(container, row.label); + } + + row.widget = widget; + parent_class->add(container, widget); + if (row.label) { + g_object_bind_property(row.widget, + "valid", + row.label, + "highlight", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } + + if (extra) { + row.extra = extra; + parent_class->add(container, extra); + } + + g_array_append_val(priv->rows, row); +} + +static int +nmt_editor_grid_find_widget(NmtEditorGrid *grid, NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + for (i = 0; i < priv->rows->len; i++) { + if (rows[i].label == widget || rows[i].widget == widget || rows[i].extra == widget) + return i; + } + + return -1; +} + +/** + * NmtEditorGridRowFlags: + * @NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be + * aligned left instead of right. + * @NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget + * should be aligned right instead of left. + * + * Flags to alter an #NmtEditorGrid row's layout. + */ + +/** + * nmt_editor_grid_set_row_flags: + * @grid: an #NmtEditorGrid + * @widget: the widget whose row you want to adjust + * @flags: the flags to set + * + * Sets flags to adjust the layout of @widget's row in @grid. + */ +void +nmt_editor_grid_set_row_flags(NmtEditorGrid * grid, + NmtNewtWidget * widget, + NmtEditorGridRowFlags flags) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + i = nmt_editor_grid_find_widget(grid, widget); + if (i != -1) + rows[i].flags = flags; +} + +static void +nmt_editor_grid_remove(NmtNewtContainer *container, NmtNewtWidget *widget) +{ + NmtEditorGrid * grid = NMT_EDITOR_GRID(container); + NmtEditorGridPrivate * priv = NMT_EDITOR_GRID_GET_PRIVATE(grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_editor_grid_parent_class); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + int i; + + i = nmt_editor_grid_find_widget(grid, widget); + if (i != -1) { + if (rows[i].label) + parent_class->remove(container, rows[i].label); + parent_class->remove(container, rows[i].widget); + if (rows[i].extra) + parent_class->remove(container, rows[i].extra); + + g_array_remove_index(priv->rows, i); + return; + } + + // FIXME: shouldn't happen + parent_class->remove(container, widget); +} + +static newtComponent * +nmt_editor_grid_get_components(NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(widget); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + newtComponent * child_cos; + GPtrArray * cos; + int i, c; + + cos = g_ptr_array_new(); + + for (i = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible(rows[i].widget)) + continue; + + if (rows[i].label) { + child_cos = nmt_newt_widget_get_components(rows[i].label); + g_assert(child_cos[0] && !child_cos[1]); + g_ptr_array_add(cos, child_cos[0]); + g_free(child_cos); + } + + child_cos = nmt_newt_widget_get_components(rows[i].widget); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add(cos, child_cos[c]); + g_free(child_cos); + + if (rows[i].extra) { + child_cos = nmt_newt_widget_get_components(rows[i].extra); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add(cos, child_cos[c]); + g_free(child_cos); + } + } + + g_ptr_array_add(cos, NULL); + return (newtComponent *) g_ptr_array_free(cos, FALSE); +} + +static NmtEditorGridFormState * +get_form_state(NmtNewtWidget *widget) +{ + NmtNewtForm * form = nmt_newt_widget_get_form(widget); + NmtEditorGridFormState *state; + + if (!form) + return NULL; + + state = g_object_get_data(G_OBJECT(form), "NmtEditorGridFormState"); + if (state) + return state; + + state = g_new0(NmtEditorGridFormState, 1); + g_object_set_data_full(G_OBJECT(form), "NmtEditorGridFormState", state, g_free); + return state; +} + +static void +nmt_editor_grid_realize(NmtNewtWidget *widget) +{ + NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(widget); + NmtNewtWidget * parent; + + NMT_NEWT_WIDGET_CLASS(nmt_editor_grid_parent_class)->realize(widget); + + /* This is a hack, but it's the simplest way to make it work... */ + priv->indent = 0; + + parent = nmt_newt_widget_get_parent(widget); + while (parent) { + if (NMT_IS_NEWT_SECTION(parent)) { + priv->indent = 2; + break; + } + parent = nmt_newt_widget_get_parent(parent); + } +} + +static void +nmt_editor_grid_unrealize(NmtNewtWidget *widget) +{ + NmtEditorGridFormState *state = get_form_state(widget); + + if (state) + memset(state->col_widths, 0, sizeof(state->col_widths)); + + NMT_NEWT_WIDGET_CLASS(nmt_editor_grid_parent_class)->unrealize(widget); +} + +static void +nmt_editor_grid_size_request(NmtNewtWidget *widget, int *width, int *height) +{ + NmtEditorGridPrivate * priv = NMT_EDITOR_GRID_GET_PRIVATE(widget); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + NmtEditorGridFormState *state = get_form_state(widget); + gboolean add_padding = FALSE; + int i; + + g_free(priv->row_heights); + priv->row_heights = g_new0(int, priv->rows->len); + + *height = 0; + for (i = 0; i < priv->rows->len; i++) { + int lwidth, lheight, wwidth, wheight, ewidth, eheight; + + if (!nmt_newt_widget_get_visible(rows[i].widget)) + continue; + + if (rows[i].label) { + nmt_newt_widget_size_request(rows[i].label, &lwidth, &lheight); + lwidth += priv->indent; + state->col_widths[0] = MAX(state->col_widths[0], lwidth); + + nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight); + state->col_widths[1] = MAX(state->col_widths[1], wwidth); + priv->row_heights[i] = wheight; + + add_padding = TRUE; + } else { + nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight); + priv->row_heights[i] = wheight; + } + + if (rows[i].extra) { + nmt_newt_widget_size_request(rows[i].extra, &ewidth, &eheight); + state->col_widths[2] = MAX(state->col_widths[2], ewidth); + priv->row_heights[i] = MAX(priv->row_heights[i], eheight); + } + + *height += priv->row_heights[i]; + } + + *width = state->col_widths[0] + state->col_widths[1] + state->col_widths[2]; + if (add_padding) + *width += 2; +} + +static void +nmt_editor_grid_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height) +{ + NmtEditorGridPrivate * priv = NMT_EDITOR_GRID_GET_PRIVATE(widget); + NmtEditorGridRow * rows = (NmtEditorGridRow *) priv->rows->data; + NmtEditorGridFormState *state = get_form_state(widget); + int col0_width, col1_width, col2_width; + int i, row; + + col0_width = state->col_widths[0] - priv->indent; + col1_width = state->col_widths[1]; + col2_width = state->col_widths[2]; + + for (i = row = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible(rows[i].widget)) + continue; + + if (rows[i].label) { + int lwidth, lheight, lx; + + if (rows[i].flags & NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT) + lx = x; + else { + nmt_newt_widget_size_request(rows[i].label, &lwidth, &lheight); + lx = x + col0_width - lwidth; + } + + nmt_newt_widget_size_allocate(rows[i].label, + lx, + y + row, + col0_width, + priv->row_heights[i]); + + nmt_newt_widget_size_allocate(rows[i].widget, + x + col0_width + 1, + y + row, + col1_width, + priv->row_heights[i]); + } else { + nmt_newt_widget_size_allocate(rows[i].widget, + x, + y + row, + col0_width + col1_width + 1, + priv->row_heights[i]); + } + + if (rows[i].extra) { + int wwidth, wheight, ex; + + if (rows[i].flags & NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT) + ex = x + col0_width + col1_width + 2; + else { + nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight); + ex = x + col0_width + wwidth + 2; + } + + nmt_newt_widget_size_allocate(rows[i].extra, + ex, + y + row, + col2_width, + priv->row_heights[i]); + } + + row += priv->row_heights[i]; + } +} + +static void +nmt_editor_grid_class_init(NmtEditorGridClass *grid_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(grid_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(grid_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS(grid_class); + + g_type_class_add_private(grid_class, sizeof(NmtEditorGridPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_editor_grid_finalize; + + widget_class->realize = nmt_editor_grid_realize; + widget_class->unrealize = nmt_editor_grid_unrealize; + widget_class->get_components = nmt_editor_grid_get_components; + widget_class->size_request = nmt_editor_grid_size_request; + widget_class->size_allocate = nmt_editor_grid_size_allocate; + + container_class->remove = nmt_editor_grid_remove; +} diff --git a/src/nmtui/nmt-editor-grid.h b/src/nmtui/nmt-editor-grid.h new file mode 100644 index 00000000..39a86067 --- /dev/null +++ b/src/nmtui/nmt-editor-grid.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_GRID_H +#define NMT_EDITOR_GRID_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_EDITOR_GRID (nmt_editor_grid_get_type()) +#define NMT_EDITOR_GRID(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGrid)) +#define NMT_EDITOR_GRID_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass)) +#define NMT_IS_EDITOR_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDITOR_GRID)) +#define NMT_IS_EDITOR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDITOR_GRID)) +#define NMT_EDITOR_GRID_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass)) + +typedef struct { + NmtNewtContainer parent; + +} NmtEditorGrid; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtEditorGridClass; + +GType nmt_editor_grid_get_type(void); + +typedef enum { + NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT = (1 << 0), + NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT = (1 << 1) +} NmtEditorGridRowFlags; + +NmtNewtWidget *nmt_editor_grid_new(void); + +void nmt_editor_grid_append(NmtEditorGrid *grid, + const char * label, + NmtNewtWidget *widget, + NmtNewtWidget *extra); +void nmt_editor_grid_set_row_flags(NmtEditorGrid * grid, + NmtNewtWidget * widget, + NmtEditorGridRowFlags flags); + +#endif /* NMT_EDITOR_GRID_H */ diff --git a/src/nmtui/nmt-editor-page-device.c b/src/nmtui/nmt-editor-page-device.c new file mode 100644 index 00000000..950f4db1 --- /dev/null +++ b/src/nmtui/nmt-editor-page-device.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-page-device + * @short_description: Abstract base class for "device" editor pages + * + * #NmtEditorPageDevice is the base class for #NmtEditorPage subclasses + * representing device-type-specific data. (Eg, #NmtPageEthernet, + * #NmtPageVlan, etc). + * + * FIXME: rename to NmtEditorPageDevice, so it doesn't sound like it's + * an actual page type. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-editor-page-device.h" + +G_DEFINE_TYPE(NmtEditorPageDevice, nmt_editor_page_device, NMT_TYPE_EDITOR_PAGE) + +#define NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevicePrivate)) + +typedef struct { + NmtDeviceEntry *device_entry; + gboolean show_by_default; +} NmtEditorPageDevicePrivate; + +enum { + PROP_0, + + PROP_DEVICE_ENTRY, + + LAST_PROP +}; + +static void +nmt_editor_page_device_init(NmtEditorPageDevice *device) +{} + +static void +nmt_editor_page_device_finalize(GObject *object) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(object); + + g_clear_object(&priv->device_entry); + + G_OBJECT_CLASS(nmt_editor_page_device_parent_class)->finalize(object); +} + +NmtDeviceEntry * +nmt_editor_page_device_get_device_entry(NmtEditorPageDevice *page) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(page); + + return priv->device_entry; +} + +static void +nmt_editor_page_device_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + priv->device_entry = g_value_dup_object(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_device_get_property(GObject * object, + guint prop_id, + GValue * value, + GParamSpec *pspec) +{ + NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + g_value_set_object(value, priv->device_entry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_device_class_init(NmtEditorPageDeviceClass *page_device_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(page_device_class); + + g_type_class_add_private(page_device_class, sizeof(NmtEditorPageDevicePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_page_device_set_property; + object_class->get_property = nmt_editor_page_device_get_property; + object_class->finalize = nmt_editor_page_device_finalize; + + /* properties */ + g_object_class_install_property( + object_class, + PROP_DEVICE_ENTRY, + g_param_spec_object("device-entry", + "", + "", + NMT_TYPE_DEVICE_ENTRY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-editor-page-device.h b/src/nmtui/nmt-editor-page-device.h new file mode 100644 index 00000000..c8dbfad9 --- /dev/null +++ b/src/nmtui/nmt-editor-page-device.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_PAGE_DEVICE_H +#define NMT_EDITOR_PAGE_DEVICE_H + +#include "nmt-editor-page.h" +#include "nmt-device-entry.h" + +#define NMT_TYPE_EDITOR_PAGE_DEVICE (nmt_editor_page_device_get_type()) +#define NMT_EDITOR_PAGE_DEVICE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevice)) +#define NMT_EDITOR_PAGE_DEVICE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass)) +#define NMT_IS_EDITOR_PAGE_DEVICE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDITOR_PAGE_DEVICE)) +#define NMT_IS_EDITOR_PAGE_DEVICE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDITOR_PAGE_DEVICE)) +#define NMT_EDITOR_PAGE_DEVICE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtEditorPageDevice; + +typedef struct { + NmtEditorPageClass parent; + +} NmtEditorPageDeviceClass; + +GType nmt_editor_page_device_get_type(void); + +NmtDeviceEntry *nmt_editor_page_device_get_device_entry(NmtEditorPageDevice *page); + +#endif /* NMT_EDITOR_PAGE_DEVICE_H */ diff --git a/src/nmtui/nmt-editor-page.c b/src/nmtui/nmt-editor-page.c new file mode 100644 index 00000000..6d90d71e --- /dev/null +++ b/src/nmtui/nmt-editor-page.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-page: + * @short_description: An #NmtEditor "page" + * + * #NmtEditorPage is the abstract base class for #NmtEditor "pages". + * A "page" is a set of related #NmtEditorSections. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-editor-page.h" + +G_DEFINE_ABSTRACT_TYPE(NmtEditorPage, nmt_editor_page, G_TYPE_OBJECT) + +#define NMT_EDITOR_PAGE_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR_PAGE, NmtEditorPagePrivate)) + +typedef struct { + NMConnection *connection; + GSList * sections; + +} NmtEditorPagePrivate; + +enum { + PROP_0, + + PROP_CONNECTION, + + LAST_PROP +}; + +static void +nmt_editor_page_init(NmtEditorPage *page) +{} + +static void +nmt_editor_page_finalize(GObject *object) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(object); + + g_clear_object(&priv->connection); + g_slist_free_full(priv->sections, g_object_unref); + + G_OBJECT_CLASS(nmt_editor_page_parent_class)->finalize(object); +} + +/** + * nmt_editor_page_get_connection: + * @page: the #NmtEditorPage + * + * Gets the page's #NMConnection. + * + * Returns: (transfer none): the page's #NMConnection. + */ +NMConnection * +nmt_editor_page_get_connection(NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(page); + + return priv->connection; +} + +/** + * nmt_editor_page_get_sections: + * @page: the #NmtEditorPage + * + * Gets the page's list of sections to display. + * + * Returns: (transfer none): the list of sections; this is the internal list + * used by the page and must not be modified or freed. + */ +GSList * +nmt_editor_page_get_sections(NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(page); + + return priv->sections; +} + +/** + * nmt_editor_page_add_section: + * @page: the #NmtEditorPage + * @section: the #NmtEditorSection + * + * Adds a section to the page. This should only be called by #NmtEditorPage + * subclasses. + */ +void +nmt_editor_page_add_section(NmtEditorPage *page, NmtEditorSection *section) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(page); + + priv->sections = g_slist_append(priv->sections, g_object_ref_sink(section)); +} + +/** + * nmt_editor_page_saved: + * @page: the #NmtEditorPage + * + * This method is called when the user saves the connection. It gives + * the page a chance to do save its data outside the connections (such as + * recommit the slave connections). + */ +void +nmt_editor_page_saved(NmtEditorPage *page) +{ + NmtEditorPageClass *editor_page_class = NMT_EDITOR_PAGE_GET_CLASS(page); + + if (editor_page_class->saved) + editor_page_class->saved(page); +} + +static void +nmt_editor_page_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->connection = g_value_dup_object(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object(value, priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_class_init(NmtEditorPageClass *page_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(page_class); + + g_type_class_add_private(page_class, sizeof(NmtEditorPagePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_page_set_property; + object_class->get_property = nmt_editor_page_get_property; + object_class->finalize = nmt_editor_page_finalize; + + /* properties */ + + /** + * NmtEditorPage:connection: + * + * The page's #NMConnection. + */ + g_object_class_install_property( + object_class, + PROP_CONNECTION, + g_param_spec_object("connection", + "", + "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-editor-page.h b/src/nmtui/nmt-editor-page.h new file mode 100644 index 00000000..74b9e09f --- /dev/null +++ b/src/nmtui/nmt-editor-page.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 - 2014 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_PAGE_H +#define NMT_EDITOR_PAGE_H + +#include "nmt-editor-grid.h" +#include "nmt-editor-section.h" + +#define NMT_TYPE_EDITOR_PAGE (nmt_editor_page_get_type()) +#define NMT_EDITOR_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPage)) +#define NMT_EDITOR_PAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) +#define NMT_IS_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDITOR_PAGE)) +#define NMT_IS_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDITOR_PAGE)) +#define NMT_EDITOR_PAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) + +typedef struct { + GObject parent; + +} NmtEditorPage; + +typedef struct { + GObjectClass parent; + + void (*saved)(NmtEditorPage *page); +} NmtEditorPageClass; + +GType nmt_editor_page_get_type(void); + +NMConnection *nmt_editor_page_get_connection(NmtEditorPage *page); + +GSList *nmt_editor_page_get_sections(NmtEditorPage *page); + +void nmt_editor_page_saved(NmtEditorPage *page); + +/*< protected >*/ +void nmt_editor_page_add_section(NmtEditorPage *page, NmtEditorSection *section); + +#endif /* NMT_EDITOR_PAGE_H */ diff --git a/src/nmtui/nmt-editor-section.c b/src/nmtui/nmt-editor-section.c new file mode 100644 index 00000000..3fbfe7a3 --- /dev/null +++ b/src/nmtui/nmt-editor-section.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-section: + * @short_description: A section of the #NmtEditor + * + * #NmtEditorSection is the abstract base class for #NmtEditor sections. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-editor-section.h" +#include "libnmt-newt/nmt-newt-toggle-button.h" + +G_DEFINE_TYPE(NmtEditorSection, nmt_editor_section, NMT_TYPE_NEWT_SECTION) + +#define NMT_EDITOR_SECTION_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionPrivate)) + +typedef struct { + NmtEditorGrid *header, *body; + char * title; + NmtNewtWidget *header_widget; + NmtNewtWidget *toggle; + + gboolean show_by_default; +} NmtEditorSectionPrivate; + +enum { + PROP_0, + + PROP_TITLE, + PROP_SHOW_BY_DEFAULT, + PROP_HEADER_WIDGET, + + LAST_PROP +}; + +/** + * nmt_editor_section_new: + * @title: the section title + * @header_widget: (allow-none): the widget to show next to the title + * @show_by_default: whether the section should be open by default + * + * Creates a new #NmtEditorSection. + * + * Returns: a new #NmtEditorSection + */ +NmtEditorSection * +nmt_editor_section_new(const char *title, NmtNewtWidget *header_widget, gboolean show_by_default) +{ + return g_object_new(NMT_TYPE_EDITOR_SECTION, + "title", + title, + "header-widget", + header_widget, + "show-by-default", + show_by_default, + NULL); +} + +static void +rebuild_header(NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + /* Removing any widget in an NmtEditorGrid removes its whole row, so we can + * remove the existing title/widget/toggle by asking to remove toggle. + */ + nmt_newt_container_remove(NMT_NEWT_CONTAINER(priv->header), priv->toggle); + + nmt_editor_grid_append(priv->header, priv->title, priv->header_widget, priv->toggle); + nmt_editor_grid_set_row_flags(priv->header, + priv->toggle, + NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT + | NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT); +} + +static void +nmt_editor_section_init(NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + priv->header = NMT_EDITOR_GRID(nmt_editor_grid_new()); + priv->body = NMT_EDITOR_GRID(nmt_editor_grid_new()); + priv->toggle = nmt_newt_toggle_button_new(_("Hide"), _("Show")); + g_object_ref_sink(priv->toggle); + + nmt_newt_section_set_header(NMT_NEWT_SECTION(section), NMT_NEWT_WIDGET(priv->header)); + nmt_newt_section_set_body(NMT_NEWT_SECTION(section), NMT_NEWT_WIDGET(priv->body)); + + g_object_bind_property(priv->toggle, "active", section, "open", G_BINDING_SYNC_CREATE); +} + +static void +nmt_editor_section_finalize(GObject *object) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(object); + + g_free(priv->title); + g_clear_object(&priv->header_widget); + g_clear_object(&priv->toggle); + + G_OBJECT_CLASS(nmt_editor_section_parent_class)->finalize(object); +} + +/** + * nmt_editor_section_get_header_widget: + * @section: the #NmtEditorSection + * + * Gets the section's header widget. + * + * Returns: the section's header widget. + */ +NmtNewtWidget * +nmt_editor_section_get_header_widget(NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + return priv->header_widget; +} + +/** + * nmt_editor_section_get_body: + * @section: the #NmtEditorSection + * + * Gets the section's body grid, so that you can add things to it. + * + * Returns: the #NmtEditorGrid used for the section body + */ +NmtEditorGrid * +nmt_editor_section_get_body(NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + return priv->body; +} + +/** + * nmt_editor_section_get_title: + * @section: the #NmtEditorSection + * + * Gets the section's title. + * + * Returns: the section's title + */ +const char * +nmt_editor_section_get_title(NmtEditorSection *section) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + return priv->title; +} + +static void +nmt_editor_section_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtEditorSection * section = NMT_EDITOR_SECTION(object); + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(section); + + switch (prop_id) { + case PROP_TITLE: + priv->title = g_value_dup_string(value); + rebuild_header(section); + break; + case PROP_SHOW_BY_DEFAULT: + priv->show_by_default = g_value_get_boolean(value); + nmt_newt_toggle_button_set_active(NMT_NEWT_TOGGLE_BUTTON(priv->toggle), + priv->show_by_default); + break; + case PROP_HEADER_WIDGET: + priv->header_widget = g_value_get_object(value); + if (priv->header_widget) + g_object_ref_sink(priv->header_widget); + rebuild_header(section); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_section_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_TITLE: + g_value_set_string(value, priv->title); + break; + case PROP_SHOW_BY_DEFAULT: + g_value_set_boolean(value, priv->show_by_default); + break; + case PROP_HEADER_WIDGET: + g_value_set_object(value, priv->header_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_section_class_init(NmtEditorSectionClass *section_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(section_class); + + g_type_class_add_private(section_class, sizeof(NmtEditorSectionPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_section_set_property; + object_class->get_property = nmt_editor_section_get_property; + object_class->finalize = nmt_editor_section_finalize; + + /* properties */ + + /** + * NmtEditorSection:title: + * + * The section's title. + */ + g_object_class_install_property( + object_class, + PROP_TITLE, + g_param_spec_string("title", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditorSection:show-by-default: + * + * Whether the section should be expanded by default. + */ + g_object_class_install_property( + object_class, + PROP_SHOW_BY_DEFAULT, + g_param_spec_boolean("show-by-default", + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditorSection:header-widget: + * + * The widget (if any) that appears between the section title and its toggle + * button. + */ + g_object_class_install_property( + object_class, + PROP_HEADER_WIDGET, + g_param_spec_object("header-widget", + "", + "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-editor-section.h b/src/nmtui/nmt-editor-section.h new file mode 100644 index 00000000..b7baf821 --- /dev/null +++ b/src/nmtui/nmt-editor-section.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_SECTION_H +#define NMT_EDITOR_SECTION_H + +#include "libnmt-newt/nmt-newt-section.h" +#include "nmt-editor-grid.h" + +#define NMT_TYPE_EDITOR_SECTION (nmt_editor_section_get_type()) +#define NMT_EDITOR_SECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSection)) +#define NMT_EDITOR_SECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass)) +#define NMT_IS_EDITOR_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDITOR_SECTION)) +#define NMT_IS_EDITOR_SECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDITOR_SECTION)) +#define NMT_EDITOR_SECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass)) + +typedef struct { + NmtNewtSection parent; + +} NmtEditorSection; + +typedef struct { + NmtNewtSectionClass parent; + +} NmtEditorSectionClass; + +GType nmt_editor_section_get_type(void); + +NmtEditorSection * +nmt_editor_section_new(const char *title, NmtNewtWidget *header_widget, gboolean show_by_default); + +const char * nmt_editor_section_get_title(NmtEditorSection *section); +NmtNewtWidget *nmt_editor_section_get_header_widget(NmtEditorSection *section); +NmtEditorGrid *nmt_editor_section_get_body(NmtEditorSection *section); + +#endif /* NMT_EDITOR_SECTION_H */ diff --git a/src/nmtui/nmt-editor.c b/src/nmtui/nmt-editor.c new file mode 100644 index 00000000..0ef23693 --- /dev/null +++ b/src/nmtui/nmt-editor.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor + * @short_description: Connection editing form + * + * #NmtEditor is the top-level form for editing a connection. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-editor.h" + +#include "nm-utils.h" + +#include "nmtui.h" + +#include "nm-editor-utils.h" +#include "nmt-utils.h" + +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +#include "nmt-page-bond.h" +#include "nmt-page-bridge.h" +#include "nmt-page-bridge-port.h" +#include "nmt-page-dsl.h" +#include "nmt-page-ethernet.h" +#include "nmt-page-infiniband.h" +#include "nmt-page-ip-tunnel.h" +#include "nmt-page-ip4.h" +#include "nmt-page-ip6.h" +#include "nmt-page-ppp.h" +#include "nmt-page-team.h" +#include "nmt-page-team-port.h" +#include "nmt-page-vlan.h" +#include "nmt-page-wifi.h" + +#include "libnmc-setting/nm-meta-setting-access.h" + +G_DEFINE_TYPE(NmtEditor, nmt_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_EDITOR_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR, NmtEditorPrivate)) + +typedef struct { + NMConnection *orig_connection; + NMConnection *edit_connection; + + NMEditorConnectionTypeData *type_data; + + GSList * pages; + NmtNewtWidget *ok, *cancel; + gboolean running; +} NmtEditorPrivate; + +enum { + PROP_0, + PROP_CONNECTION, + PROP_TYPE_DATA, + + LAST_PROP +}; + +/** + * nmt_editor_new: + * @connection: the #NMConnection to edit + * + * Creates a new #NmtEditor to edit @connection. + * + * Returns: a new #NmtEditor + */ +NmtNewtForm * +nmt_editor_new(NMConnection *connection) +{ + NMEditorConnectionTypeData *type_data; + + type_data = nm_editor_utils_get_connection_type_data(connection); + if (!type_data) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection(connection); + if (s_con) { + nmt_newt_message_dialog(_("Could not create editor for connection '%s' of type '%s'."), + nm_connection_get_id(connection), + nm_setting_connection_get_connection_type(s_con)); + } else { + nmt_newt_message_dialog(_("Could not create editor for invalid connection '%s'."), + nm_connection_get_id(connection)); + } + + return NULL; + } + + return g_object_new(NMT_TYPE_EDITOR, + "connection", + connection, + "type-data", + type_data, + "title", + _("Edit Connection"), + "fullscreen-vertical", + TRUE, + NULL); +} + +static void +nmt_editor_init(NmtEditor *entry) +{} + +static void +connection_updated(GObject *connection, GAsyncResult *result, gpointer op) +{ + GError *error = NULL; + + nm_remote_connection_commit_changes_finish(NM_REMOTE_CONNECTION(connection), result, &error); + nmt_sync_op_complete_boolean(op, error == NULL, error); + g_clear_error(&error); +} + +static void +connection_added(GObject *client, GAsyncResult *result, gpointer op) +{ + NMRemoteConnection *connection; + GError * error = NULL; + + connection = nm_client_add_connection_finish(NM_CLIENT(client), result, &error); + nmt_sync_op_complete_boolean(op, error == NULL, error); + g_clear_object(&connection); + g_clear_error(&error); +} + +static void +page_saved(gpointer data, gpointer user_data) +{ + NmtEditorPage *page = data; + + nmt_editor_page_saved(page); +} + +static void +save_connection_and_exit(NmtNewtButton *button, gpointer user_data) +{ + NmtEditor * editor = user_data; + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(editor); + NmtSyncOp op; + GError * error = NULL; + + nm_connection_replace_settings_from_connection(priv->orig_connection, priv->edit_connection); + + nmt_sync_op_init(&op); + if (NM_IS_REMOTE_CONNECTION(priv->orig_connection)) { + nm_remote_connection_commit_changes_async(NM_REMOTE_CONNECTION(priv->orig_connection), + TRUE, + NULL, + connection_updated, + &op); + if (!nmt_sync_op_wait_boolean(&op, &error)) { + nmt_newt_message_dialog(_("Unable to save connection: %s"), error->message); + g_error_free(error); + return; + } + + /* Clear secrets so they don't lay around in memory; they'll get + * requested again anyway next time the connection is edited. + */ + nm_connection_clear_secrets(priv->orig_connection); + } else { + nm_client_add_connection_async(nm_client, + priv->orig_connection, + TRUE, + NULL, + connection_added, + &op); + if (!nmt_sync_op_wait_boolean(&op, &error)) { + nmt_newt_message_dialog(_("Unable to add new connection: %s"), error->message); + g_error_free(error); + return; + } + } + + /* Let the page know that it was saved. */ + g_slist_foreach(priv->pages, page_saved, NULL); + + nmt_newt_form_quit(NMT_NEWT_FORM(editor)); +} + +static void +got_secrets(GObject *object, GAsyncResult *result, gpointer op) +{ + GVariant *secrets; + GError * error = NULL; + + secrets = nm_remote_connection_get_secrets_finish(NM_REMOTE_CONNECTION(object), result, &error); + if (secrets) + g_variant_ref(secrets); + nmt_sync_op_complete_pointer(op, secrets, error); + g_clear_error(&error); +} + +static NMConnection * +build_edit_connection(NMConnection *orig_connection) +{ + NMConnection *edit_connection; + GVariant * settings, *secrets; + GVariantIter iter; + const char * setting_name; + NmtSyncOp op; + + edit_connection = nm_simple_connection_new_clone(orig_connection); + + if (!NM_IS_REMOTE_CONNECTION(orig_connection)) + return edit_connection; + + settings = nm_connection_to_dbus(orig_connection, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET); + g_variant_iter_init(&iter, settings); + while (g_variant_iter_next(&iter, "{&s@a{sv}}", &setting_name, NULL)) { + if (!nm_meta_setting_info_editor_has_secrets( + nm_meta_setting_info_editor_find_by_name(setting_name, FALSE))) + continue; + nmt_sync_op_init(&op); + nm_remote_connection_get_secrets_async(NM_REMOTE_CONNECTION(orig_connection), + setting_name, + NULL, + got_secrets, + &op); + /* FIXME: error handling */ + secrets = nmt_sync_op_wait_pointer(&op, NULL); + if (secrets) { + (void) nm_connection_update_secrets(edit_connection, setting_name, secrets, NULL); + g_variant_unref(secrets); + } + } + g_variant_unref(settings); + + return edit_connection; +} + +static gboolean +permissions_transform_to_allusers(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + char **perms = g_value_get_boxed(source_value); + + g_value_set_boolean(target_value, g_strv_length(perms) == 0); + return TRUE; +} + +static gboolean +permissions_transform_from_allusers(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + gboolean allusers = g_value_get_boolean(source_value); + char ** perms = NULL; + + if (!allusers) { + perms = g_new(char *, 2); + + perms[0] = g_strdup_printf("user:%s:", g_get_user_name()); + perms[1] = NULL; + } + g_value_take_boxed(target_value, perms); + return TRUE; +} + +static NmtNewtWidget * +add_sections_for_page(NmtEditor *editor, NmtEditorGrid *grid, NmtEditorPage *page) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(editor); + NmtNewtWidget * first_section = NULL; + const GSList * sections, *iter; + + g_return_val_if_fail(NMT_IS_EDITOR_PAGE(page), NULL); + + priv->pages = g_slist_prepend(priv->pages, page); + + sections = nmt_editor_page_get_sections(page); + for (iter = sections; iter; iter = iter->next) { + if (!first_section) + first_section = iter->data; + nmt_editor_grid_append(grid, NULL, iter->data, NULL); + } + + return first_section; +} + +static void +nmt_editor_constructed(GObject *object) +{ + NmtEditor * editor = NMT_EDITOR(object); + NmtEditorPrivate * priv = NMT_EDITOR_GET_PRIVATE(editor); + NMSettingConnection *s_con; + NmtNewtWidget * vbox, *widget, *buttons; + NmtEditorGrid * grid; + const char * deventry_label; + NmtDeviceEntry * deventry; + GType hardware_type; + const char * slave_type; + NmtEditorPage * page; + + if (G_OBJECT_CLASS(nmt_editor_parent_class)->constructed) + G_OBJECT_CLASS(nmt_editor_parent_class)->constructed(object); + + priv->edit_connection = build_edit_connection(priv->orig_connection); + + vbox = nmt_newt_grid_new(); + + s_con = nm_connection_get_setting_connection(priv->edit_connection); + + grid = NMT_EDITOR_GRID(nmt_editor_grid_new()); + nmt_newt_grid_add(NMT_NEWT_GRID(vbox), NMT_NEWT_WIDGET(grid), 0, 0); + + /* Add the top widgets */ + + widget = nmt_newt_entry_new(40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property(s_con, + NM_SETTING_CONNECTION_ID, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Profile name"), widget, NULL); + + if (priv->type_data->virtual) + hardware_type = G_TYPE_NONE; + else + hardware_type = priv->type_data->device_type; + + /* For connections involving multiple network devices, clarify which one + * NMSettingConnection:interface-name refers to. + */ + if (nm_connection_is_type(priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME)) + deventry_label = _("Ethernet device"); + else + deventry_label = _("Device"); + + widget = nmt_device_entry_new(deventry_label, 40, hardware_type); + nmt_editor_grid_append(grid, NULL, widget, NULL); + deventry = NMT_DEVICE_ENTRY(widget); + g_object_bind_property(s_con, + NM_SETTING_CONNECTION_INTERFACE_NAME, + deventry, + "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + /* Now add the various pages... */ + + if (nm_connection_is_type(priv->edit_connection, NM_SETTING_BOND_SETTING_NAME)) + page = nmt_page_bond_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_BRIDGE_SETTING_NAME)) + page = nmt_page_bridge_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_INFINIBAND_SETTING_NAME)) + page = nmt_page_infiniband_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME)) + page = nmt_page_dsl_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_TEAM_SETTING_NAME)) + page = nmt_page_team_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_VLAN_SETTING_NAME)) + page = nmt_page_vlan_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_WIRED_SETTING_NAME)) + page = nmt_page_ethernet_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_WIRELESS_SETTING_NAME)) + page = nmt_page_wifi_new(priv->edit_connection, deventry); + else if (nm_connection_is_type(priv->edit_connection, NM_SETTING_IP_TUNNEL_SETTING_NAME)) + page = nmt_page_ip_tunnel_new(priv->edit_connection, deventry); + else + g_assert_not_reached(); + + add_sections_for_page(editor, grid, page); + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + slave_type = nm_setting_connection_get_slave_type(s_con); + if (slave_type) { + if (!strcmp(slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) + add_sections_for_page(editor, grid, nmt_page_bridge_port_new(priv->edit_connection)); + else if (!strcmp(slave_type, NM_SETTING_TEAM_SETTING_NAME)) + add_sections_for_page(editor, grid, nmt_page_team_port_new(priv->edit_connection)); + } else { + NmtNewtWidget *section; + + section = add_sections_for_page(editor, grid, nmt_page_ip4_new(priv->edit_connection)); + + /* Add a separator between ip4 and ip6 that's only visible if ip4 is open */ + widget = nmt_newt_separator_new(); + g_object_bind_property(section, "open", widget, "visible", G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + add_sections_for_page(editor, grid, nmt_page_ip6_new(priv->edit_connection)); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + } + + /* And finally the bottom widgets */ + + widget = nmt_newt_checkbox_new(_("Automatically connect")); + g_object_bind_property(s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Available to all users")); + g_object_bind_property_full(s_con, + NM_SETTING_CONNECTION_PERMISSIONS, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + permissions_transform_to_allusers, + permissions_transform_from_allusers, + NULL, + NULL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + /* And the button box */ + + buttons = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add(NMT_NEWT_GRID(vbox), buttons, 0, 1); + nmt_newt_widget_set_padding(buttons, 0, 1, 0, 0); + + priv->cancel = nmt_newt_button_box_add_end(NMT_NEWT_BUTTON_BOX(buttons), _("Cancel")); + nmt_newt_widget_set_exit_on_activate(priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end(NMT_NEWT_BUTTON_BOX(buttons), _("OK")); + g_signal_connect(priv->ok, "clicked", G_CALLBACK(save_connection_and_exit), editor); + g_object_bind_property(NMT_NEWT_WIDGET(grid), + "valid", + priv->ok, + "sensitive", + G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content(NMT_NEWT_FORM(editor), vbox); +} + +static void +nmt_editor_finalize(GObject *object) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(object); + + g_clear_object(&priv->orig_connection); + g_clear_object(&priv->edit_connection); + + g_slist_free_full(priv->pages, g_object_unref); + + g_clear_object(&priv->ok); + g_clear_object(&priv->cancel); + + G_OBJECT_CLASS(nmt_editor_parent_class)->finalize(object); +} + +static void +nmt_editor_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->orig_connection = g_value_dup_object(value); + break; + case PROP_TYPE_DATA: + priv->type_data = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object(value, priv->orig_connection); + break; + case PROP_TYPE_DATA: + g_value_set_pointer(value, priv->type_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_class_init(NmtEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_editor_constructed; + object_class->set_property = nmt_editor_set_property; + object_class->get_property = nmt_editor_get_property; + object_class->finalize = nmt_editor_finalize; + + /** + * NmtEditor:connection: + * + * The connection being edited. + */ + g_object_class_install_property( + object_class, + PROP_CONNECTION, + g_param_spec_object("connection", + "", + "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtEditor:type-data: + * + * The #NmEditorConnectionTypeData for #NmtEditor:connection. + */ + g_object_class_install_property( + object_class, + PROP_TYPE_DATA, + g_param_spec_pointer("type-data", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-editor.h b/src/nmtui/nmt-editor.h new file mode 100644 index 00000000..49fe89d3 --- /dev/null +++ b/src/nmtui/nmt-editor.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_H +#define NMT_EDITOR_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_EDITOR (nmt_editor_get_type()) +#define NMT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_EDITOR, NmtEditor)) +#define NMT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_EDITOR, NmtEditorClass)) +#define NMT_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_EDITOR)) +#define NMT_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_EDITOR)) +#define NMT_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_EDITOR, NmtEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtEditorClass; + +GType nmt_editor_get_type(void); + +NmtNewtForm *nmt_editor_new(NMConnection *connection); + +#endif /* NMT_EDITOR_H */ diff --git a/src/nmtui/nmt-ip-entry.c b/src/nmtui/nmt-ip-entry.c new file mode 100644 index 00000000..b801eec4 --- /dev/null +++ b/src/nmtui/nmt-ip-entry.c @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-ip-entry + * @short_description: #NmtNewtEntry for IP address entry + * + * #NmtIPEntry is an #NmtNewtEntry for entering IP addresses, or IP + * address/prefix combination. It will only allow typing characters + * that are valid in an IP address, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid IP address. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE(NmtIPEntry, nmt_ip_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_IP_ENTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_IP_ENTRY, NmtIPEntryPrivate)) + +typedef struct { + int family; + gboolean prefix; + gboolean optional; + +} NmtIPEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_PREFIX, + PROP_OPTIONAL, + + LAST_PROP +}; + +/** + * nmt_ip_entry_new: + * @width: the width of the entry + * @family: the IP address family. Eg, %AF_INET + * @prefix: whether to require a trailing "/prefix" + * @optional: whether the address is optional + * + * Creates a new #NmtIPEntry, to accept IP addresses in the indicated + * @family, or (if @prefix is %TRUE), to accept IP address/prefix combos. + * + * If @optional is %TRUE then the address is considered optional, and + * so will still be #NmtNewtWidget:valid even when it is empty. If + * @optional is %FALSE, the entry will be invalid when it is empty. + */ +NmtNewtWidget * +nmt_ip_entry_new(int width, int family, gboolean prefix, gboolean optional) +{ + return g_object_new(NMT_TYPE_IP_ENTRY, + "width", + width, + "family", + family, + "prefix", + prefix, + "optional", + optional, + NULL); +} + +static gboolean +ip_entry_filter(NmtNewtEntry *entry, const char *text, int ch, int position, gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE(entry); + const char * slash; + gboolean inaddr; + + if (g_ascii_isdigit(ch)) + return TRUE; + + slash = strchr(text, '/'); + if (ch == '/') + return priv->prefix && slash == NULL; + + inaddr = !slash || (position <= (slash - text)); + + if (priv->family == AF_INET) { + if (ch == '.') + return inaddr; + else + return FALSE; + } else if (priv->family == AF_INET6) { + if (g_ascii_isxdigit(ch) || ch == ':') + return inaddr; + else + return FALSE; + } else + g_return_val_if_reached(FALSE); +} + +static gboolean +ip_entry_validate(NmtNewtEntry *entry, const char *text, gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE(entry); + + if (!*text) + return priv->optional; + if (priv->prefix) + return nm_utils_parse_inaddr_prefix(priv->family, text, NULL, NULL); + return nm_utils_parse_inaddr(priv->family, text, NULL); +} + +static void +nmt_ip_entry_init(NmtIPEntry *entry) +{ + nmt_newt_entry_set_filter(NMT_NEWT_ENTRY(entry), ip_entry_filter, NULL); + nmt_newt_entry_set_validator(NMT_NEWT_ENTRY(entry), ip_entry_validate, NULL); +} + +static void +nmt_ip_entry_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int(value); + break; + case PROP_PREFIX: + priv->prefix = g_value_get_boolean(value); + break; + case PROP_OPTIONAL: + priv->optional = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, priv->family); + break; + case PROP_PREFIX: + g_value_set_boolean(value, priv->prefix); + break; + case PROP_OPTIONAL: + g_value_set_boolean(value, priv->optional); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_class_init(NmtIPEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtIPEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_ip_entry_set_property; + object_class->get_property = nmt_ip_entry_get_property; + + /** + * NmtIPEntry:family: + * + * The address family. Eg, %AF_INET + */ + g_object_class_install_property( + object_class, + PROP_FAMILY, + g_param_spec_int("family", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:prefix: + * + * If %TRUE, the entry accepts address/prefix combinations. If + * %FALSE it accepts just addresses. + */ + g_object_class_install_property( + object_class, + PROP_PREFIX, + g_param_spec_boolean("prefix", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:optional: + * + * If %TRUE, the entry will be #NmtNewtWidget:valid when it is + * empty. If %FALSE, it will only be valid when it contains a + * valid address or address/prefix. + */ + g_object_class_install_property( + object_class, + PROP_OPTIONAL, + g_param_spec_boolean("optional", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-ip-entry.h b/src/nmtui/nmt-ip-entry.h new file mode 100644 index 00000000..1489326d --- /dev/null +++ b/src/nmtui/nmt-ip-entry.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_IP_ENTRY_H +#define NMT_IP_ENTRY_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_IP_ENTRY (nmt_ip_entry_get_type()) +#define NMT_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry)) +#define NMT_IP_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) +#define NMT_IS_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_IP_ENTRY)) +#define NMT_IS_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_IP_ENTRY)) +#define NMT_IP_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtIPEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtIPEntryClass; + +GType nmt_ip_entry_get_type(void); + +NmtNewtWidget *nmt_ip_entry_new(int width, int family, gboolean prefix, gboolean optional); + +#endif /* NMT_IP_ENTRY_H */ diff --git a/src/nmtui/nmt-mac-entry.c b/src/nmtui/nmt-mac-entry.c new file mode 100644 index 00000000..3f71f4c9 --- /dev/null +++ b/src/nmtui/nmt-mac-entry.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mac-entry + * @short_description: #NmtNewtEntry for hardware address entry + * + * #NmtMacEntry is an #NmtNewtEntry for entering hardware addresses. + * It will only allow typing characters that are valid in a hardware + * address, and will set its #NmtNewtWidget:valid property depending + * on whether it currently contains a valid hardware address. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-mac-entry.h" + +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> + +#include "libnm-core-aux-intern/nm-common-macros.h" + +G_DEFINE_TYPE(NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_MAC_ENTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_MAC_ENTRY, NmtMacEntryPrivate)) + +typedef struct { + int mac_length; + int mac_str_length; + NmtMacEntryType entry_type; + +} NmtMacEntryPrivate; + +enum { + PROP_0, + PROP_MAC_LENGTH, + PROP_MAC_ADDRESS, + PROP_ENTRY_TYPE, + + LAST_PROP +}; + +/** + * nmt_mac_entry_new: + * @width: the width in characters of the entry + * @mac_length: the length in bytes of the hardware address + * (either %ETH_ALEN or %INFINIBAND_ALEN) + * @entry_type: the type of the entry. + * + * Creates a new #NmtMacEntry. + * + * Returns: a new #NmtMacEntry. + */ +NmtNewtWidget * +nmt_mac_entry_new(int width, int mac_length, NmtMacEntryType entry_type) +{ + return g_object_new(NMT_TYPE_MAC_ENTRY, + "width", + width, + "mac-length", + mac_length, + "entry-type", + (int) entry_type, + NULL); +} + +static gboolean +mac_filter(NmtNewtEntry *entry, const char *text, int ch, int position, gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE(entry); + + if (priv->entry_type != NMT_MAC_ENTRY_TYPE_MAC) + return TRUE; + + if (position >= priv->mac_str_length) + return FALSE; + + return g_ascii_isxdigit(ch) || ch == ':'; +} + +static gboolean +mac_validator(NmtNewtEntry *entry, const char *text, gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE(entry); + const char * p; + + if (!*text) + return TRUE; + + if (priv->entry_type == NMT_MAC_ENTRY_TYPE_CLONED) { + if (NM_CLONED_MAC_IS_SPECIAL(text)) + return TRUE; + } + + p = text; + while (g_ascii_isxdigit(p[0]) && g_ascii_isxdigit(p[1]) && p[2] == ':') + p += 3; + + if (!g_ascii_isxdigit(p[0]) || !g_ascii_isxdigit(p[1])) + return FALSE; + p += 2; + + if (!*p) + return (p - text == priv->mac_str_length); + + if (g_ascii_isxdigit(p[0]) && !p[1] && p - text < priv->mac_str_length) { + char *fixed = g_strdup_printf("%.*s:%c", (int) (p - text), text, *p); + + nmt_newt_entry_set_text(entry, fixed); + g_free(fixed); + + /* FIXME: NmtNewtEntry doesn't correctly deal with us calling set_text() + * from inside the validator. + */ + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(entry)); + } + + return FALSE; +} + +static void +nmt_mac_entry_init(NmtMacEntry *entry) +{ + nmt_newt_entry_set_filter(NMT_NEWT_ENTRY(entry), mac_filter, NULL); + nmt_newt_entry_set_validator(NMT_NEWT_ENTRY(entry), mac_validator, NULL); +} + +static void +nmt_mac_entry_notify(GObject *object, GParamSpec *pspec) +{ + if (G_OBJECT_CLASS(nmt_mac_entry_parent_class)->notify) + G_OBJECT_CLASS(nmt_mac_entry_parent_class)->notify(object, pspec); + + if (pspec->owner_type == NMT_TYPE_NEWT_ENTRY && !strcmp(pspec->name, "text")) + g_object_notify(object, "mac-address"); +} + +static void +nmt_mac_entry_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MAC_LENGTH: + priv->mac_length = g_value_get_int(value); + priv->mac_str_length = priv->mac_length * 3 - 1; + break; + case PROP_MAC_ADDRESS: + nmt_newt_entry_set_text(NMT_NEWT_ENTRY(object), g_value_get_string(value)); + break; + case PROP_ENTRY_TYPE: + /* construct-only */ + priv->entry_type = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MAC_LENGTH: + g_value_set_int(value, priv->mac_length); + break; + case PROP_MAC_ADDRESS: + g_value_set_string(value, + nm_str_not_empty(nmt_newt_entry_get_text(NMT_NEWT_ENTRY(object)))); + break; + case PROP_ENTRY_TYPE: + g_value_set_int(value, priv->entry_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_class_init(NmtMacEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtMacEntryPrivate)); + + /* virtual methods */ + object_class->notify = nmt_mac_entry_notify; + object_class->set_property = nmt_mac_entry_set_property; + object_class->get_property = nmt_mac_entry_get_property; + + /** + * NmtMacEntry:mac-length: + * + * The length in bytes of the hardware address type the entry + * accepts: either %ETH_ALEN or %INFINIBAND_ALEN. + */ + g_object_class_install_property(object_class, + PROP_MAC_LENGTH, + g_param_spec_int("mac-length", + "", + "", + 0, + INFINIBAND_ALEN, + ETH_ALEN, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:mac-address: + * + * The MAC address, as a string (as with the various #NMSetting + * "mac-address" properties). + */ + g_object_class_install_property( + object_class, + PROP_MAC_ADDRESS, + g_param_spec_string("mac-address", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:entry-type: + * + * The type of the #NmtMacEntry. Can be either used for plain + * MAC addresses or for the extended format for cloned MAC addresses. + */ + g_object_class_install_property( + object_class, + PROP_ENTRY_TYPE, + g_param_spec_int("entry-type", + "", + "", + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, + NMT_MAC_ENTRY_TYPE_MAC, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-mac-entry.h b/src/nmtui/nmt-mac-entry.h new file mode 100644 index 00000000..0ed5a28c --- /dev/null +++ b/src/nmtui/nmt-mac-entry.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_MAC_ENTRY_H +#define NMT_MAC_ENTRY_H + +#include "nm-utils.h" +#include "libnmt-newt/nmt-newt.h" + +typedef enum { /*< skip >*/ + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, +} NmtMacEntryType; + +#define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type()) +#define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) +#define NMT_MAC_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) +#define NMT_IS_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_MAC_ENTRY)) +#define NMT_IS_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_MAC_ENTRY)) +#define NMT_MAC_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtMacEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtMacEntryClass; + +GType nmt_mac_entry_get_type(void); + +NmtNewtWidget *nmt_mac_entry_new(int width, int mac_length, NmtMacEntryType type); + +#endif /* NMT_MAC_ENTRY_H */ diff --git a/src/nmtui/nmt-mtu-entry.c b/src/nmtui/nmt-mtu-entry.c new file mode 100644 index 00000000..eab25a23 --- /dev/null +++ b/src/nmtui/nmt-mtu-entry.c @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mtu-entry + * @short_description: #NmtNewtEntry for MTU entry + * + * #NmtMtuEntry is an #NmtNewtEntry for entering MTU values. It will + * only allow typing numeric characters, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid MTU. + * + * The entry also has an attached #NmtNewtLabel. When the entry value + * is "0", the label will read "(default)". Otherwise, it reads "bytes", + * indicating the units used by the entry. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE(NmtMtuEntry, nmt_mtu_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_MTU_ENTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_MTU_ENTRY, NmtMtuEntryPrivate)) + +typedef struct { + int mtu; + + NmtNewtEntry *entry; + NmtNewtLabel *label; + +} NmtMtuEntryPrivate; + +enum { + PROP_0, + PROP_MTU, + + LAST_PROP +}; + +/** + * nmt_mtu_entry_new: + * + * Creates a new #NmtMtuEntry + * + * Returns: a new #NmtMtuEntry + */ +NmtNewtWidget * +nmt_mtu_entry_new(void) +{ + return g_object_new(NMT_TYPE_MTU_ENTRY, NULL); +} + +static gboolean +mtu_validator(NmtNewtEntry *entry, const char *text, gpointer user_data) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE(user_data); + + if (*text && !atoi(text)) { + nmt_newt_entry_set_text(entry, ""); + text = ""; + } + + if (!*text) + nmt_newt_label_set_text(priv->label, _("(default)")); + else + nmt_newt_label_set_text(priv->label, _("bytes")); + + return TRUE; +} + +static gboolean +mtu_transform_to_text(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + int mtu = g_value_get_int(source_value); + + if (mtu) + g_value_transform(source_value, target_value); + else + g_value_set_string(target_value, ""); + return TRUE; +} + +static void +nmt_mtu_entry_init(NmtMtuEntry *entry) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE(entry); + NmtNewtGrid * grid = NMT_NEWT_GRID(entry); + NmtNewtWidget * real_entry, *label; + + real_entry = nmt_newt_entry_numeric_new(10, 0, 65535); + priv->entry = NMT_NEWT_ENTRY(real_entry); + + label = nmt_newt_label_new(_("bytes")); + priv->label = NMT_NEWT_LABEL(label); + + nmt_newt_grid_add(grid, real_entry, 0, 0); + nmt_newt_grid_add(grid, label, 1, 0); + nmt_newt_widget_set_padding(label, 1, 0, 0, 0); + + nmt_newt_entry_set_validator(priv->entry, mtu_validator, entry); + g_object_bind_property_full(entry, + "mtu", + real_entry, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + mtu_transform_to_text, + NULL, + NULL, + NULL); +} + +static void +nmt_mtu_entry_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MTU: + priv->mtu = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MTU: + g_value_set_int(value, priv->mtu); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_class_init(NmtMtuEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtMtuEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_mtu_entry_set_property; + object_class->get_property = nmt_mtu_entry_get_property; + + /** + * NmtMtuEntry:mtu: + * + * The contents of the entry, as a number. + */ + g_object_class_install_property(object_class, + PROP_MTU, + g_param_spec_int("mtu", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-mtu-entry.h b/src/nmtui/nmt-mtu-entry.h new file mode 100644 index 00000000..4654dabf --- /dev/null +++ b/src/nmtui/nmt-mtu-entry.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_MTU_ENTRY_H +#define NMT_MTU_ENTRY_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_MTU_ENTRY (nmt_mtu_entry_get_type()) +#define NMT_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry)) +#define NMT_MTU_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) +#define NMT_IS_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_MTU_ENTRY)) +#define NMT_IS_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_MTU_ENTRY)) +#define NMT_MTU_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtMtuEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtMtuEntryClass; + +GType nmt_mtu_entry_get_type(void); + +NmtNewtWidget *nmt_mtu_entry_new(void); + +#endif /* NMT_MTU_ENTRY_H */ diff --git a/src/nmtui/nmt-page-bond.c b/src/nmtui/nmt-page-bond.c new file mode 100644 index 00000000..d3fab1ff --- /dev/null +++ b/src/nmtui/nmt-page-bond.c @@ -0,0 +1,445 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bond + * @short_description: The editor page for Bond connections + * + * Note that this is fairly different from most of the other editor + * pages, because #NMSettingBond doesn't have properties, so we + * can't just use #GBinding. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-bond.h" + +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> + +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "nmt-mac-entry.h" +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE(NmtPageBond, nmt_page_bond, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_BOND_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_BOND, NmtPageBondPrivate)) + +typedef enum { + NMT_PAGE_BOND_MONITORING_UNKNOWN = -1, + NMT_PAGE_BOND_MONITORING_MII = 0, + NMT_PAGE_BOND_MONITORING_ARP = 1, +} NmtPageBondMonitoringMode; + +typedef struct { + NmtSlaveList *slaves; + + /* Note: when adding new options to the UI also ensure they are + * initialized in bond_connection_setup_func() + */ + NmtNewtPopup * mode; + NmtNewtEntry * primary; + NmtNewtPopup * monitoring; + NmtNewtEntry * miimon; + NmtNewtEntry * updelay; + NmtNewtEntry * downdelay; + NmtNewtEntry * arp_interval; + NmtAddressList *arp_ip_target; + + NmtPageBondMonitoringMode monitoring_mode; + + NMSettingBond *s_bond; + GType slave_type; + gboolean updating; +} NmtPageBondPrivate; + +/*****************************************************************************/ + +static void arp_ip_target_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data); + +/*****************************************************************************/ + +NmtEditorPage * +nmt_page_bond_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_BOND, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_bond_init(NmtPageBond *bond) +{ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_UNKNOWN; + priv->slave_type = G_TYPE_NONE; +} + +static NmtNewtPopupEntry bond_mode[] = { + {N_("Round-robin"), "balance-rr"}, + {N_("Active Backup"), "active-backup"}, + {N_("XOR"), "balance-xor"}, + {N_("Broadcast"), "broadcast"}, + {N_("802.3ad"), "802.3ad"}, + {N_("Adaptive Transmit Load Balancing (tlb)"), "balance-tlb"}, + {N_("Adaptive Load Balancing (alb)"), "balance-alb"}, + {NULL, NULL}}; + +/* NB: the ordering/numbering here corresponds to NmtPageBondMonitoringMode */ +static NmtNewtPopupEntry bond_monitoring[] = {{N_("MII (recommended)"), "mii"}, + {N_("ARP"), "arp"}, + {NULL, NULL}}; + +static void +bond_options_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSettingBond * s_bond = NM_SETTING_BOND(object); + NmtPageBond * bond = NMT_PAGE_BOND(user_data); + NmtPageBondPrivate * priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + gs_free const char **ips = NULL; + const char * val; + gboolean visible_mii; + NMBondMode mode; + + if (priv->updating) + return; + + priv->updating = TRUE; + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MODE); + nmt_newt_popup_set_active_id(priv->mode, val); + + mode = _nm_setting_bond_mode_from_string(val ?: ""); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + nmt_newt_entry_set_text(priv->primary, val); + + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), mode == NM_BOND_MODE_ACTIVEBACKUP); + + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_UNKNOWN) { + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + if (_nm_utils_ascii_str_to_int64(val, 10, 0, G_MAXINT, 0) > 0) + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_ARP; + else + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_MII; + } + nmt_newt_popup_set_active(priv->monitoring, priv->monitoring_mode); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MIIMON); + nmt_newt_entry_set_text(priv->miimon, val ?: "0"); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_UPDELAY); + nmt_newt_entry_set_text(priv->updelay, val ?: "0"); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); + nmt_newt_entry_set_text(priv->downdelay, val ?: "0"); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + nmt_newt_entry_set_text(priv->arp_interval, val ?: "0"); + + val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + ips = nm_utils_bond_option_arp_ip_targets_split(val); + g_object_set(G_OBJECT(priv->arp_ip_target), + "strings", + ips ?: NM_PTRARRAY_EMPTY(const char *), + NULL); + + visible_mii = (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII); + + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->miimon), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->updelay), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->downdelay), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_interval), !visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_ip_target), !visible_mii); + + priv->updating = FALSE; +} + +static void +slaves_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NmtPageBond * bond = NMT_PAGE_BOND(user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + GPtrArray * slaves; + + g_object_get(object, "connections", &slaves, NULL); + if (slaves->len == 0) { + if (priv->slave_type == G_TYPE_NONE) + return; + priv->slave_type = G_TYPE_NONE; + } else { + NMConnection *slave = slaves->pdata[0]; + + if (priv->slave_type != G_TYPE_NONE) + return; + + if (nm_connection_is_type(slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } + + if (priv->slave_type == NM_TYPE_SETTING_INFINIBAND) { + nmt_newt_popup_set_active_id(priv->mode, "active-backup"); + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->mode), FALSE); + } else + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->mode), TRUE); +} + +static void +_bond_add_option(NMSettingBond *s_bond, const char *option, const char *value) +{ + if (nm_str_is_empty(value)) + nm_setting_bond_remove_option(s_bond, option); + else + nm_setting_bond_add_option(s_bond, option, value); + + if (nm_streq(option, NM_SETTING_BOND_OPTION_ARP_INTERVAL)) + _nm_setting_bond_remove_options_miimon(s_bond); + else if (nm_streq(option, NM_SETTING_BOND_OPTION_MIIMON)) + _nm_setting_bond_remove_options_arp_interval(s_bond); + nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE); +} + +#define WIDGET_CHANGED_FUNC(widget, func, option, dflt) \ + static void widget##_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) \ + { \ + NmtPageBond * bond = NMT_PAGE_BOND(user_data); \ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); \ + const char * v; \ + \ + if (priv->updating) \ + return; \ + \ + v = func(priv->widget); \ + priv->updating = TRUE; \ + _bond_add_option(priv->s_bond, option, v ?: dflt); \ + priv->updating = FALSE; \ + } + +WIDGET_CHANGED_FUNC(primary, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_PRIMARY, NULL) +WIDGET_CHANGED_FUNC(miimon, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_MIIMON, "0") +WIDGET_CHANGED_FUNC(updelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_UPDELAY, "0") +WIDGET_CHANGED_FUNC(downdelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_DOWNDELAY, "0") +WIDGET_CHANGED_FUNC(arp_interval, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0") + +static void +mode_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NmtPageBond * bond = NMT_PAGE_BOND(user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + const char * mode; + + if (priv->updating) + return; + + mode = nmt_newt_popup_get_active_id(priv->mode); + priv->updating = TRUE; + _bond_add_option(priv->s_bond, NM_SETTING_BOND_OPTION_MODE, mode); + priv->updating = FALSE; + + if (NM_IN_STRSET(mode, "balance-tlb", "balance-alb")) { + nmt_newt_popup_set_active(priv->monitoring, NMT_PAGE_BOND_MONITORING_MII); + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->monitoring), FALSE); + } else + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->monitoring), TRUE); + + if (NM_IN_STRSET(mode, "active-backup", "balance-alb", "balance-tlb")) { + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), TRUE); + _bond_add_option(priv->s_bond, + NM_SETTING_BOND_OPTION_PRIMARY, + nmt_newt_entry_get_text(priv->primary)); + } else { + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->primary), FALSE); + nm_setting_bond_remove_option(priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + } +} + +static void +monitoring_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NmtPageBond * bond = NMT_PAGE_BOND(user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + gboolean visible_mii; + + if (priv->updating) + return; + + priv->monitoring_mode = nmt_newt_popup_get_active(priv->monitoring); + visible_mii = (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII); + + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->miimon), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->updelay), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->downdelay), visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_interval), !visible_mii); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->arp_ip_target), !visible_mii); + + if (visible_mii) { + miimon_widget_changed(NULL, NULL, bond); + updelay_widget_changed(NULL, NULL, bond); + downdelay_widget_changed(NULL, NULL, bond); + } else { + arp_interval_widget_changed(NULL, NULL, bond); + arp_ip_target_widget_changed(NULL, NULL, bond); + } +} + +static void +arp_ip_target_widget_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NmtPageBond * bond = NMT_PAGE_BOND(user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + char ** ips, *target; + + if (priv->updating) + return; + + g_object_get(G_OBJECT(priv->arp_ip_target), "strings", &ips, NULL); + target = g_strjoinv(",", ips); + + priv->updating = TRUE; + _bond_add_option(priv->s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, target); + priv->updating = FALSE; + + g_free(target); + g_strfreev(ips); +} + +static gboolean +bond_connection_type_filter(GType connection_type, gpointer user_data) +{ + NmtPageBond * bond = user_data; + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + + if (priv->slave_type != NM_TYPE_SETTING_WIRED && connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND && connection_type == NM_TYPE_SETTING_WIRED) + return TRUE; + + return FALSE; +} + +static void +nmt_page_bond_constructed(GObject *object) +{ + NmtPageBond * bond = NMT_PAGE_BOND(object); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(bond); + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingWired * s_wired; + NMSettingBond * s_bond; + NmtNewtWidget * widget, *label; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(bond)); + s_bond = nm_connection_get_setting_bond(conn); + if (!s_bond) { + nm_connection_add_setting(conn, nm_setting_bond_new()); + s_bond = nm_connection_get_setting_bond(conn); + } + priv->s_bond = s_bond; + + s_wired = nm_connection_get_setting_wired(conn); + if (!s_wired) { + nm_connection_add_setting(conn, nm_setting_wired_new()); + s_wired = nm_connection_get_setting_wired(conn); + } + + section = nmt_editor_section_new(_("BOND"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_separator_new(); + nmt_editor_grid_append(grid, _("Slaves"), widget, NULL); + nmt_editor_grid_set_row_flags(grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new(conn, bond_connection_type_filter, bond); + g_signal_connect(widget, "notify::connections", G_CALLBACK(slaves_changed), bond); + nmt_editor_grid_append(grid, NULL, widget, NULL); + priv->slaves = NMT_SLAVE_LIST(widget); + + widget = nmt_newt_popup_new(bond_mode); + g_signal_connect(widget, "notify::active-id", G_CALLBACK(mode_widget_changed), bond); + nmt_editor_grid_append(grid, _("Mode"), widget, NULL); + priv->mode = NMT_NEWT_POPUP(widget); + + widget = nmt_newt_entry_new(40, 0); + g_signal_connect(widget, "notify::text", G_CALLBACK(primary_widget_changed), bond); + nmt_editor_grid_append(grid, _("Primary"), widget, NULL); + priv->primary = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_popup_new(bond_monitoring); + g_signal_connect(widget, "notify::active", G_CALLBACK(monitoring_widget_changed), bond); + nmt_editor_grid_append(grid, _("Link monitoring"), widget, NULL); + priv->monitoring = NMT_NEWT_POPUP(widget); + + widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); + g_signal_connect(widget, "notify::text", G_CALLBACK(miimon_widget_changed), bond); + label = nmt_newt_label_new(C_("milliseconds", "ms")); + nmt_editor_grid_append(grid, _("Monitoring frequency"), widget, label); + priv->miimon = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); + g_signal_connect(widget, "notify::text", G_CALLBACK(updelay_widget_changed), bond); + label = nmt_newt_label_new(C_("milliseconds", "ms")); + nmt_editor_grid_append(grid, _("Link up delay"), widget, label); + priv->updelay = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); + g_signal_connect(widget, "notify::text", G_CALLBACK(downdelay_widget_changed), bond); + label = nmt_newt_label_new(C_("milliseconds", "ms")); + nmt_editor_grid_append(grid, _("Link down delay"), widget, label); + priv->downdelay = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); + g_signal_connect(widget, "notify::text", G_CALLBACK(arp_interval_widget_changed), bond); + label = nmt_newt_label_new(C_("milliseconds", "ms")); + nmt_editor_grid_append(grid, _("Monitoring frequency"), widget, label); + priv->arp_interval = NMT_NEWT_ENTRY(widget); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP4); + g_signal_connect(widget, "notify::strings", G_CALLBACK(arp_ip_target_widget_changed), bond); + nmt_editor_grid_append(grid, _("ARP targets"), widget, NULL); + priv->arp_ip_target = NMT_ADDRESS_LIST(widget); + + widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Cloned MAC address"), widget, NULL); + + g_signal_connect(s_bond, + "notify::" NM_SETTING_BOND_OPTIONS, + G_CALLBACK(bond_options_changed), + bond); + bond_options_changed(G_OBJECT(s_bond), NULL, bond); + slaves_changed(G_OBJECT(priv->slaves), NULL, bond); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(bond), section); + + G_OBJECT_CLASS(nmt_page_bond_parent_class)->constructed(object); +} + +static void +nmt_page_bond_saved(NmtEditorPage *editor_page) +{ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE(editor_page); + + nmt_edit_connection_list_recommit(NMT_EDIT_CONNECTION_LIST(priv->slaves)); +} + +static void +nmt_page_bond_class_init(NmtPageBondClass *bond_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(bond_class); + NmtEditorPageClass *editor_page_class = NMT_EDITOR_PAGE_CLASS(bond_class); + + g_type_class_add_private(bond_class, sizeof(NmtPageBondPrivate)); + + object_class->constructed = nmt_page_bond_constructed; + editor_page_class->saved = nmt_page_bond_saved; +} diff --git a/src/nmtui/nmt-page-bond.h b/src/nmtui/nmt-page-bond.h new file mode 100644 index 00000000..9481ec45 --- /dev/null +++ b/src/nmtui/nmt-page-bond.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BOND_H +#define NMT_PAGE_BOND_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_BOND (nmt_page_bond_get_type()) +#define NMT_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_BOND, NmtPageBond)) +#define NMT_PAGE_BOND_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) +#define NMT_IS_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_BOND)) +#define NMT_IS_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_BOND)) +#define NMT_PAGE_BOND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageBond; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageBondClass; + +GType nmt_page_bond_get_type(void); + +NmtEditorPage *nmt_page_bond_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_BOND_H */ diff --git a/src/nmtui/nmt-page-bridge-port.c b/src/nmtui/nmt-page-bridge-port.c new file mode 100644 index 00000000..30163d15 --- /dev/null +++ b/src/nmtui/nmt-page-bridge-port.c @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge-port + * @short_description: The editor page for Bridge ports + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-bridge-port.h" + +G_DEFINE_TYPE(NmtPageBridgePort, nmt_page_bridge_port, NMT_TYPE_EDITOR_PAGE) + +NmtEditorPage * +nmt_page_bridge_port_new(NMConnection *conn) +{ + return g_object_new(NMT_TYPE_PAGE_BRIDGE_PORT, "connection", conn, NULL); +} + +static void +nmt_page_bridge_port_init(NmtPageBridgePort *bridge) +{} + +static void +nmt_page_bridge_port_constructed(GObject *object) +{ + NmtPageBridgePort * bridge = NMT_PAGE_BRIDGE_PORT(object); + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingBridgePort *s_port; + NmtNewtWidget * widget; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(bridge)); + s_port = nm_connection_get_setting_bridge_port(conn); + if (!s_port) { + nm_connection_add_setting(conn, nm_setting_bridge_port_new()); + s_port = nm_connection_get_setting_bridge_port(conn); + } + + section = nmt_editor_section_new(_("BRIDGE PORT"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_entry_numeric_new(10, 0, 63); + g_object_bind_property(s_port, + NM_SETTING_BRIDGE_PORT_PRIORITY, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new(10, 1, 65535); + g_object_bind_property(s_port, + NM_SETTING_BRIDGE_PORT_PATH_COST, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Path cost"), widget, NULL); + + widget = nmt_newt_checkbox_new(_("Hairpin mode")); + g_object_bind_property(s_port, + NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(bridge), section); + + G_OBJECT_CLASS(nmt_page_bridge_port_parent_class)->constructed(object); +} + +static void +nmt_page_bridge_port_class_init(NmtPageBridgePortClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(bridge_class); + + object_class->constructed = nmt_page_bridge_port_constructed; +} diff --git a/src/nmtui/nmt-page-bridge-port.h b/src/nmtui/nmt-page-bridge-port.h new file mode 100644 index 00000000..dc6a479c --- /dev/null +++ b/src/nmtui/nmt-page-bridge-port.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_PORT_H +#define NMT_PAGE_BRIDGE_PORT_H + +#include "nmt-editor-page.h" + +#define NMT_TYPE_PAGE_BRIDGE_PORT (nmt_page_bridge_port_get_type()) +#define NMT_PAGE_BRIDGE_PORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort)) +#define NMT_PAGE_BRIDGE_PORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) +#define NMT_IS_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_IS_PAGE_BRIDGE_PORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_PAGE_BRIDGE_PORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageBridgePort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageBridgePortClass; + +GType nmt_page_bridge_port_get_type(void); + +NmtEditorPage *nmt_page_bridge_port_new(NMConnection *conn); + +#endif /* NMT_PAGE_BRIDGE_PORT_H */ diff --git a/src/nmtui/nmt-page-bridge.c b/src/nmtui/nmt-page-bridge.c new file mode 100644 index 00000000..52af8db1 --- /dev/null +++ b/src/nmtui/nmt-page-bridge.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge + * @short_description: The editor page for Bridge connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-bridge.h" + +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE(NmtPageBridge, nmt_page_bridge, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_BRIDGE_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgePrivate)) + +typedef struct { + NmtSlaveList *slaves; +} NmtPageBridgePrivate; + +NmtEditorPage * +nmt_page_bridge_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_BRIDGE, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_bridge_init(NmtPageBridge *bridge) +{} + +static gboolean +bridge_connection_type_filter(GType connection_type, gpointer user_data) +{ + return (connection_type == NM_TYPE_SETTING_WIRED || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN); +} + +static void +nmt_page_bridge_constructed(GObject *object) +{ + NmtPageBridge * bridge = NMT_PAGE_BRIDGE(object); + NmtPageBridgePrivate *priv = NMT_PAGE_BRIDGE_GET_PRIVATE(bridge); + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingBridge * s_bridge; + NmtNewtWidget * widget, *label, *stp; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(bridge)); + s_bridge = nm_connection_get_setting_bridge(conn); + if (!s_bridge) { + nm_connection_add_setting(conn, nm_setting_bridge_new()); + s_bridge = nm_connection_get_setting_bridge(conn); + } + + section = nmt_editor_section_new(_("BRIDGE"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_separator_new(); + nmt_editor_grid_append(grid, _("Slaves"), widget, NULL); + nmt_editor_grid_set_row_flags(grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new(conn, bridge_connection_type_filter, bridge); + nmt_editor_grid_append(grid, NULL, widget, NULL); + priv->slaves = NMT_SLAVE_LIST(widget); + + widget = nmt_newt_entry_numeric_new(10, 0, 1000000); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_AGEING_TIME, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new(_("seconds")); + nmt_editor_grid_append(grid, _("Aging time"), widget, label); + + widget = nmt_newt_checkbox_new(_("Enable IGMP snooping")); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_MULTICAST_SNOOPING, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = stp = nmt_newt_checkbox_new(_("Enable STP (Spanning Tree Protocol)")); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_STP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_entry_numeric_new(10, 0, G_MAXINT); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_PRIORITY, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_STP, + widget, + "sensitive", + G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new(10, 2, 30); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_FORWARD_DELAY, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_STP, + widget, + "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new(_("seconds")); + nmt_editor_grid_append(grid, _("Forward delay"), widget, label); + + widget = nmt_newt_entry_numeric_new(10, 1, 10); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_HELLO_TIME, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_STP, + widget, + "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new(_("seconds")); + nmt_editor_grid_append(grid, _("Hello time"), widget, label); + + widget = nmt_newt_entry_numeric_new(10, 6, 40); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_MAX_AGE, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_STP, + widget, + "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new(_("seconds")); + nmt_editor_grid_append(grid, _("Max age"), widget, label); + + widget = nmt_newt_entry_numeric_new(10, 0, 65535); + g_object_bind_property(s_bridge, + NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Group forward mask"), widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(bridge), section); + + G_OBJECT_CLASS(nmt_page_bridge_parent_class)->constructed(object); +} + +static void +nmt_page_bridge_saved(NmtEditorPage *editor_page) +{ + NmtPageBridgePrivate *priv = NMT_PAGE_BRIDGE_GET_PRIVATE(editor_page); + + nmt_edit_connection_list_recommit(NMT_EDIT_CONNECTION_LIST(priv->slaves)); +} + +static void +nmt_page_bridge_class_init(NmtPageBridgeClass *bridge_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(bridge_class); + NmtEditorPageClass *editor_page_class = NMT_EDITOR_PAGE_CLASS(bridge_class); + + object_class->constructed = nmt_page_bridge_constructed; + editor_page_class->saved = nmt_page_bridge_saved; +} diff --git a/src/nmtui/nmt-page-bridge.h b/src/nmtui/nmt-page-bridge.h new file mode 100644 index 00000000..c5c7a39c --- /dev/null +++ b/src/nmtui/nmt-page-bridge.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_H +#define NMT_PAGE_BRIDGE_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_BRIDGE (nmt_page_bridge_get_type()) +#define NMT_PAGE_BRIDGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridge)) +#define NMT_PAGE_BRIDGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) +#define NMT_IS_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_IS_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_PAGE_BRIDGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageBridge; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageBridgeClass; + +GType nmt_page_bridge_get_type(void); + +NmtEditorPage *nmt_page_bridge_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_BRIDGE_H */ diff --git a/src/nmtui/nmt-page-dsl.c b/src/nmtui/nmt-page-dsl.c new file mode 100644 index 00000000..d7e0d144 --- /dev/null +++ b/src/nmtui/nmt-page-dsl.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-dsl + * @short_description: The editor page for DSL connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-dsl.h" +#include "nmt-page-ethernet.h" +#include "nmt-page-ppp.h" +#include "nmt-password-fields.h" + +G_DEFINE_TYPE(NmtPageDsl, nmt_page_dsl, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_DSL_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_DSL, NmtPageDslPrivate)) + +typedef struct { + NmtEditorPage *ethernet_page, *ppp_page; + +} NmtPageDslPrivate; + +NmtEditorPage * +nmt_page_dsl_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_DSL, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_dsl_init(NmtPageDsl *dsl) +{} + +static NmtEditorSection * +build_dsl_section(NmtPageDsl *dsl, NMSettingPppoe *s_pppoe) +{ + NmtEditorSection *section; + NmtEditorGrid * grid; + NmtNewtWidget * widget; + + section = nmt_editor_section_new(_("DSL"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Username"), widget, NULL); + g_object_bind_property(s_pppoe, + NM_SETTING_PPPOE_USERNAME, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_password_fields_new(40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property(s_pppoe, + NM_SETTING_PPPOE_PASSWORD, + widget, + "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, _("Password"), widget, NULL); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Service"), widget, NULL); + g_object_bind_property(s_pppoe, + NM_SETTING_PPPOE_SERVICE, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + return section; +} + +static void +nmt_page_dsl_constructed(GObject *object) +{ + NmtPageDsl * dsl = NMT_PAGE_DSL(object); + NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE(dsl); + NMConnection * conn; + NMSettingPppoe * s_pppoe; + NmtEditorSection * section; + const GSList * sections, *iter; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(dsl)); + s_pppoe = nm_connection_get_setting_pppoe(conn); + if (!s_pppoe) { + nm_connection_add_setting(conn, nm_setting_pppoe_new()); + s_pppoe = nm_connection_get_setting_pppoe(conn); + } + + section = build_dsl_section(dsl, s_pppoe); + nmt_editor_page_add_section(NMT_EDITOR_PAGE(dsl), section); + + priv->ethernet_page = + nmt_page_ethernet_new(conn, + nmt_editor_page_device_get_device_entry(NMT_EDITOR_PAGE_DEVICE(dsl))); + sections = nmt_editor_page_get_sections(priv->ethernet_page); + for (iter = sections; iter; iter = iter->next) + nmt_editor_page_add_section(NMT_EDITOR_PAGE(dsl), iter->data); + + priv->ppp_page = nmt_page_ppp_new(conn); + sections = nmt_editor_page_get_sections(priv->ppp_page); + for (iter = sections; iter; iter = iter->next) + nmt_editor_page_add_section(NMT_EDITOR_PAGE(dsl), iter->data); + + G_OBJECT_CLASS(nmt_page_dsl_parent_class)->constructed(object); +} + +static void +nmt_page_dsl_finalize(GObject *object) +{ + NmtPageDsl * dsl = NMT_PAGE_DSL(object); + NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE(dsl); + + g_clear_object(&priv->ethernet_page); + g_clear_object(&priv->ppp_page); + + G_OBJECT_CLASS(nmt_page_dsl_parent_class)->finalize(object); +} + +static void +nmt_page_dsl_class_init(NmtPageDslClass *dsl_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(dsl_class); + + g_type_class_add_private(object_class, sizeof(NmtPageDslPrivate)); + + object_class->constructed = nmt_page_dsl_constructed; + object_class->finalize = nmt_page_dsl_finalize; +} diff --git a/src/nmtui/nmt-page-dsl.h b/src/nmtui/nmt-page-dsl.h new file mode 100644 index 00000000..e14d67be --- /dev/null +++ b/src/nmtui/nmt-page-dsl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_DSL_H +#define NMT_PAGE_DSL_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_DSL (nmt_page_dsl_get_type()) +#define NMT_PAGE_DSL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_DSL, NmtPageDsl)) +#define NMT_PAGE_DSL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_DSL, NmtPageDslClass)) +#define NMT_IS_PAGE_DSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_DSL)) +#define NMT_IS_PAGE_DSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_DSL)) +#define NMT_PAGE_DSL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_DSL, NmtPageDslClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageDsl; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageDslClass; + +GType nmt_page_dsl_get_type(void); + +NmtEditorPage *nmt_page_dsl_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_DSL_H */ diff --git a/src/nmtui/nmt-page-ethernet.c b/src/nmtui/nmt-page-ethernet.c new file mode 100644 index 00000000..f5dc9119 --- /dev/null +++ b/src/nmtui/nmt-page-ethernet.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ethernet + * @short_description: The editor page for Ethernet connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-ethernet.h" + +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> + +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE(NmtPageEthernet, nmt_page_ethernet, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_ethernet_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_ETHERNET, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_ethernet_init(NmtPageEthernet *ethernet) +{} + +static void +nmt_page_ethernet_constructed(GObject *object) +{ + NmtPageEthernet * ethernet = NMT_PAGE_ETHERNET(object); + NmtDeviceEntry * deventry; + NmtEditorSection *section; + NmtEditorGrid * grid; + NMSettingWired * s_wired; + NmtNewtWidget * widget; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(ethernet)); + s_wired = nm_connection_get_setting_wired(conn); + if (!s_wired) { + nm_connection_add_setting(conn, nm_setting_wired_new()); + s_wired = nm_connection_get_setting_wired(conn); + } + + deventry = nmt_editor_page_device_get_device_entry(NMT_EDITOR_PAGE_DEVICE(object)); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + deventry, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new(_("ETHERNET"), NULL, FALSE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new(); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_MTU, + widget, + "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(ethernet), section); + + G_OBJECT_CLASS(nmt_page_ethernet_parent_class)->constructed(object); +} + +static void +nmt_page_ethernet_class_init(NmtPageEthernetClass *ethernet_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ethernet_class); + + object_class->constructed = nmt_page_ethernet_constructed; +} diff --git a/src/nmtui/nmt-page-ethernet.h b/src/nmtui/nmt-page-ethernet.h new file mode 100644 index 00000000..f7e6cf15 --- /dev/null +++ b/src/nmtui/nmt-page-ethernet.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_ETHERNET_H +#define NMT_PAGE_ETHERNET_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_ETHERNET (nmt_page_ethernet_get_type()) +#define NMT_PAGE_ETHERNET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernet)) +#define NMT_PAGE_ETHERNET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) +#define NMT_IS_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_IS_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_PAGE_ETHERNET_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageEthernet; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageEthernetClass; + +GType nmt_page_ethernet_get_type(void); + +NmtEditorPage *nmt_page_ethernet_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_ETHERNET_H */ diff --git a/src/nmtui/nmt-page-infiniband.c b/src/nmtui/nmt-page-infiniband.c new file mode 100644 index 00000000..c0e3611b --- /dev/null +++ b/src/nmtui/nmt-page-infiniband.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-infiniband + * @short_description: The editor page for InfiniBand connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-infiniband.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE(NmtPageInfiniband, nmt_page_infiniband, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_infiniband_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_INFINIBAND, + "connection", + conn, + "device-entry", + deventry, + NULL); +} + +static void +nmt_page_infiniband_init(NmtPageInfiniband *infiniband) +{} + +static NmtNewtPopupEntry transport_mode[] = {{N_("Datagram"), "datagram"}, + {N_("Connected"), "connected"}, + {NULL, NULL}}; + +static void +nmt_page_infiniband_constructed(GObject *object) +{ + NmtPageInfiniband * infiniband = NMT_PAGE_INFINIBAND(object); + NmtDeviceEntry * deventry; + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingInfiniband *s_ib; + NmtNewtWidget * widget; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(infiniband)); + s_ib = nm_connection_get_setting_infiniband(conn); + if (!s_ib) { + nm_connection_add_setting(conn, nm_setting_infiniband_new()); + s_ib = nm_connection_get_setting_infiniband(conn); + } + /* initialize 'transport-mode' if it is NULL */ + if (!nm_setting_infiniband_get_transport_mode(s_ib)) { + g_object_set(G_OBJECT(s_ib), NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); + } + + deventry = nmt_editor_page_device_get_device_entry(NMT_EDITOR_PAGE_DEVICE(object)); + g_object_bind_property(s_ib, + NM_SETTING_INFINIBAND_MAC_ADDRESS, + deventry, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new(_("INFINIBAND"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_popup_new(transport_mode); + g_object_bind_property(s_ib, + NM_SETTING_INFINIBAND_TRANSPORT_MODE, + widget, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Transport mode"), widget, NULL); + + widget = nmt_mtu_entry_new(); + g_object_bind_property(s_ib, + NM_SETTING_INFINIBAND_MTU, + widget, + "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(infiniband), section); + + G_OBJECT_CLASS(nmt_page_infiniband_parent_class)->constructed(object); +} + +static void +nmt_page_infiniband_class_init(NmtPageInfinibandClass *infiniband_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(infiniband_class); + + object_class->constructed = nmt_page_infiniband_constructed; +} diff --git a/src/nmtui/nmt-page-infiniband.h b/src/nmtui/nmt-page-infiniband.h new file mode 100644 index 00000000..da8fb3d6 --- /dev/null +++ b/src/nmtui/nmt-page-infiniband.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_INFINIBAND_H +#define NMT_PAGE_INFINIBAND_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_INFINIBAND (nmt_page_infiniband_get_type()) +#define NMT_PAGE_INFINIBAND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfiniband)) +#define NMT_PAGE_INFINIBAND_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) +#define NMT_IS_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_IS_PAGE_INFINIBAND_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_PAGE_INFINIBAND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageInfiniband; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageInfinibandClass; + +GType nmt_page_infiniband_get_type(void); + +NmtEditorPage *nmt_page_infiniband_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_INFINIBAND_H */ diff --git a/src/nmtui/nmt-page-ip-tunnel.c b/src/nmtui/nmt-page-ip-tunnel.c new file mode 100644 index 00000000..da6d12b9 --- /dev/null +++ b/src/nmtui/nmt-page-ip-tunnel.c @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ +/** + * SECTION:nmt-page-ip_tunnel + * @short_description: The editor page for IP tunnel connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-ip-tunnel.h" + +#include "nmt-device-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE(NmtPageIPTunnel, nmt_page_ip_tunnel, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_IP_TUNNEL_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_IP_TUNNEL, NmtPageIPTunnelPrivate)) + +typedef struct { + NmtNewtEntry *input_key; + NmtNewtEntry *output_key; +} NmtPageIPTunnelPrivate; + +NmtEditorPage * +nmt_page_ip_tunnel_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_IP_TUNNEL, + "connection", + conn, + "device-entry", + deventry, + NULL); +} + +static void +nmt_page_ip_tunnel_init(NmtPageIPTunnel *ip_tunnel) +{} + +static void +mode_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSettingIPTunnel * s_ip_tunnel = NM_SETTING_IP_TUNNEL(object); + NmtPageIPTunnel * ip_tunnel = NMT_PAGE_IP_TUNNEL(user_data); + NmtPageIPTunnelPrivate *priv = NMT_PAGE_IP_TUNNEL_GET_PRIVATE(ip_tunnel); + NMIPTunnelMode mode; + gboolean enable_keys; + + mode = nm_setting_ip_tunnel_get_mode(s_ip_tunnel); + enable_keys = NM_IN_SET(mode, NM_IP_TUNNEL_MODE_GRE, NM_IP_TUNNEL_MODE_IP6GRE); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->input_key), enable_keys); + nmt_newt_widget_set_visible(NMT_NEWT_WIDGET(priv->output_key), enable_keys); + + if (!enable_keys) { + nmt_newt_entry_set_text(priv->input_key, ""); + nmt_newt_entry_set_text(priv->output_key, ""); + } +} + +static NmtNewtPopupEntry tunnel_mode[] = { + /* The order must match the NM_IP_TUNNEL_MODE_* enum */ + {N_("IPIP"), "IPIP"}, + {N_("GRE"), "GRE"}, + {N_("SIT"), "SIT"}, + {N_("ISATAP"), "ISATAP"}, + {N_("VTI"), "VTI"}, + {N_("IP6IP6"), "IP6IP6"}, + {N_("IPIP6"), "IPIP6"}, + {N_("IP6GRE"), "IP6GRE"}, + {N_("VTI6"), "VTI6"}, + {NULL, NULL}}; + +static gboolean +add_offset(GBinding *binding, const GValue *from_value, GValue *to_value, gpointer user_data) +{ + guint v; + int offset = GPOINTER_TO_INT(user_data); + + g_return_val_if_fail(G_VALUE_HOLDS(from_value, G_TYPE_UINT), FALSE); + g_return_val_if_fail(G_VALUE_HOLDS(to_value, G_TYPE_UINT), FALSE); + + v = g_value_get_uint(from_value); + v += offset; + + g_value_set_uint(to_value, v); + + return TRUE; +} + +static void +nmt_page_ip_tunnel_constructed(GObject *object) +{ + NmtPageIPTunnel * ip_tunnel = NMT_PAGE_IP_TUNNEL(object); + NmtPageIPTunnelPrivate *priv = NMT_PAGE_IP_TUNNEL_GET_PRIVATE(ip_tunnel); + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingIPTunnel * s_ip_tunnel; + NmtNewtWidget * widget, *parent; + NMConnection * conn; + GClosure * s2w, *w2s; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(ip_tunnel)); + s_ip_tunnel = nm_connection_get_setting_ip_tunnel(conn); + if (!s_ip_tunnel) { + nm_connection_add_setting(conn, nm_setting_ip_tunnel_new()); + s_ip_tunnel = nm_connection_get_setting_ip_tunnel(conn); + } + + /* Initialize the mode for new connections */ + if (nm_setting_ip_tunnel_get_mode(s_ip_tunnel) == NM_IP_TUNNEL_MODE_UNKNOWN) { + g_object_set(s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, (guint) NM_IP_TUNNEL_MODE_IPIP, NULL); + } + + section = nmt_editor_section_new(_("IP tunnel"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + /* To convert between widget index (0-based) and setting index (1-based) */ + s2w = g_cclosure_new(G_CALLBACK(add_offset), GINT_TO_POINTER(-1), NULL); + w2s = g_cclosure_new(G_CALLBACK(add_offset), GINT_TO_POINTER(1), NULL); + + widget = nmt_newt_popup_new(tunnel_mode); + g_object_bind_property_with_closures(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_MODE, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + s2w, + w2s); + nmt_editor_grid_append(grid, _("Mode"), widget, NULL); + + widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_PARENT, + widget, + "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Local IP"), widget, NULL); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_LOCAL, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Remote IP"), widget, NULL); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_REMOTE, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Input key"), widget, NULL); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_INPUT_KEY, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + priv->input_key = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_entry_new(40, 0); + nmt_editor_grid_append(grid, _("Output key"), widget, NULL); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_OUTPUT_KEY, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + priv->output_key = NMT_NEWT_ENTRY(widget); + + widget = nmt_mtu_entry_new(); + g_object_bind_property(s_ip_tunnel, + NM_SETTING_IP_TUNNEL_MTU, + widget, + "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("MTU"), widget, NULL); + + g_signal_connect(s_ip_tunnel, + "notify::" NM_SETTING_IP_TUNNEL_MODE, + G_CALLBACK(mode_changed), + ip_tunnel); + mode_changed(G_OBJECT(s_ip_tunnel), NULL, ip_tunnel); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(ip_tunnel), section); + + G_OBJECT_CLASS(nmt_page_ip_tunnel_parent_class)->constructed(object); +} + +static void +nmt_page_ip_tunnel_class_init(NmtPageIPTunnelClass *ip_tunnel_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ip_tunnel_class); + + g_type_class_add_private(ip_tunnel_class, sizeof(NmtPageIPTunnelPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_page_ip_tunnel_constructed; +} diff --git a/src/nmtui/nmt-page-ip-tunnel.h b/src/nmtui/nmt-page-ip-tunnel.h new file mode 100644 index 00000000..dcc27880 --- /dev/null +++ b/src/nmtui/nmt-page-ip-tunnel.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP_TUNNEL_H +#define NMT_PAGE_IP_TUNNEL_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_IP_TUNNEL (nmt_page_ip_tunnel_get_type()) +#define NMT_PAGE_IP_TUNNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_IP_TUNNEL, NmtPageIPTunnel)) +#define NMT_PAGE_IP_TUNNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_IP_TUNNEL, NmtPageIPTunnelClass)) +#define NMT_IS_PAGE_IP_TUNNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_IP_TUNNEL)) +#define NMT_IS_PAGE_IP_TUNNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_IP_TUNNEL)) +#define NMT_PAGE_IP_TUNNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_IP_TUNNEL, NmtPageIPTunnelClass)) + +typedef struct { + NmtEditorPageDevice parent; +} NmtPageIPTunnel; + +typedef struct { + NmtEditorPageDeviceClass parent; +} NmtPageIPTunnelClass; + +GType nmt_page_ip_tunnel_get_type(void); + +NmtEditorPage *nmt_page_ip_tunnel_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_IP_TUNNEL_H */ diff --git a/src/nmtui/nmt-page-ip4.c b/src/nmtui/nmt-page-ip4.c new file mode 100644 index 00000000..ecd71447 --- /dev/null +++ b/src/nmtui/nmt-page-ip4.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip4 + * @short_description: The editor page for IP4 configuration + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmt-page-ip4.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE(NmtPageIP4, nmt_page_ip4, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip4methods[] = { + {N_("Disabled"), NM_SETTING_IP4_CONFIG_METHOD_DISABLED}, + {N_("Automatic"), NM_SETTING_IP4_CONFIG_METHOD_AUTO}, + {N_("Link-Local"), NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL}, + {N_("Manual"), NM_SETTING_IP4_CONFIG_METHOD_MANUAL}, + {N_("Shared"), NM_SETTING_IP4_CONFIG_METHOD_SHARED}, + {NULL, NULL}}; + +NmtEditorPage * +nmt_page_ip4_new(NMConnection *conn) +{ + return g_object_new(NMT_TYPE_PAGE_IP4, "connection", conn, NULL); +} + +static void +nmt_page_ip4_init(NmtPageIP4 *ip4) +{} + +static void +edit_routes(NmtNewtButton *button, gpointer user_data) +{ + NMSetting * s_ip4 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new(s_ip4); + nmt_newt_form_run_sync(form); + g_object_unref(form); +} + +static gboolean +ip4_routes_transform_to_description(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + GPtrArray *routes; + char * text; + + routes = g_value_get_boxed(source_value); + if (!routes || !routes->len) + text = g_strdup(_("(No custom routes)")); + else { + text = g_strdup_printf( + g_dngettext(GETTEXT_PACKAGE, "One custom route", "%d custom routes", routes->len), + routes->len); + } + + g_value_take_string(target_value, text); + return TRUE; +} + +static void +nmt_page_ip4_constructed(GObject *object) +{ + NmtPageIP4 * ip4 = NMT_PAGE_IP4(object); + gboolean show_by_default; + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingIPConfig *s_ip4; + NmtNewtWidget * widget, *button; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(ip4)); + s_ip4 = nm_connection_get_setting_ip4_config(conn); + if (!s_ip4) { + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(conn, (NMSetting *) s_ip4); + } + + widget = nmt_newt_popup_new(ip4methods); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + widget, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + if (!g_strcmp0(nm_setting_ip_config_get_method(s_ip4), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + show_by_default = TRUE; + else if (nm_setting_ip_config_get_num_addresses(s_ip4)) + show_by_default = TRUE; + else + show_by_default = FALSE; + + section = nmt_editor_section_new(_("IPv4 CONFIGURATION"), widget, show_by_default); + grid = nmt_editor_section_get_body(section); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP4_WITH_PREFIX); + nm_editor_bind_ip_addresses_with_prefix_to_strv(AF_INET, + s_ip4, + NM_SETTING_IP_CONFIG_ADDRESSES, + widget, + "strings", + G_BINDING_BIDIRECTIONAL + | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new(25, AF_INET, FALSE, TRUE); + nm_editor_bind_ip_gateway_to_string(AF_INET, + s_ip4, + widget, + "text", + "sensitive", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP4); + nm_editor_bind_ip_addresses_to_strv(AF_INET, + s_ip4, + NM_SETTING_IP_CONFIG_DNS, + widget, + "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_DNS_SEARCH, + widget, + "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Search domains"), widget, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = g_object_new(NMT_TYPE_NEWT_LABEL, "text", "", "style", NMT_NEWT_LABEL_PLAIN, NULL); + g_object_bind_property_full(s_ip4, + NM_SETTING_IP_CONFIG_ROUTES, + widget, + "text", + G_BINDING_SYNC_CREATE, + ip4_routes_transform_to_description, + NULL, + NULL, + NULL); + button = nmt_newt_button_new(_("Edit...")); + g_signal_connect(button, "clicked", G_CALLBACK(edit_routes), s_ip4); + nmt_editor_grid_append(grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new(_("Never use this network for default route")); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Ignore automatically obtained routes")); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Ignore automatically obtained DNS parameters")); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_newt_checkbox_new(_("Require IPv4 addressing for this connection")); + g_object_bind_property(s_ip4, + NM_SETTING_IP_CONFIG_MAY_FAIL, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL + | G_BINDING_INVERT_BOOLEAN); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(ip4), section); + + G_OBJECT_CLASS(nmt_page_ip4_parent_class)->constructed(object); +} + +static void +nmt_page_ip4_class_init(NmtPageIP4Class *ip4_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ip4_class); + + object_class->constructed = nmt_page_ip4_constructed; +} diff --git a/src/nmtui/nmt-page-ip4.h b/src/nmtui/nmt-page-ip4.h new file mode 100644 index 00000000..52d3c842 --- /dev/null +++ b/src/nmtui/nmt-page-ip4.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP4_H +#define NMT_PAGE_IP4_H + +#include "nmt-editor-page.h" + +#define NMT_TYPE_PAGE_IP4 (nmt_page_ip4_get_type()) +#define NMT_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4)) +#define NMT_PAGE_IP4_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) +#define NMT_IS_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_IP4)) +#define NMT_IS_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_IP4)) +#define NMT_PAGE_IP4_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP4; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP4Class; + +GType nmt_page_ip4_get_type(void); + +NmtEditorPage *nmt_page_ip4_new(NMConnection *conn); + +#endif /* NMT_PAGE_IP4_H */ diff --git a/src/nmtui/nmt-page-ip6.c b/src/nmtui/nmt-page-ip6.c new file mode 100644 index 00000000..f79c553d --- /dev/null +++ b/src/nmtui/nmt-page-ip6.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip6 + * @short_description: The editor page for IP6 configuration + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmt-page-ip6.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE(NmtPageIP6, nmt_page_ip6, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip6methods[] = { + {N_("Ignore"), NM_SETTING_IP6_CONFIG_METHOD_IGNORE}, + {N_("Automatic"), NM_SETTING_IP6_CONFIG_METHOD_AUTO}, + {N_("Automatic (DHCP-only)"), NM_SETTING_IP6_CONFIG_METHOD_DHCP}, + {N_("Link-Local"), NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL}, + {N_("Manual"), NM_SETTING_IP6_CONFIG_METHOD_MANUAL}, + {N_("Disabled"), NM_SETTING_IP6_CONFIG_METHOD_DISABLED}, + {NULL, NULL}}; + +NmtEditorPage * +nmt_page_ip6_new(NMConnection *conn) +{ + return g_object_new(NMT_TYPE_PAGE_IP6, "connection", conn, NULL); +} + +static void +nmt_page_ip6_init(NmtPageIP6 *ip6) +{} + +static void +edit_routes(NmtNewtButton *button, gpointer user_data) +{ + NMSetting * s_ip6 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new(s_ip6); + nmt_newt_form_run_sync(form); + g_object_unref(form); +} + +static gboolean +ip6_routes_transform_to_description(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + GPtrArray *routes; + char * text; + + routes = g_value_get_boxed(source_value); + if (!routes || !routes->len) + text = g_strdup(_("(No custom routes)")); + else { + text = g_strdup_printf( + g_dngettext(GETTEXT_PACKAGE, "One custom route", "%d custom routes", routes->len), + routes->len); + } + + g_value_take_string(target_value, text); + return TRUE; +} + +static void +nmt_page_ip6_constructed(GObject *object) +{ + NmtPageIP6 * ip6 = NMT_PAGE_IP6(object); + gboolean show_by_default; + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingIPConfig *s_ip6; + NmtNewtWidget * widget, *button; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(ip6)); + s_ip6 = nm_connection_get_setting_ip6_config(conn); + if (!s_ip6) { + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(conn, (NMSetting *) s_ip6); + } + + widget = nmt_newt_popup_new(ip6methods); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + widget, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + if (!g_strcmp0(nm_setting_ip_config_get_method(s_ip6), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + show_by_default = TRUE; + else if (nm_setting_ip_config_get_num_addresses(s_ip6)) + show_by_default = TRUE; + else + show_by_default = FALSE; + + section = nmt_editor_section_new(_("IPv6 CONFIGURATION"), widget, show_by_default); + grid = nmt_editor_section_get_body(section); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP6_WITH_PREFIX); + nm_editor_bind_ip_addresses_with_prefix_to_strv(AF_INET6, + s_ip6, + NM_SETTING_IP_CONFIG_ADDRESSES, + widget, + "strings", + G_BINDING_BIDIRECTIONAL + | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new(25, AF_INET6, FALSE, TRUE); + nm_editor_bind_ip_gateway_to_string(AF_INET6, + s_ip6, + widget, + "text", + "sensitive", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_IP6); + nm_editor_bind_ip_addresses_to_strv(AF_INET6, + s_ip6, + NM_SETTING_IP_CONFIG_DNS, + widget, + "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new(NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_DNS_SEARCH, + widget, + "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Search domains"), widget, NULL); + + widget = g_object_new(NMT_TYPE_NEWT_LABEL, "text", "", "style", NMT_NEWT_LABEL_PLAIN, NULL); + g_object_bind_property_full(s_ip6, + NM_SETTING_IP_CONFIG_ROUTES, + widget, + "text", + G_BINDING_SYNC_CREATE, + ip6_routes_transform_to_description, + NULL, + NULL, + NULL); + button = nmt_newt_button_new(_("Edit...")); + g_signal_connect(button, "clicked", G_CALLBACK(edit_routes), s_ip6); + nmt_editor_grid_append(grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new(_("Never use this network for default route")); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Ignore automatically obtained routes")); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Ignore automatically obtained DNS parameters")); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_newt_checkbox_new(_("Require IPv6 addressing for this connection")); + g_object_bind_property(s_ip6, + NM_SETTING_IP_CONFIG_MAY_FAIL, + widget, + "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL + | G_BINDING_INVERT_BOOLEAN); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(ip6), section); + + G_OBJECT_CLASS(nmt_page_ip6_parent_class)->constructed(object); +} + +static void +nmt_page_ip6_class_init(NmtPageIP6Class *ip6_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ip6_class); + + object_class->constructed = nmt_page_ip6_constructed; +} diff --git a/src/nmtui/nmt-page-ip6.h b/src/nmtui/nmt-page-ip6.h new file mode 100644 index 00000000..cefb36b9 --- /dev/null +++ b/src/nmtui/nmt-page-ip6.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP6_H +#define NMT_PAGE_IP6_H + +#include "nmt-editor-page.h" + +#define NMT_TYPE_PAGE_IP6 (nmt_page_ip6_get_type()) +#define NMT_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6)) +#define NMT_PAGE_IP6_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) +#define NMT_IS_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_IP6)) +#define NMT_IS_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_IP6)) +#define NMT_PAGE_IP6_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP6; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP6Class; + +GType nmt_page_ip6_get_type(void); + +NmtEditorPage *nmt_page_ip6_new(NMConnection *conn); + +#endif /* NMT_PAGE_IP6_H */ diff --git a/src/nmtui/nmt-page-ppp.c b/src/nmtui/nmt-page-ppp.c new file mode 100644 index 00000000..ffa1a8b2 --- /dev/null +++ b/src/nmtui/nmt-page-ppp.c @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ppp + * @short_description: The editor page for PPP configuration + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmt-page-ppp.h" +#include "libnmt-newt/nmt-newt-section.h" +#include "libnmt-newt/nmt-newt-separator.h" + +G_DEFINE_TYPE(NmtPagePpp, nmt_page_ppp, NMT_TYPE_EDITOR_PAGE) + +typedef struct { + guint32 lcp_echo_failure; + guint32 lcp_echo_interval; +} NmtPagePppPrivate; + +#define NMT_PAGE_PPP_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_PPP, NmtPagePppPrivate)) + +NmtEditorPage * +nmt_page_ppp_new(NMConnection *conn) +{ + return g_object_new(NMT_TYPE_PAGE_PPP, "connection", conn, NULL); +} + +static void +nmt_page_ppp_init(NmtPagePpp *ppp) +{} + +static gboolean +transform_lcp_echo_properties_to_checkbox(GBinding * binding, + const GValue *from_value, + GValue * to_value, + gpointer user_data) +{ + NMSettingPpp *s_ppp = NM_SETTING_PPP(g_binding_get_source(binding)); + + if (nm_setting_ppp_get_lcp_echo_interval(s_ppp) != 0 + && nm_setting_ppp_get_lcp_echo_failure(s_ppp) != 0) + g_value_set_boolean(to_value, TRUE); + else + g_value_set_boolean(to_value, FALSE); + + return TRUE; +} + +static gboolean +transform_checkbox_to_lcp_echo_interval(GBinding * binding, + const GValue *from_value, + GValue * to_value, + gpointer user_data) +{ + NmtPagePpp * ppp = user_data; + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE(ppp); + + if (g_value_get_boolean(from_value)) + g_value_set_uint(to_value, priv->lcp_echo_interval); + else + g_value_set_uint(to_value, 0); + + return TRUE; +} + +static gboolean +transform_checkbox_to_lcp_echo_failure(GBinding * binding, + const GValue *from_value, + GValue * to_value, + gpointer user_data) +{ + NmtPagePpp * ppp = user_data; + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE(ppp); + + if (g_value_get_boolean(from_value)) + g_value_set_uint(to_value, priv->lcp_echo_failure); + else + g_value_set_uint(to_value, 0); + + return TRUE; +} + +static void +nmt_page_ppp_constructed(GObject *object) +{ + NmtPagePpp * ppp = NMT_PAGE_PPP(object); + NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE(ppp); + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingPpp * s_ppp; + NmtNewtWidget * widget, *use_mppe; + NmtNewtGrid * auth_grid, *mppe_grid; + NmtNewtSection * auth_section, *mppe_section; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(ppp)); + s_ppp = nm_connection_get_setting_ppp(conn); + if (s_ppp) { + priv->lcp_echo_interval = nm_setting_ppp_get_lcp_echo_interval(s_ppp); + priv->lcp_echo_failure = nm_setting_ppp_get_lcp_echo_failure(s_ppp); + } else { + s_ppp = (NMSettingPpp *) nm_setting_ppp_new(); + nm_connection_add_setting(conn, (NMSetting *) s_ppp); + + priv->lcp_echo_interval = 30; + priv->lcp_echo_failure = 5; + } + + section = nmt_editor_section_new(_("PPP CONFIGURATION"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + /* Auth methods */ + widget = nmt_newt_section_new(FALSE); + auth_section = NMT_NEWT_SECTION(widget); + g_object_set(auth_section, "open", TRUE, NULL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_label_new(_("Allowed authentication methods:")); + nmt_newt_section_set_header(auth_section, widget); + + widget = nmt_newt_grid_new(); + auth_grid = NMT_NEWT_GRID(widget); + nmt_newt_section_set_body(auth_section, widget); + + widget = nmt_newt_checkbox_new(_("EAP")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REFUSE_EAP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(auth_grid, widget, 0, 0); + + widget = nmt_newt_checkbox_new(_("PAP")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REFUSE_PAP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(auth_grid, widget, 0, 1); + + widget = nmt_newt_checkbox_new(_("CHAP")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REFUSE_CHAP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(auth_grid, widget, 0, 2); + + widget = nmt_newt_checkbox_new(_("MSCHAPv2")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REFUSE_MSCHAPV2, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(auth_grid, widget, 0, 3); + + widget = nmt_newt_checkbox_new(_("MSCHAP")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REFUSE_MSCHAP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(auth_grid, widget, 0, 4); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + /* MPPE */ + widget = nmt_newt_section_new(FALSE); + mppe_section = NMT_NEWT_SECTION(widget); + g_object_set(mppe_section, "open", TRUE, NULL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Use point-to-point encryption (MPPE)")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REQUIRE_MPPE, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + use_mppe = widget; + nmt_newt_section_set_header(mppe_section, widget); + + widget = nmt_newt_grid_new(); + mppe_grid = NMT_NEWT_GRID(widget); + nmt_newt_section_set_body(mppe_section, widget); + + widget = nmt_newt_checkbox_new(_("Require 128-bit encryption")); + g_object_bind_property(use_mppe, "active", widget, "sensitive", G_BINDING_SYNC_CREATE); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_REQUIRE_MPPE_128, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(mppe_grid, widget, 0, 0); + + widget = nmt_newt_checkbox_new(_("Use stateful MPPE")); + g_object_bind_property(use_mppe, "active", widget, "sensitive", G_BINDING_SYNC_CREATE); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_MPPE_STATEFUL, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(mppe_grid, widget, 0, 1); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_newt_checkbox_new(_("Allow BSD data compression")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_NOBSDCOMP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Allow Deflate data compression")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_NODEFLATE, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new(_("Use TCP header compression")); + g_object_bind_property(s_ppp, + NM_SETTING_PPP_NO_VJ_COMP, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN + | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_newt_checkbox_new(_("Send PPP echo packets")); + g_object_bind_property_full(s_ppp, + NM_SETTING_PPP_LCP_ECHO_INTERVAL, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + transform_lcp_echo_properties_to_checkbox, + transform_checkbox_to_lcp_echo_interval, + ppp, + NULL); + g_object_bind_property_full(s_ppp, + NM_SETTING_PPP_LCP_ECHO_FAILURE, + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + transform_lcp_echo_properties_to_checkbox, + transform_checkbox_to_lcp_echo_failure, + ppp, + NULL); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(ppp), section); + + G_OBJECT_CLASS(nmt_page_ppp_parent_class)->constructed(object); +} + +static void +nmt_page_ppp_class_init(NmtPagePppClass *ppp_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(ppp_class); + + g_type_class_add_private(object_class, sizeof(NmtPagePppPrivate)); + + object_class->constructed = nmt_page_ppp_constructed; +} diff --git a/src/nmtui/nmt-page-ppp.h b/src/nmtui/nmt-page-ppp.h new file mode 100644 index 00000000..8538c102 --- /dev/null +++ b/src/nmtui/nmt-page-ppp.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_PPP_H +#define NMT_PAGE_PPP_H + +#include "nmt-editor-page.h" + +#define NMT_TYPE_PAGE_PPP (nmt_page_ppp_get_type()) +#define NMT_PAGE_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_PPP, NmtPagePpp)) +#define NMT_PAGE_PPP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_PPP, NmtPagePppClass)) +#define NMT_IS_PAGE_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_PPP)) +#define NMT_IS_PAGE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_PPP)) +#define NMT_PAGE_PPP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_PPP, NmtPagePppClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPagePpp; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPagePppClass; + +GType nmt_page_ppp_get_type(void); + +NmtEditorPage *nmt_page_ppp_new(NMConnection *conn); + +#endif /* NMT_PAGE_PPP_H */ diff --git a/src/nmtui/nmt-page-team-port.c b/src/nmtui/nmt-page-team-port.c new file mode 100644 index 00000000..13bb84f0 --- /dev/null +++ b/src/nmtui/nmt-page-team-port.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team-port + * @short_description: The editor page for Team ports. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-team-port.h" + +G_DEFINE_TYPE(NmtPageTeamPort, nmt_page_team_port, NMT_TYPE_EDITOR_PAGE) + +#define NMT_PAGE_TEAM_PORT_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortPrivate)) + +typedef struct { + NMSettingTeamPort *s_port; + +} NmtPageTeamPortPrivate; + +NmtEditorPage * +nmt_page_team_port_new(NMConnection *conn) +{ + return g_object_new(NMT_TYPE_PAGE_TEAM_PORT, "connection", conn, NULL); +} + +static void +nmt_page_team_port_init(NmtPageTeamPort *team) +{} + +static void +edit_clicked(NmtNewtButton *button, gpointer user_data) +{ + NmtPageTeamPort * team = user_data; + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE(team); + const char * config; + char * new_config; + + config = nm_setting_team_port_get_config(priv->s_port); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string(config); + + if (new_config && !*new_config) + nm_clear_g_free(&new_config); + g_object_set(G_OBJECT(priv->s_port), NM_SETTING_TEAM_PORT_CONFIG, new_config, NULL); + g_free(new_config); +} + +static void +nmt_page_team_port_constructed(GObject *object) +{ + NmtPageTeamPort * team = NMT_PAGE_TEAM_PORT(object); + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE(team); + NmtEditorSection * section; + NmtNewtGrid * grid; + NMSettingTeamPort * s_port; + NmtNewtWidget * widget; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(team)); + s_port = nm_connection_get_setting_team_port(conn); + if (!s_port) { + nm_connection_add_setting(conn, nm_setting_team_port_new()); + s_port = nm_connection_get_setting_team_port(conn); + } + priv->s_port = s_port; + + section = nmt_editor_section_new(_("TEAM PORT"), NULL, TRUE); + + widget = nmt_newt_grid_new(); + nmt_editor_grid_append(nmt_editor_section_get_body(section), NULL, widget, NULL); + + grid = NMT_NEWT_GRID(widget); + + widget = nmt_newt_label_new(_("JSON configuration")); + nmt_newt_grid_add(grid, widget, 0, 2); + + widget = + nmt_newt_textbox_new(NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property(s_port, + NM_SETTING_TEAM_PORT_CONFIG, + widget, + "text", + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(grid, widget, 0, 3); + nmt_newt_widget_set_padding(widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new(_("Edit...")); + g_signal_connect(widget, "clicked", G_CALLBACK(edit_clicked), team); + nmt_newt_grid_add(grid, widget, 0, 4); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(team), section); + + G_OBJECT_CLASS(nmt_page_team_port_parent_class)->constructed(object); +} + +static void +nmt_page_team_port_class_init(NmtPageTeamPortClass *team_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(team_class); + + g_type_class_add_private(team_class, sizeof(NmtPageTeamPortPrivate)); + + object_class->constructed = nmt_page_team_port_constructed; +} diff --git a/src/nmtui/nmt-page-team-port.h b/src/nmtui/nmt-page-team-port.h new file mode 100644 index 00000000..de6e744e --- /dev/null +++ b/src/nmtui/nmt-page-team-port.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_PORT_H +#define NMT_PAGE_TEAM_PORT_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_TEAM_PORT (nmt_page_team_port_get_type()) +#define NMT_PAGE_TEAM_PORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPort)) +#define NMT_PAGE_TEAM_PORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) +#define NMT_IS_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_IS_PAGE_TEAM_PORT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_PAGE_TEAM_PORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageTeamPort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageTeamPortClass; + +GType nmt_page_team_port_get_type(void); + +NmtEditorPage *nmt_page_team_port_new(NMConnection *conn); + +#endif /* NMT_PAGE_TEAM_PORT_H */ diff --git a/src/nmtui/nmt-page-team.c b/src/nmtui/nmt-page-team.c new file mode 100644 index 00000000..3cda1257 --- /dev/null +++ b/src/nmtui/nmt-page-team.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team + * @short_description: The editor page for Team connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-page-team.h" + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE(NmtPageTeam, nmt_page_team, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_TEAM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_TEAM, NmtPageTeamPrivate)) + +typedef struct { + NmtSlaveList *slaves; + + NMSettingTeam *s_team; + GType slave_type; + +} NmtPageTeamPrivate; + +NmtEditorPage * +nmt_page_team_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_TEAM, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_team_init(NmtPageTeam *team) +{ + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(team); + + priv->slave_type = G_TYPE_NONE; +} + +static void +slaves_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NmtPageTeam * team = NMT_PAGE_TEAM(user_data); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(team); + GPtrArray * slaves; + + g_object_get(object, "connections", &slaves, NULL); + if (slaves->len == 0) { + priv->slave_type = G_TYPE_NONE; + } else if (priv->slave_type == G_TYPE_NONE) { + NMConnection *slave = slaves->pdata[0]; + + if (nm_connection_is_type(slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } +} + +static gboolean +team_connection_type_filter(GType connection_type, gpointer user_data) +{ + NmtPageTeam * team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(team); + + if (priv->slave_type != NM_TYPE_SETTING_WIRED) { + if (connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + } + if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND) { + if (connection_type == NM_TYPE_SETTING_WIRED || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN) + return TRUE; + } + + return FALSE; +} + +static void +edit_clicked(NmtNewtButton *button, gpointer user_data) +{ + NmtPageTeam * team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(team); + const char * config; + char * new_config; + + config = nm_setting_team_get_config(priv->s_team); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string(config); + + if (new_config && !*new_config) + nm_clear_g_free(&new_config); + g_object_set(G_OBJECT(priv->s_team), NM_SETTING_TEAM_CONFIG, new_config, NULL); + g_free(new_config); +} + +static void +nmt_page_team_constructed(GObject *object) +{ + NmtPageTeam * team = NMT_PAGE_TEAM(object); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(team); + NmtEditorSection * section; + NmtNewtGrid * grid; + NMSettingTeam * s_team; + NmtNewtWidget * widget; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(team)); + s_team = nm_connection_get_setting_team(conn); + if (!s_team) { + nm_connection_add_setting(conn, nm_setting_team_new()); + s_team = nm_connection_get_setting_team(conn); + } + priv->s_team = s_team; + + section = nmt_editor_section_new(_("TEAM"), NULL, TRUE); + + widget = nmt_newt_grid_new(); + nmt_editor_grid_append(nmt_editor_section_get_body(section), NULL, widget, NULL); + + grid = NMT_NEWT_GRID(widget); + + widget = nmt_newt_label_new(_("Slaves")); + nmt_newt_grid_add(grid, widget, 0, 0); + + widget = nmt_slave_list_new(conn, team_connection_type_filter, team); + g_signal_connect(widget, "notify::connections", G_CALLBACK(slaves_changed), team); + nmt_newt_grid_add(grid, widget, 0, 1); + nmt_newt_widget_set_padding(widget, 0, 0, 0, 1); + priv->slaves = NMT_SLAVE_LIST(widget); + slaves_changed(G_OBJECT(priv->slaves), NULL, team); + + widget = nmt_newt_label_new(_("JSON configuration")); + nmt_newt_grid_add(grid, widget, 0, 2); + + widget = + nmt_newt_textbox_new(NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property(s_team, NM_SETTING_TEAM_CONFIG, widget, "text", G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(grid, widget, 0, 3); + nmt_newt_widget_set_padding(widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new(_("Edit...")); + g_signal_connect(widget, "clicked", G_CALLBACK(edit_clicked), team); + nmt_newt_grid_add(grid, widget, 0, 4); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(team), section); + + G_OBJECT_CLASS(nmt_page_team_parent_class)->constructed(object); +} + +static void +nmt_page_team_saved(NmtEditorPage *editor_page) +{ + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE(editor_page); + + nmt_edit_connection_list_recommit(NMT_EDIT_CONNECTION_LIST(priv->slaves)); +} + +static void +nmt_page_team_class_init(NmtPageTeamClass *team_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(team_class); + NmtEditorPageClass *editor_page_class = NMT_EDITOR_PAGE_CLASS(team_class); + + g_type_class_add_private(team_class, sizeof(NmtPageTeamPrivate)); + + object_class->constructed = nmt_page_team_constructed; + editor_page_class->saved = nmt_page_team_saved; +} diff --git a/src/nmtui/nmt-page-team.h b/src/nmtui/nmt-page-team.h new file mode 100644 index 00000000..5eb792ae --- /dev/null +++ b/src/nmtui/nmt-page-team.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_H +#define NMT_PAGE_TEAM_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_TEAM (nmt_page_team_get_type()) +#define NMT_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeam)) +#define NMT_PAGE_TEAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) +#define NMT_IS_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_TEAM)) +#define NMT_IS_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_TEAM)) +#define NMT_PAGE_TEAM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageTeam; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageTeamClass; + +GType nmt_page_team_get_type(void); + +NmtEditorPage *nmt_page_team_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_TEAM_H */ diff --git a/src/nmtui/nmt-page-vlan.c b/src/nmtui/nmt-page-vlan.c new file mode 100644 index 00000000..299a7267 --- /dev/null +++ b/src/nmtui/nmt-page-vlan.c @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-vlan + * @short_description: The editor page for VLAN connections + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nm-editor-bindings.h" + +#include <linux/if_ether.h> + +#include "nmt-page-vlan.h" +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE(NmtPageVlan, nmt_page_vlan, NMT_TYPE_EDITOR_PAGE_DEVICE) + +NmtEditorPage * +nmt_page_vlan_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_VLAN, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_vlan_init(NmtPageVlan *vlan) +{} + +static gboolean +vlan_device_filter(NmtDeviceEntry *deventry, NMDevice *device, gpointer user_data) +{ + // FIXME + return NM_IS_DEVICE_ETHERNET(device); +} + +static void +nmt_page_vlan_constructed(GObject *object) +{ + NmtPageVlan * vlan = NMT_PAGE_VLAN(object); + NmtEditorSection *section; + NmtEditorGrid * grid; + NMSettingWired * s_wired; + NMSettingVlan * s_vlan; + NmtNewtWidget * widget, *parent, *id_entry; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(vlan)); + s_vlan = nm_connection_get_setting_vlan(conn); + if (!s_vlan) { + nm_connection_add_setting(conn, nm_setting_vlan_new()); + s_vlan = nm_connection_get_setting_vlan(conn); + } + s_wired = nm_connection_get_setting_wired(conn); + if (!s_wired) { + nm_connection_add_setting(conn, nm_setting_wired_new()); + s_wired = nm_connection_get_setting_wired(conn); + } + + section = nmt_editor_section_new(_("VLAN"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + nm_editor_bind_vlan_name(s_vlan, nm_connection_get_setting_connection(conn)); + + widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE); + nmt_device_entry_set_device_filter(NMT_DEVICE_ENTRY(widget), vlan_device_filter, vlan); + g_object_bind_property(s_vlan, + NM_SETTING_VLAN_PARENT, + widget, + "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, NULL, widget, NULL); + + widget = id_entry = nmt_newt_entry_numeric_new(8, 0, 4094); + g_object_bind_property(s_vlan, + NM_SETTING_VLAN_ID, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("VLAN id"), widget, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new(); + g_object_bind_property(s_wired, + NM_SETTING_WIRED_MTU, + widget, + "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(vlan), section); + + G_OBJECT_CLASS(nmt_page_vlan_parent_class)->constructed(object); +} + +static void +nmt_page_vlan_class_init(NmtPageVlanClass *vlan_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(vlan_class); + + /* virtual methods */ + object_class->constructed = nmt_page_vlan_constructed; +} diff --git a/src/nmtui/nmt-page-vlan.h b/src/nmtui/nmt-page-vlan.h new file mode 100644 index 00000000..deebda00 --- /dev/null +++ b/src/nmtui/nmt-page-vlan.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_VLAN_H +#define NMT_PAGE_VLAN_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_VLAN (nmt_page_vlan_get_type()) +#define NMT_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlan)) +#define NMT_PAGE_VLAN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) +#define NMT_IS_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_VLAN)) +#define NMT_IS_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_VLAN)) +#define NMT_PAGE_VLAN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageVlan; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageVlanClass; + +GType nmt_page_vlan_get_type(void); + +NmtEditorPage *nmt_page_vlan_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_VLAN_H */ diff --git a/src/nmtui/nmt-page-wifi.c b/src/nmtui/nmt-page-wifi.c new file mode 100644 index 00000000..247a0e4e --- /dev/null +++ b/src/nmtui/nmt-page-wifi.c @@ -0,0 +1,409 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-wifi + * @short_description: The editor page for Wi-Fi connections + * + * #NmtPageWifi is the editor page for Wi-Fi connections, which + * includes both #NMSettingWireless and #NMSettingWirelessSecurity + * properties. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> +#include <linux/if_ether.h> + +#include "nmt-page-wifi.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" +#include "nmt-password-fields.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE(NmtPageWifi, nmt_page_wifi, NMT_TYPE_EDITOR_PAGE_DEVICE) + +#define NMT_PAGE_WIFI_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PAGE_WIFI, NmtPageWifiPrivate)) + +typedef struct { + NMSettingWirelessSecurity *s_wsec; + +} NmtPageWifiPrivate; + +NmtEditorPage * +nmt_page_wifi_new(NMConnection *conn, NmtDeviceEntry *deventry) +{ + return g_object_new(NMT_TYPE_PAGE_WIFI, "connection", conn, "device-entry", deventry, NULL); +} + +static void +nmt_page_wifi_init(NmtPageWifi *wifi) +{} + +static NmtNewtPopupEntry wifi_mode[] = {{NC_("Wi-Fi", "Client"), NM_SETTING_WIRELESS_MODE_INFRA}, + {N_("Access Point"), NM_SETTING_WIRELESS_MODE_AP}, + {N_("Ad-Hoc Network"), NM_SETTING_WIRELESS_MODE_ADHOC}, + {NULL, NULL}}; + +static NmtNewtPopupEntry wifi_band[] = {{NC_("Wi-Fi", "Automatic"), NULL}, + /* 802.11a Wi-Fi network */ + {N_("A (5 GHz)"), "a"}, + /* 802.11b / 802.11g Wi-Fi network */ + {N_("B/G (2.4 GHz)"), "bg"}, + {NULL, NULL}}; + +static NmtNewtPopupEntry wifi_security[] = {{NC_("Wi-Fi security", "None"), "none"}, + {N_("WPA & WPA2 Personal"), "wpa-personal"}, + {N_("WPA3 Personal"), "wpa3-personal"}, + {N_("WPA & WPA2 Enterprise"), "wpa-enterprise"}, + {N_("WEP 40/128-bit Key (Hex or ASCII)"), "wep-key"}, + {N_("WEP 128-bit Passphrase"), "wep-passphrase"}, + {N_("Dynamic WEP (802.1x)"), "dynamic-wep"}, + {N_("LEAP"), "leap"}, + {N_("Enhanced Open (OWE)"), "owe"}, + {NULL, NULL}}; + +static NmtNewtPopupEntry wep_index[] = {{NC_("WEP key index", "1 (Default)"), "1"}, + {NC_("WEP key index", "2"), "2"}, + {NC_("WEP key index", "3"), "3"}, + {NC_("WEP key index", "4"), "4"}, + {NULL, NULL}}; + +static NmtNewtPopupEntry wep_auth[] = {{N_("Open System"), "open"}, + {N_("Shared Key"), "shared"}, + {NULL, NULL}}; + +static gboolean +mode_transform_to_band_visibility(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + if (!g_strcmp0(g_value_get_string(source_value), NM_SETTING_WIRELESS_MODE_INFRA)) + g_value_set_boolean(target_value, FALSE); + else + g_value_set_boolean(target_value, TRUE); + return TRUE; +} + +static gboolean +band_transform_to_channel_visibility(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + g_value_set_boolean(target_value, g_value_get_string(source_value) != NULL); + return TRUE; +} + +static gboolean +ssid_transform_to_entry(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + GBytes *ssid; + char * utf8; + + ssid = g_value_get_boxed(source_value); + if (ssid) + utf8 = nm_utils_ssid_to_utf8(g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); + else + utf8 = g_strdup(""); + g_value_take_string(target_value, utf8); + return TRUE; +} + +static gboolean +ssid_transform_from_entry(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + NMSettingWireless *s_wireless = user_data; + const char * text; + GBytes * old_ssid, *ssid; + char * utf8; + + text = g_value_get_string(source_value); + + old_ssid = nm_setting_wireless_get_ssid(s_wireless); + if (old_ssid) + utf8 = nm_utils_ssid_to_utf8(g_bytes_get_data(old_ssid, NULL), g_bytes_get_size(old_ssid)); + else + utf8 = g_strdup(""); + + if (!g_strcmp0(text, utf8)) { + g_free(utf8); + return FALSE; + } + g_free(utf8); + + ssid = g_bytes_new(text, strlen(text)); + g_value_take_boxed(target_value, ssid); + return TRUE; +} + +static void +nmt_page_wifi_constructed(GObject *object) +{ + NmtPageWifiPrivate * priv = NMT_PAGE_WIFI_GET_PRIVATE(object); + NmtPageWifi * wifi = NMT_PAGE_WIFI(object); + NmtDeviceEntry * deventry; + NmtEditorSection * section; + NmtEditorGrid * grid; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NmtNewtWidget * widget, *hbox, *subgrid; + NmtNewtWidget * mode, *band, *security, *entry; + NmtNewtStack * stack; + NMConnection * conn; + + conn = nmt_editor_page_get_connection(NMT_EDITOR_PAGE(wifi)); + s_wireless = nm_connection_get_setting_wireless(conn); + if (!s_wireless) { + nm_connection_add_setting(conn, nm_setting_wireless_new()); + s_wireless = nm_connection_get_setting_wireless(conn); + } + + s_wsec = nm_connection_get_setting_wireless_security(conn); + if (!s_wsec) { + /* It makes things simpler if we always have a + * NMSettingWirelessSecurity; we'll hold a ref on one, and add + * it to and remove it from the connection as needed. + */ + s_wsec = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new()); + } + priv->s_wsec = g_object_ref_sink(s_wsec); + + deventry = nmt_editor_page_device_get_device_entry(NMT_EDITOR_PAGE_DEVICE(object)); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_MAC_ADDRESS, + deventry, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + section = nmt_editor_section_new(_("WI-FI"), NULL, TRUE); + grid = nmt_editor_section_get_body(section); + + widget = nmt_newt_entry_new(40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property_full(s_wireless, + NM_SETTING_WIRELESS_SSID, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + ssid_transform_to_entry, + ssid_transform_from_entry, + s_wireless, + NULL); + nmt_editor_grid_append(grid, _("SSID"), widget, NULL); + + widget = nmt_newt_popup_new(wifi_mode); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_MODE, + widget, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Mode"), widget, NULL); + mode = widget; + + hbox = nmt_newt_grid_new(); + widget = nmt_newt_popup_new(wifi_band); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_BAND, + widget, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(NMT_NEWT_GRID(hbox), widget, 0, 0); + band = widget; + + widget = nmt_newt_entry_numeric_new(10, 0, 255); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_CHANNEL, + widget, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add(NMT_NEWT_GRID(hbox), widget, 1, 0); + nmt_newt_widget_set_padding(widget, 1, 0, 0, 0); + + g_object_bind_property_full(band, + "active-id", + widget, + "visible", + G_BINDING_SYNC_CREATE, + band_transform_to_channel_visibility, + NULL, + NULL, + NULL); + g_object_bind_property_full(mode, + "active-id", + hbox, + "visible", + G_BINDING_SYNC_CREATE, + mode_transform_to_band_visibility, + NULL, + NULL, + NULL); + nmt_editor_grid_append(grid, _("Channel"), hbox, NULL); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_newt_popup_new(wifi_security); + nmt_editor_grid_append(grid, _("Security"), widget, NULL); + security = widget; + + widget = nmt_newt_stack_new(); + stack = NMT_NEWT_STACK(widget); + + /* none */ + subgrid = nmt_editor_grid_new(); + nmt_newt_stack_add(stack, "none", subgrid); + + /* wpa-personal */ + subgrid = nmt_editor_grid_new(); + widget = nmt_password_fields_new(40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property(s_wsec, + NM_SETTING_WIRELESS_SECURITY_PSK, + widget, + "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Password"), widget, NULL); + nmt_newt_stack_add(stack, "wpa-personal", subgrid); + nmt_newt_stack_add(stack, "wpa3-personal", subgrid); + + /* "wpa-enterprise" */ + // FIXME + widget = nmt_newt_label_new(_("(No support for wpa-enterprise yet...)")); + nmt_newt_stack_add(stack, "wpa-enterprise", widget); + + /* wep-key */ + subgrid = nmt_editor_grid_new(); + + widget = entry = nmt_password_fields_new(40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Key"), widget, NULL); + + widget = nmt_newt_popup_new(wep_index); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key(s_wsec, + entry, + "password", + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new(wep_auth); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add(stack, "wep-key", subgrid); + + /* wep-passphrase */ + subgrid = nmt_editor_grid_new(); + + widget = entry = nmt_password_fields_new(40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Password"), widget, NULL); + + widget = nmt_newt_popup_new(wep_index); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key(s_wsec, + entry, + "password", + widget, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new(wep_auth); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add(stack, "wep-passphrase", subgrid); + + /* "dynamic-wep" */ + // FIXME + widget = nmt_newt_label_new(_("(No support for dynamic-wep yet...)")); + nmt_newt_stack_add(stack, "dynamic-wep", widget); + + /* leap */ + subgrid = nmt_editor_grid_new(); + + widget = nmt_newt_entry_new(40, NMT_NEWT_ENTRY_NONEMPTY); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Username"), widget, NULL); + g_object_bind_property(s_wsec, + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, + widget, + "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + widget = nmt_password_fields_new(40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property(s_wsec, + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + widget, + "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Password"), widget, NULL); + + nmt_newt_stack_add(stack, "leap", subgrid); + + nmt_editor_grid_append(grid, NULL, NMT_NEWT_WIDGET(stack), NULL); + g_object_bind_property(security, "active-id", stack, "active-id", G_BINDING_SYNC_CREATE); + nm_editor_bind_wireless_security_method(conn, + s_wsec, + security, + "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_editor_grid_append(grid, NULL, nmt_newt_separator_new(), NULL); + + widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_MAC); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_BSSID, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("BSSID"), widget, NULL); + + widget = nmt_mac_entry_new(40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + widget, + "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new(); + g_object_bind_property(s_wireless, + NM_SETTING_WIRELESS_MTU, + widget, + "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_grid_append(grid, _("MTU"), widget, NULL); + + nmt_editor_page_add_section(NMT_EDITOR_PAGE(wifi), section); + + G_OBJECT_CLASS(nmt_page_wifi_parent_class)->constructed(object); +} + +static void +nmt_page_wifi_finalize(GObject *object) +{ + NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE(object); + + g_clear_object(&priv->s_wsec); + + G_OBJECT_CLASS(nmt_page_wifi_parent_class)->finalize(object); +} + +static void +nmt_page_wifi_class_init(NmtPageWifiClass *wifi_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(wifi_class); + + g_type_class_add_private(wifi_class, sizeof(NmtPageWifiPrivate)); + + object_class->constructed = nmt_page_wifi_constructed; + object_class->finalize = nmt_page_wifi_finalize; +} diff --git a/src/nmtui/nmt-page-wifi.h b/src/nmtui/nmt-page-wifi.h new file mode 100644 index 00000000..c01fdd1d --- /dev/null +++ b/src/nmtui/nmt-page-wifi.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_WIFI_H +#define NMT_PAGE_WIFI_H + +#include "nmt-editor-page-device.h" + +#define NMT_TYPE_PAGE_WIFI (nmt_page_wifi_get_type()) +#define NMT_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifi)) +#define NMT_PAGE_WIFI_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) +#define NMT_IS_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PAGE_WIFI)) +#define NMT_IS_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PAGE_WIFI)) +#define NMT_PAGE_WIFI_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) + +typedef struct { + NmtEditorPageDevice parent; + +} NmtPageWifi; + +typedef struct { + NmtEditorPageDeviceClass parent; + +} NmtPageWifiClass; + +GType nmt_page_wifi_get_type(void); + +NmtEditorPage *nmt_page_wifi_new(NMConnection *conn, NmtDeviceEntry *deventry); + +#endif /* NMT_PAGE_WIFI_H */ diff --git a/src/nmtui/nmt-password-dialog.c b/src/nmtui/nmt-password-dialog.c new file mode 100644 index 00000000..99b9b3f7 --- /dev/null +++ b/src/nmtui/nmt-password-dialog.c @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-dialog + * @short_description: A password dialog + * + * #NmtPasswordDialog is the password dialog used to get connection + * secrets when activating a connection. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-password-dialog.h" +#include "libnmc-base/nm-secret-agent-simple.h" +#include "nmtui.h" + +G_DEFINE_TYPE(NmtPasswordDialog, nmt_password_dialog, NMT_TYPE_NEWT_FORM) + +#define NMT_PASSWORD_DIALOG_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogPrivate)) + +typedef struct { + char * request_id; + char * prompt; + GPtrArray *secrets; + GPtrArray *entries; + + NmtNewtWidget *ok, *cancel; + NmtNewtWidget *last_entry; + NmtNewtWidget *secret_grid; + + gboolean succeeded; +} NmtPasswordDialogPrivate; + +enum { + PROP_0, + PROP_REQUEST_ID, + PROP_PROMPT, + PROP_SECRETS, + + LAST_PROP +}; + +/** + * nmt_password_dialog_new: + * @request_id: the request ID from the #NMSecretAgentSimple + * @title: the dialog title + * @prompt: the prompt text to display + * @secrets: (element-type #NMSecretAgentSimpleSecret): the secrets requested + * + * Creates a new #NmtPasswordDialog to request passwords from + * the user. + * + * Returns: a new #NmtPasswordDialog. + */ +NmtNewtForm * +nmt_password_dialog_new(const char *request_id, + const char *title, + const char *prompt, + GPtrArray * secrets) +{ + return g_object_new(NMT_TYPE_PASSWORD_DIALOG, + "request-id", + request_id, + "title", + title, + "prompt", + prompt, + "secrets", + secrets, + "escape-exits", + TRUE, + NULL); +} + +static void +nmt_password_dialog_init(NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + + priv->entries = g_ptr_array_new(); +} + +static void +maybe_save_input_and_exit(NmtNewtWidget *widget, gpointer dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + int i; + + /* This gets invoked when the user types Return in the final entry, + * but the form may not be fully valid in that case. + */ + if (!nmt_newt_widget_get_valid(priv->secret_grid)) + return; + + priv->succeeded = TRUE; + + for (i = 0; i < priv->secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = priv->secrets->pdata[i]; + + g_free(secret->value); + g_object_get(priv->entries->pdata[i], "text", &secret->value, NULL); + } + + nmt_newt_form_quit(nmt_newt_widget_get_form(widget)); +} + +static void +nmt_password_dialog_constructed(GObject *object) +{ + NmtPasswordDialog * dialog = NMT_PASSWORD_DIALOG(object); + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + NmtNewtWidget * widget; + NmtNewtGrid * grid, *secret_grid; + NmtNewtButtonBox * bbox; + int i; + + widget = nmt_newt_grid_new(); + nmt_newt_form_set_content(NMT_NEWT_FORM(dialog), widget); + grid = NMT_NEWT_GRID(widget); + + widget = nmt_newt_textbox_new(0, 60); + nmt_newt_textbox_set_text(NMT_NEWT_TEXTBOX(widget), priv->prompt); + nmt_newt_grid_add(grid, widget, 0, 0); + + widget = nmt_newt_grid_new(); + nmt_newt_grid_add(grid, widget, 0, 1); + nmt_newt_widget_set_padding(widget, 0, 1, 0, 1); + priv->secret_grid = widget; + secret_grid = NMT_NEWT_GRID(widget); + + for (i = 0; i < priv->secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = priv->secrets->pdata[i]; + NmtNewtEntryFlags flags; + + widget = nmt_newt_label_new(secret->pretty_name); + nmt_newt_grid_add(secret_grid, widget, 0, i); + nmt_newt_widget_set_padding(widget, 4, 0, 1, 0); + + flags = NMT_NEWT_ENTRY_NONEMPTY; + if (secret->is_secret) + flags |= NMT_NEWT_ENTRY_PASSWORD; + widget = nmt_newt_entry_new(30, flags); + if (secret->value) + nmt_newt_entry_set_text(NMT_NEWT_ENTRY(widget), secret->value); + nmt_newt_grid_add(secret_grid, widget, 1, i); + g_ptr_array_add(priv->entries, widget); + + if (i == priv->secrets->len - 1) { + priv->last_entry = widget; + g_signal_connect(widget, "activated", G_CALLBACK(maybe_save_input_and_exit), dialog); + } + } + + widget = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add(grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX(widget); + + priv->cancel = nmt_newt_button_box_add_end(NMT_NEWT_BUTTON_BOX(bbox), _("Cancel")); + nmt_newt_widget_set_exit_on_activate(priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end(NMT_NEWT_BUTTON_BOX(bbox), _("OK")); + g_signal_connect(priv->ok, "activated", G_CALLBACK(maybe_save_input_and_exit), dialog); + g_object_bind_property(priv->secret_grid, + "valid", + priv->ok, + "sensitive", + G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS(nmt_password_dialog_parent_class)->constructed(object); +} + +static void +nmt_password_dialog_finalize(GObject *object) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(object); + + g_free(priv->request_id); + g_free(priv->prompt); + nm_clear_pointer(&priv->entries, g_ptr_array_unref); + nm_clear_pointer(&priv->secrets, g_ptr_array_unref); + + G_OBJECT_CLASS(nmt_password_dialog_parent_class)->finalize(object); +} + +static void +nmt_password_dialog_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_REQUEST_ID: + priv->request_id = g_value_dup_string(value); + break; + case PROP_PROMPT: + priv->prompt = g_value_dup_string(value); + break; + case PROP_SECRETS: + priv->secrets = g_value_dup_boxed(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_REQUEST_ID: + g_value_set_string(value, priv->request_id); + break; + case PROP_PROMPT: + g_value_set_string(value, priv->prompt); + break; + case PROP_SECRETS: + g_value_set_boxed(value, priv->secrets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_class_init(NmtPasswordDialogClass *dialog_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(dialog_class); + + g_type_class_add_private(dialog_class, sizeof(NmtPasswordDialogPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_dialog_constructed; + object_class->set_property = nmt_password_dialog_set_property; + object_class->get_property = nmt_password_dialog_get_property; + object_class->finalize = nmt_password_dialog_finalize; + + /** + * NmtPasswordDialog:request-id: + * + * The request ID from the #NMSecretAgentSimple + */ + g_object_class_install_property( + object_class, + PROP_REQUEST_ID, + g_param_spec_string("request-id", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:prompt: + * + * The prompt text. + */ + g_object_class_install_property( + object_class, + PROP_PROMPT, + g_param_spec_string("prompt", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:secrets: + * + * The array of request secrets + * + * Element-Type: #NMSecretAgentSimpleSecret. + */ + g_object_class_install_property( + object_class, + PROP_SECRETS, + g_param_spec_boxed("secrets", + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +/** + * nmt_password_dialog_succeeded: + * @dialog: the #NmtPasswordDialog + * + * After the dialog has exited, returns %TRUE if the user clicked + * "OK", %FALSE if "Cancel". + * + * Returns: whether the dialog succeeded. + */ +gboolean +nmt_password_dialog_succeeded(NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + + return priv->succeeded; +} + +/** + * nmt_password_dialog_get_request_id: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's request ID. + * + * Returns: the dialog's request ID. + */ +const char * +nmt_password_dialog_get_request_id(NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + + return priv->request_id; +} + +/** + * nmt_password_dialog_get_secrets: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's secrets array. + * + * Returns: (transfer none): the dialog's secrets array. + */ +GPtrArray * +nmt_password_dialog_get_secrets(NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE(dialog); + + return priv->secrets; +} diff --git a/src/nmtui/nmt-password-dialog.h b/src/nmtui/nmt-password-dialog.h new file mode 100644 index 00000000..77e8f292 --- /dev/null +++ b/src/nmtui/nmt-password-dialog.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_DIALOG_H +#define NMT_PASSWORD_DIALOG_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_PASSWORD_DIALOG (nmt_password_dialog_get_type()) +#define NMT_PASSWORD_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog)) +#define NMT_PASSWORD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) +#define NMT_IS_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_IS_PASSWORD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_PASSWORD_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtPasswordDialog; + +typedef struct { + NmtNewtFormClass parent; + +} NmtPasswordDialogClass; + +GType nmt_password_dialog_get_type(void); + +NmtNewtForm *nmt_password_dialog_new(const char *request_id, + const char *title, + const char *prompt, + GPtrArray * secrets); + +gboolean nmt_password_dialog_succeeded(NmtPasswordDialog *dialog); + +const char *nmt_password_dialog_get_request_id(NmtPasswordDialog *dialog); +GPtrArray * nmt_password_dialog_get_secrets(NmtPasswordDialog *dialog); + +#endif /* NMT_PASSWORD_DIALOG_H */ diff --git a/src/nmtui/nmt-password-fields.c b/src/nmtui/nmt-password-fields.c new file mode 100644 index 00000000..45073c7e --- /dev/null +++ b/src/nmtui/nmt-password-fields.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-fields + * @short_description: Widgets for password-related data + * + * #NmtPasswordFields provides an entry to type a password into, followed + * optionally by an "Ask for this password every time" checkbox and/or a + * "Show password" checkbox that toggles whether the password is visible. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-password-fields.h" + +G_DEFINE_TYPE(NmtPasswordFields, nmt_password_fields, NMT_TYPE_NEWT_GRID) + +#define NMT_PASSWORD_FIELDS_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsPrivate)) + +typedef struct { + NmtPasswordFieldsExtras extras; + + NmtNewtEntry * entry; + NmtNewtCheckbox *always_ask; + NmtNewtCheckbox *show_password; + + char *init_password; + +} NmtPasswordFieldsPrivate; + +enum { + PROP_0, + PROP_WIDTH, + PROP_EXTRAS, + PROP_PASSWORD, + PROP_ALWAYS_ASK, + PROP_SHOW_PASSWORD, + + LAST_PROP +}; + +/** + * NmtPasswordFieldsExtras: + * @NMT_PASSWORD_FIELDS_ALWAYS_ASK: show an "Always ask" checkbox + * @NMT_PASSWORD_FIELDS_SHOW_PASSWORD: show a "Show password" checkbox + * + * Extra widgets to include in an #NmtPasswordFields + */ + +/** + * nmt_password_fields_new: + * @width: width in characters of the password entry + * @extras: extra widgets to show + * + * Creates a new #NmtPasswordFields + * + * Returns: a new #NmtPasswordFields + */ +NmtNewtWidget * +nmt_password_fields_new(int width, NmtPasswordFieldsExtras extras) +{ + return g_object_new(NMT_TYPE_PASSWORD_FIELDS, "width", width, "extras", extras, NULL); +} + +static void +nmt_password_fields_set_password(NmtPasswordFields *fields, const char *password) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(fields); + + if (!g_strcmp0(password, nmt_newt_entry_get_text(priv->entry))) + return; + + nmt_newt_entry_set_text(priv->entry, password); + g_object_notify(G_OBJECT(fields), "password"); +} + +static const char * +nmt_password_fields_get_password(NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(fields); + + return nmt_newt_entry_get_text(priv->entry); +} + +static void +always_ask_changed(GObject *object, GParamSpec *pspec, gpointer fields) +{ + g_object_notify(fields, "always-ask"); +} + +static void +show_password_changed(GObject *object, GParamSpec *pspec, gpointer fields) +{ + g_object_notify(fields, "show-password"); +} + +static void +nmt_password_fields_init(NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(fields); + + priv->entry = NMT_NEWT_ENTRY(nmt_newt_entry_new(-1, 0)); + priv->always_ask = + NMT_NEWT_CHECKBOX(nmt_newt_checkbox_new(_("Ask for this password every time"))); + priv->show_password = NMT_NEWT_CHECKBOX(nmt_newt_checkbox_new(_("Show password"))); +} + +static void +nmt_password_fields_constructed(GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(object); + NmtNewtGrid * grid = NMT_NEWT_GRID(object); + + nmt_newt_grid_add(grid, NMT_NEWT_WIDGET(priv->entry), 0, 0); + + if (priv->extras & NMT_PASSWORD_FIELDS_ALWAYS_ASK) { + nmt_newt_grid_add(grid, NMT_NEWT_WIDGET(priv->always_ask), 0, 1); + g_signal_connect(priv->always_ask, + "notify::active", + G_CALLBACK(always_ask_changed), + object); + } else + g_clear_object(&priv->always_ask); + + if (priv->extras & NMT_PASSWORD_FIELDS_SHOW_PASSWORD) { + nmt_newt_grid_add(grid, NMT_NEWT_WIDGET(priv->show_password), 0, 2); + g_signal_connect(priv->show_password, + "notify::active", + G_CALLBACK(show_password_changed), + object); + g_object_bind_property(priv->show_password, + "active", + priv->entry, + "password", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } else + g_clear_object(&priv->show_password); + + g_object_bind_property(priv->entry, + "text", + object, + "password", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS(nmt_password_fields_parent_class)->constructed(object); +} + +static void +nmt_password_fields_finalize(GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(object); + + if (priv->always_ask) { + g_signal_handlers_disconnect_by_func(priv->always_ask, + G_CALLBACK(always_ask_changed), + object); + } + if (priv->show_password) { + g_signal_handlers_disconnect_by_func(priv->show_password, + G_CALLBACK(show_password_changed), + object); + } + + G_OBJECT_CLASS(nmt_password_fields_parent_class)->finalize(object); +} + +static void +nmt_password_fields_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtPasswordFields * fields = NMT_PASSWORD_FIELDS(object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(fields); + + switch (prop_id) { + case PROP_WIDTH: + nmt_newt_entry_set_width(priv->entry, g_value_get_int(value)); + break; + case PROP_EXTRAS: + priv->extras = g_value_get_uint(value); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(fields)); + break; + case PROP_PASSWORD: + nmt_password_fields_set_password(fields, g_value_get_string(value)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + nmt_newt_checkbox_set_active(priv->always_ask, g_value_get_boolean(value)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + nmt_newt_checkbox_set_active(priv->show_password, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtPasswordFields * entry = NMT_PASSWORD_FIELDS(object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE(entry); + + switch (prop_id) { + case PROP_WIDTH: + g_value_set_int(value, nmt_newt_entry_get_width(priv->entry)); + break; + case PROP_EXTRAS: + g_value_set_uint(value, priv->extras); + break; + case PROP_PASSWORD: + g_value_set_string(value, nmt_password_fields_get_password(entry)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + g_value_set_boolean(value, nmt_newt_checkbox_get_active(priv->always_ask)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + g_value_set_boolean(value, nmt_newt_checkbox_get_active(priv->show_password)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_class_init(NmtPasswordFieldsClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtPasswordFieldsPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_fields_constructed; + object_class->set_property = nmt_password_fields_set_property; + object_class->get_property = nmt_password_fields_get_property; + object_class->finalize = nmt_password_fields_finalize; + + /** + * NmtPasswordFields:width: + * + * The width in characters of the password entry + */ + g_object_class_install_property( + object_class, + PROP_WIDTH, + g_param_spec_int("width", "", "", -1, 80, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:extras: + * + * The extra widgets to show + */ + g_object_class_install_property( + object_class, + PROP_EXTRAS, + g_param_spec_uint("extras", + "", + "", + 0, + 0xFFFF, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:password: + * + * The entered password. + */ + g_object_class_install_property( + object_class, + PROP_PASSWORD, + g_param_spec_string("password", "", "", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:always-ask: + * + * The current state of the "Always ask" checkbox. + */ + g_object_class_install_property( + object_class, + PROP_ALWAYS_ASK, + g_param_spec_boolean("always-ask", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:show-password: + * + * The current state of the "Show password" checkbox. + */ + g_object_class_install_property( + object_class, + PROP_SHOW_PASSWORD, + g_param_spec_boolean("show-password", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-password-fields.h b/src/nmtui/nmt-password-fields.h new file mode 100644 index 00000000..3e3d8145 --- /dev/null +++ b/src/nmtui/nmt-password-fields.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_FIELDS_H +#define NMT_PASSWORD_FIELDS_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_PASSWORD_FIELDS (nmt_password_fields_get_type()) +#define NMT_PASSWORD_FIELDS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields)) +#define NMT_PASSWORD_FIELDS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) +#define NMT_IS_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_IS_PASSWORD_FIELDS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_PASSWORD_FIELDS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtPasswordFields; + +typedef struct { + NmtNewtGridClass parent; + +} NmtPasswordFieldsClass; + +GType nmt_password_fields_get_type(void); + +typedef enum { + NMT_PASSWORD_FIELDS_ALWAYS_ASK = (1 << 0), + NMT_PASSWORD_FIELDS_SHOW_PASSWORD = (1 << 1), +} NmtPasswordFieldsExtras; + +NmtNewtWidget *nmt_password_fields_new(int width, NmtPasswordFieldsExtras extras); + +#endif /* NMT_PASSWORD_FIELDS_H */ diff --git a/src/nmtui/nmt-route-editor.c b/src/nmtui/nmt-route-editor.c new file mode 100644 index 00000000..ffccf38d --- /dev/null +++ b/src/nmtui/nmt-route-editor.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-editor + * @short_description: Route editing dialog + * + * #NmtRouteEditor implements a form for editing IPv4 or IPv6 routes. + * This was implemented as a separate dialog because it seemed too + * wide to fit into the main window. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-route-editor.h" +#include "nmt-route-table.h" + +G_DEFINE_TYPE(NmtRouteEditor, nmt_route_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_ROUTE_EDITOR_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorPrivate)) + +typedef struct { + NMSetting *orig_setting; + NMSetting *edit_setting; + +} NmtRouteEditorPrivate; + +enum { + PROP_0, + PROP_SETTING, + + LAST_PROP +}; + +/** + * nmt_route_editor_new: + * @setting: the #NMSettingIP4Config or #NMSettingIP6Config to edit + * + * Creates a new #NmtRouteEditor to edit the routes in @setting + * + * Returns: a new #NmtRouteEditor + */ +NmtNewtForm * +nmt_route_editor_new(NMSetting *setting) +{ + return g_object_new(NMT_TYPE_ROUTE_EDITOR, "setting", setting, NULL); +} + +static void +nmt_route_editor_init(NmtRouteEditor *entry) +{} + +static void +save_routes_and_exit(NmtNewtButton *button, gpointer user_data) +{ + NmtRouteEditor * editor = user_data; + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE(editor); + GPtrArray * routes; + + g_object_get(priv->edit_setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL); + g_object_set(priv->orig_setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL); + g_ptr_array_unref(routes); + + nmt_newt_form_quit(NMT_NEWT_FORM(editor)); +} + +static void +nmt_route_editor_constructed(GObject *object) +{ + NmtRouteEditor * editor = NMT_ROUTE_EDITOR(object); + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE(editor); + NmtNewtWidget * vbox, *routes, *buttons, *ok, *cancel; + + if (G_OBJECT_CLASS(nmt_route_editor_parent_class)->constructed) + G_OBJECT_CLASS(nmt_route_editor_parent_class)->constructed(object); + + if (NM_IS_SETTING_IP4_CONFIG(priv->edit_setting)) + routes = nmt_route_table_new(AF_INET); + else + routes = nmt_route_table_new(AF_INET6); + g_object_bind_property(priv->edit_setting, + NM_SETTING_IP_CONFIG_ROUTES, + routes, + "routes", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + vbox = nmt_newt_grid_new(); + nmt_newt_grid_add(NMT_NEWT_GRID(vbox), routes, 0, 0); + + buttons = nmt_newt_grid_new(); + nmt_newt_grid_add(NMT_NEWT_GRID(vbox), buttons, 0, 1); + nmt_newt_widget_set_padding(buttons, 0, 1, 0, 0); + + cancel = g_object_ref_sink(nmt_newt_button_new(_("Cancel"))); + nmt_newt_widget_set_exit_on_activate(cancel, TRUE); + nmt_newt_grid_add(NMT_NEWT_GRID(buttons), cancel, 0, 0); + nmt_newt_grid_set_flags(NMT_NEWT_GRID(buttons), + cancel, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT + | NMT_NEWT_GRID_FILL_Y); + + ok = g_object_ref_sink(nmt_newt_button_new(_("OK"))); + g_signal_connect(ok, "clicked", G_CALLBACK(save_routes_and_exit), editor); + nmt_newt_grid_add(NMT_NEWT_GRID(buttons), ok, 1, 0); + nmt_newt_widget_set_padding(ok, 1, 0, 0, 0); + g_object_bind_property(routes, "valid", ok, "sensitive", G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content(NMT_NEWT_FORM(editor), vbox); +} + +static void +nmt_route_editor_finalize(GObject *object) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE(object); + + g_clear_object(&priv->orig_setting); + g_clear_object(&priv->edit_setting); + + G_OBJECT_CLASS(nmt_route_editor_parent_class)->finalize(object); +} + +static void +nmt_route_editor_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_SETTING: + priv->orig_setting = g_value_dup_object(value); + priv->edit_setting = nm_setting_duplicate(priv->orig_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_SETTING: + g_value_set_object(value, priv->edit_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_class_init(NmtRouteEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtRouteEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_editor_constructed; + object_class->set_property = nmt_route_editor_set_property; + object_class->get_property = nmt_route_editor_get_property; + object_class->finalize = nmt_route_editor_finalize; + + /** + * NmtRouteEditor:setting: + * + * The #NMSettingIP4Config or #NMSettingIP6Config whose routes are + * being edited. + */ + g_object_class_install_property( + object_class, + PROP_SETTING, + g_param_spec_object("setting", + "", + "", + NM_TYPE_SETTING, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-route-editor.h b/src/nmtui/nmt-route-editor.h new file mode 100644 index 00000000..3255324f --- /dev/null +++ b/src/nmtui/nmt-route-editor.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_EDITOR_H +#define NMT_ROUTE_EDITOR_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_ROUTE_EDITOR (nmt_route_editor_get_type()) +#define NMT_ROUTE_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor)) +#define NMT_ROUTE_EDITOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) +#define NMT_IS_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_IS_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_ROUTE_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtRouteEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtRouteEditorClass; + +GType nmt_route_editor_get_type(void); + +NmtNewtForm *nmt_route_editor_new(NMSetting *setting); + +#endif /* NMT_ROUTE_EDITOR_H */ diff --git a/src/nmtui/nmt-route-entry.c b/src/nmtui/nmt-route-entry.c new file mode 100644 index 00000000..5cd150b0 --- /dev/null +++ b/src/nmtui/nmt-route-entry.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-entry + * @short_description: A set of widgets describing a single route. + * + * #NmtRouteEntry provides a set of three entry widgets, for entering + * a network/prefix, a next hop, and a metric. + * + * This is used as a building block by #NmtRouteTable. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include "nmt-route-entry.h" +#include "nmt-ip-entry.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE(NmtRouteEntry, nmt_route_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_ENTRY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryPrivate)) + +typedef struct { + NmtNewtWidget *dest, *next_hop, *metric; + + int family; + int ip_entry_width, metric_entry_width; + NMIPRoute *route; +} NmtRouteEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_IP_ENTRY_WIDTH, + PROP_METRIC_ENTRY_WIDTH, + PROP_ROUTE, + + LAST_PROP +}; + +/** + * nmt_route_entry_new: + * @family: the address family, eg %AF_INET + * @ip_entry_width: the width in characters for the IP address entries + * @metric_entry_width: the width in characters for the metric entry + * + * Creates a new #NmtRouteEntry + * + * Returns: a new #NmtRouteEntry + */ +NmtNewtWidget * +nmt_route_entry_new(int family, int ip_entry_width, int metric_entry_width) +{ + return g_object_new(NMT_TYPE_ROUTE_ENTRY, + "family", + family, + "ip-entry-width", + ip_entry_width, + "metric-entry-width", + metric_entry_width, + NULL); +} + +static void +nmt_route_entry_init(NmtRouteEntry *entry) +{} + +static gboolean +entry_validity_transform_to_warning_label(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + if (g_value_get_boolean(source_value)) + g_value_set_string(target_value, " "); + else + g_value_set_string(target_value, "!"); + return TRUE; +} + +static NmtNewtWidget * +create_warning_label(NmtNewtWidget *entry) +{ + NmtNewtWidget *label; + + label = g_object_new(NMT_TYPE_NEWT_LABEL, "highlight", TRUE, NULL); + g_object_bind_property_full(entry, + "valid", + label, + "text", + G_BINDING_SYNC_CREATE, + entry_validity_transform_to_warning_label, + NULL, + NULL, + NULL); + return label; +} + +static void +nmt_route_entry_constructed(GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE(object); + NmtNewtGrid * grid = NMT_NEWT_GRID(object); + NmtNewtWidget * warning_label; + + priv->dest = nmt_ip_entry_new(priv->ip_entry_width, priv->family, TRUE, FALSE); + priv->next_hop = nmt_ip_entry_new(priv->ip_entry_width, priv->family, FALSE, TRUE); + priv->metric = nmt_newt_entry_numeric_new_full(priv->metric_entry_width, 0, G_MAXUINT32, TRUE); + + nmt_newt_grid_add(grid, priv->dest, 0, 0); + warning_label = create_warning_label(priv->dest); + nmt_newt_grid_add(grid, warning_label, 1, 0); + + nmt_newt_grid_add(grid, priv->next_hop, 2, 0); + nmt_newt_widget_set_padding(priv->next_hop, 1, 0, 0, 0); + warning_label = create_warning_label(priv->next_hop); + nmt_newt_grid_add(grid, warning_label, 3, 0); + + nmt_newt_grid_add(grid, priv->metric, 4, 0); + nmt_newt_widget_set_padding(priv->metric, 1, 0, 0, 0); + + nm_editor_bind_ip_route_to_strings(priv->family, + object, + "route", + priv->dest, + "text", + priv->next_hop, + "text", + priv->metric, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS(nmt_route_entry_parent_class)->constructed(object); +} + +static newtComponent +nmt_route_entry_get_focus_component(NmtNewtWidget *widget) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE(widget); + + return nmt_newt_widget_get_focus_component(priv->dest); +} + +static void +nmt_route_entry_finalize(GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE(object); + + nm_clear_pointer(&priv->route, nm_ip_route_unref); + + G_OBJECT_CLASS(nmt_route_entry_parent_class)->finalize(object); +} + +static void +nmt_route_entry_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int(value); + break; + case PROP_IP_ENTRY_WIDTH: + priv->ip_entry_width = g_value_get_int(value); + break; + case PROP_METRIC_ENTRY_WIDTH: + priv->metric_entry_width = g_value_get_int(value); + break; + case PROP_ROUTE: + if (priv->route) + nm_ip_route_unref(priv->route); + priv->route = g_value_dup_boxed(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, priv->family); + break; + case PROP_IP_ENTRY_WIDTH: + g_value_set_int(value, priv->ip_entry_width); + break; + case PROP_METRIC_ENTRY_WIDTH: + g_value_set_int(value, priv->metric_entry_width); + break; + case PROP_ROUTE: + g_value_set_boxed(value, priv->route); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_class_init(NmtRouteEntryClass *entry_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(entry_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS(entry_class); + + g_type_class_add_private(entry_class, sizeof(NmtRouteEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_entry_constructed; + object_class->set_property = nmt_route_entry_set_property; + object_class->get_property = nmt_route_entry_get_property; + object_class->finalize = nmt_route_entry_finalize; + + widget_class->get_focus_component = nmt_route_entry_get_focus_component; + + /** + * NmtRouteEntry:family: + * + * The address family of the route, eg, %AF_INET + */ + g_object_class_install_property( + object_class, + PROP_FAMILY, + g_param_spec_int("family", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:ip-entry-width: + * + * The width in characters of the IP address entries + */ + g_object_class_install_property( + object_class, + PROP_IP_ENTRY_WIDTH, + g_param_spec_int("ip-entry-width", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:metric-entry-width: + * + * The width in characters of the metric entry + */ + g_object_class_install_property( + object_class, + PROP_METRIC_ENTRY_WIDTH, + g_param_spec_int("metric-entry-width", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:route: + * + * The contents of the entries, as an #NMIPRoute. + */ + g_object_class_install_property(object_class, + PROP_ROUTE, + g_param_spec_boxed("route", + "", + "", + nm_ip_route_get_type(), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-route-entry.h b/src/nmtui/nmt-route-entry.h new file mode 100644 index 00000000..102cb61c --- /dev/null +++ b/src/nmtui/nmt-route-entry.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_ENTRY_H +#define NMT_ROUTE_ENTRY_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_ROUTE_ENTRY (nmt_route_entry_get_type()) +#define NMT_ROUTE_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry)) +#define NMT_ROUTE_ENTRY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) +#define NMT_IS_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_IS_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_ROUTE_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteEntryClass; + +GType nmt_route_entry_get_type(void); + +NmtNewtWidget *nmt_route_entry_new(int family, int ip_entry_width, int metric_entry_width); + +#endif /* NMT_ROUTE_ENTRY_H */ diff --git a/src/nmtui/nmt-route-table.c b/src/nmtui/nmt-route-table.c new file mode 100644 index 00000000..fcb14e6a --- /dev/null +++ b/src/nmtui/nmt-route-table.c @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-table + * @short_description: An editable list of IP4 or IP6 routes + * + * #NmtRouteTable implements a list of #NmtRouteEntry, plus headers, + * and buttons to add and remove entries. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include "nmt-route-table.h" +#include "nmt-route-entry.h" +#include "nmt-widget-list.h" + +G_DEFINE_TYPE(NmtRouteTable, nmt_route_table, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_TABLE_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ROUTE_TABLE, NmtRouteTablePrivate)) + +typedef struct { + int family; + + int ip_entry_width; + int metric_entry_width; + + GPtrArray * routes; + NmtNewtWidget *list; +} NmtRouteTablePrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_ROUTES, + + LAST_PROP +}; + +/** + * nmt_route_table_new: + * @family: the address family, eg %AF_INET + * + * Creates a new #NmtRouteTable + * + * Returns: a new #NmtRouteTable + */ +NmtNewtWidget * +nmt_route_table_new(int family) +{ + return g_object_new(NMT_TYPE_ROUTE_TABLE, "family", family, NULL); +} + +static gboolean +route_list_transform_to_route(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + NmtRouteTable * table = NMT_ROUTE_TABLE(g_binding_get_source(binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + int n = GPOINTER_TO_INT(user_data); + NMIPRoute * route; + + if (n >= priv->routes->len) + return FALSE; + + route = priv->routes->pdata[n]; + g_value_set_boxed(target_value, route); + return TRUE; +} + +static gboolean +route_list_transform_from_route(GBinding * binding, + const GValue *source_value, + GValue * target_value, + gpointer user_data) +{ + NmtRouteTable * table = NMT_ROUTE_TABLE(g_binding_get_source(binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + int n = GPOINTER_TO_INT(user_data); + GPtrArray * routes; + NMIPRoute * route; + + if (n >= priv->routes->len) + return FALSE; + route = priv->routes->pdata[n]; + + routes = priv->routes; + priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); + + if (route) + nm_ip_route_unref(route); + routes->pdata[n] = g_value_dup_boxed(source_value); + + g_value_take_boxed(target_value, routes); + return TRUE; +} + +static NmtNewtWidget * +create_route_entry(NmtWidgetList *list, int num, gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + NmtNewtWidget * entry; + + entry = nmt_route_entry_new(priv->family, priv->ip_entry_width, priv->metric_entry_width); + + g_object_bind_property_full(table, + "routes", + entry, + "route", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + route_list_transform_to_route, + route_list_transform_from_route, + GINT_TO_POINTER(num), + NULL); + return entry; +} + +static void +add_route(NmtWidgetList *list, gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + NMIPRoute * route; + + if (priv->family == AF_INET) + route = nm_ip_route_new(AF_INET, "0.0.0.0", 32, NULL, -1, NULL); + else + route = nm_ip_route_new(AF_INET6, "::", 128, NULL, -1, NULL); + g_ptr_array_add(priv->routes, route); + nmt_widget_list_set_length(list, priv->routes->len); + g_object_notify(table, "routes"); +} + +static void +remove_route(NmtWidgetList *list, int num, gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + + if (num >= priv->routes->len) + return; + + g_ptr_array_remove_index(priv->routes, num); + nmt_widget_list_set_length(list, priv->routes->len); + + g_object_notify(table, "routes"); +} + +static void +nmt_route_table_init(NmtRouteTable *table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); + NmtNewtWidget * header, *empty; + NmtNewtWidget * dest_prefix_label, *next_hop_label, *metric_label; + int dest_prefix_width, next_hop_width, metric_width; + char * text; + + priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); + + header = nmt_newt_grid_new(); + + text = g_strdup_printf("%s/%s", _("Destination"), _("Prefix")); + dest_prefix_width = nmt_newt_text_width(text); + dest_prefix_label = + g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); + g_free(text); + nmt_newt_grid_add(NMT_NEWT_GRID(header), dest_prefix_label, 0, 0); + + text = _("Next Hop"); + next_hop_label = + g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); + next_hop_width = nmt_newt_text_width(text); + nmt_newt_grid_add(NMT_NEWT_GRID(header), next_hop_label, 1, 0); + + text = _("Metric"); + metric_label = + g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); + metric_width = nmt_newt_text_width(text); + nmt_newt_grid_add(NMT_NEWT_GRID(header), metric_label, 2, 0); + + priv->ip_entry_width = MAX(20, MAX(dest_prefix_width, next_hop_width)); + priv->metric_entry_width = MAX(7, metric_width); + + nmt_newt_widget_set_padding(dest_prefix_label, + 0, + 0, + priv->ip_entry_width - dest_prefix_width, + 0); + nmt_newt_widget_set_padding(next_hop_label, 2, 0, priv->ip_entry_width - next_hop_width, 0); + nmt_newt_widget_set_padding(metric_label, 2, 0, priv->metric_entry_width - metric_width, 0); + + nmt_newt_grid_add(NMT_NEWT_GRID(table), header, 0, 0); + + empty = nmt_newt_label_new(_("No custom routes are defined.")); + priv->list = nmt_widget_list_new(create_route_entry, table, NULL, empty); + g_signal_connect(priv->list, "add-clicked", G_CALLBACK(add_route), table); + g_signal_connect(priv->list, "remove-clicked", G_CALLBACK(remove_route), table); + nmt_newt_grid_add(NMT_NEWT_GRID(table), priv->list, 0, 1); +} + +static void +nmt_route_table_finalize(GObject *object) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); + + g_ptr_array_unref(priv->routes); + + G_OBJECT_CLASS(nmt_route_table_parent_class)->finalize(object); +} + +static void +nmt_route_table_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); + GPtrArray * array; + int i; + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int(value); + break; + case PROP_ROUTES: + array = g_value_get_boxed(value); + g_ptr_array_set_size(priv->routes, 0); + for (i = 0; i < array->len; i++) { + nm_ip_route_ref(array->pdata[i]); + g_ptr_array_add(priv->routes, array->pdata[i]); + } + nmt_widget_list_set_length(NMT_WIDGET_LIST(priv->list), priv->routes->len); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_table_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int(value, priv->family); + break; + case PROP_ROUTES: + g_value_set_boxed(value, priv->routes); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_route_table_class_init(NmtRouteTableClass *table_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(table_class); + + g_type_class_add_private(table_class, sizeof(NmtRouteTablePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_route_table_set_property; + object_class->get_property = nmt_route_table_get_property; + object_class->finalize = nmt_route_table_finalize; + + /** + * NmtRouteTable:family: + * + * The network address family of the routes, eg %AF_INET + */ + g_object_class_install_property( + object_class, + PROP_FAMILY, + g_param_spec_int("family", + "", + "", + -1, + G_MAXINT, + -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteTable:routes: + * + * The array of routes, suitable for binding to #NMSettingIP4Config:routes + * or #NMSettingIP6Config:routes. + * + * Element-type: NMIPRoute + */ + g_object_class_install_property(object_class, + PROP_ROUTES, + g_param_spec_boxed("routes", + "", + "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-route-table.h b/src/nmtui/nmt-route-table.h new file mode 100644 index 00000000..fb29fd33 --- /dev/null +++ b/src/nmtui/nmt-route-table.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_TABLE_H +#define NMT_ROUTE_TABLE_H + +#include "libnmt-newt/nmt-newt.h" + +#define NMT_TYPE_ROUTE_TABLE (nmt_route_table_get_type()) +#define NMT_ROUTE_TABLE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable)) +#define NMT_ROUTE_TABLE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) +#define NMT_IS_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_ROUTE_TABLE)) +#define NMT_IS_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_ROUTE_TABLE)) +#define NMT_ROUTE_TABLE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteTable; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteTableClass; + +GType nmt_route_table_get_type(void); + +NmtNewtWidget *nmt_route_table_new(int family); + +#endif /* NMT_ROUTE_TABLE_H */ diff --git a/src/nmtui/nmt-slave-list.c b/src/nmtui/nmt-slave-list.c new file mode 100644 index 00000000..a6ce554d --- /dev/null +++ b/src/nmtui/nmt-slave-list.c @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-slave-list: + * @short_description: An editable list of a connection's slaves + * + * #NmtSlaveList implements an #NmtEditConnectionList for the + * slaves of a connection. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE(NmtSlaveList, nmt_slave_list, NMT_TYPE_EDIT_CONNECTION_LIST) + +#define NMT_SLAVE_LIST_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_SLAVE_LIST, NmtSlaveListPrivate)) + +typedef struct { + NMConnection *master; + const char * master_type, *master_uuid; + + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; +} NmtSlaveListPrivate; + +enum { + PROP_0, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static gboolean nmt_slave_list_connection_filter(NmtEditConnectionList *list, + NMConnection * connection, + gpointer user_data); + +/** + * nmt_slave_list_new: + * @master: the master #NMConnection whose slaves are being listed + * @type_filter: (allow-none): a function to limit the available slave types + * @type_filter_data: (allow-none): data for @type_filter. + * + * Creates a new #NmtSlaveList. + * + * If @type_filter is non-%NULL, it will be used to limit the connection + * types that are available when the user clicks on the "Add" button to add + * a new slave. If the @type_filter filters the list down to only a single + * connection type, then the user will not be presented with a connection-type + * dialog, and will instead be immediately taken to an editor window for the + * new slave after clicking "Add". + * + * Returns: a new #NmtSlaveList. + */ +NmtNewtWidget * +nmt_slave_list_new(NMConnection * master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + return g_object_new(NMT_TYPE_SLAVE_LIST, + "master", + master, + "type-filter", + type_filter, + "type-filter-data", + type_filter_data, + "grouped", + FALSE, + "connection-filter", + nmt_slave_list_connection_filter, + NULL); +} + +static void +nmt_slave_list_init(NmtSlaveList *list) +{} + +static void +nmt_slave_list_finalize(GObject *object) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE(object); + + g_object_unref(priv->master); + + G_OBJECT_CLASS(nmt_slave_list_parent_class)->finalize(object); +} + +static gboolean +nmt_slave_list_connection_filter(NmtEditConnectionList *list, + NMConnection * connection, + gpointer user_data) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE(list); + NMSettingConnection *s_con; + const char * master, *master_ifname, *slave_type; + + s_con = nm_connection_get_setting_connection(connection); + g_return_val_if_fail(s_con != NULL, FALSE); + + slave_type = nm_setting_connection_get_slave_type(s_con); + if (g_strcmp0(slave_type, priv->master_type) != 0) + return FALSE; + + master = nm_setting_connection_get_master(s_con); + if (!master) + return FALSE; + + master_ifname = nm_connection_get_interface_name(priv->master); + if (g_strcmp0(master, master_ifname) != 0 && g_strcmp0(master, priv->master_uuid) != 0) + return FALSE; + + return TRUE; +} + +static void +nmt_slave_list_add_connection(NmtEditConnectionList *list) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE(list); + + nmt_add_connection_full(_("Select the type of slave connection you wish to add."), + NULL, + priv->master, + priv->type_filter, + priv->type_filter_data); +} + +static void +nmt_slave_list_edit_connection(NmtEditConnectionList *list, NMConnection *connection) +{ + nmt_edit_connection(connection); +} + +static void +nmt_slave_list_remove_connection(NmtEditConnectionList *list, NMRemoteConnection *connection) +{ + nmt_remove_connection(connection); +} + +static void +nmt_slave_list_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MASTER: + priv->master = g_value_dup_object(value); + if (priv->master) { + NMSettingConnection *s_con = nm_connection_get_setting_connection(priv->master); + + priv->master_type = nm_setting_connection_get_connection_type(s_con); + priv->master_uuid = nm_setting_connection_get_uuid(s_con); + } + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer(value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_MASTER: + g_value_set_object(value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer(value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer(value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_class_init(NmtSlaveListClass *list_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(list_class); + NmtEditConnectionListClass *connection_list_class = NMT_EDIT_CONNECTION_LIST_CLASS(list_class); + + g_type_class_add_private(list_class, sizeof(NmtSlaveListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_slave_list_set_property; + object_class->get_property = nmt_slave_list_get_property; + object_class->finalize = nmt_slave_list_finalize; + + connection_list_class->add_connection = nmt_slave_list_add_connection; + connection_list_class->edit_connection = nmt_slave_list_edit_connection; + connection_list_class->remove_connection = nmt_slave_list_remove_connection; + + /** + * NmtSlaveList:master: + * + * The master #NMConnection whose slaves are being displayed. + */ + g_object_class_install_property( + object_class, + PROP_MASTER, + g_param_spec_object("master", + "", + "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter: + * + * If non-%NULL, this will be used to limit the connection types + * that are available when the user clicks on the "Add" button to + * add a new slave. If the filter filters the list down to only a + * single connection type, then the user will not be presented + * with a connection-type dialog, and will instead be immediately + * taken to an editor window for the new slave after clicking + * "Add". + */ + g_object_class_install_property( + object_class, + PROP_TYPE_FILTER, + g_param_spec_pointer("type-filter", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter-data: + * + * User data passed to #NmtSlaveList:type-filter + */ + g_object_class_install_property( + object_class, + PROP_TYPE_FILTER_DATA, + g_param_spec_pointer("type-filter-data", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-slave-list.h b/src/nmtui/nmt-slave-list.h new file mode 100644 index 00000000..f220fc12 --- /dev/null +++ b/src/nmtui/nmt-slave-list.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_SLAVE_LIST_H +#define NMT_SLAVE_LIST_H + +#include "nmt-edit-connection-list.h" +#include "nmtui-edit.h" + +#define NMT_TYPE_SLAVE_LIST (nmt_slave_list_get_type()) +#define NMT_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList)) +#define NMT_SLAVE_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) +#define NMT_IS_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_SLAVE_LIST)) +#define NMT_IS_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_SLAVE_LIST)) +#define NMT_SLAVE_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) + +typedef struct { + NmtEditConnectionList parent; + +} NmtSlaveList; + +typedef struct { + NmtEditConnectionListClass parent; + +} NmtSlaveListClass; + +GType nmt_slave_list_get_type(void); + +NmtNewtWidget *nmt_slave_list_new(NMConnection * master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +#endif /* NMT_SLAVE_LIST_H */ diff --git a/src/nmtui/nmt-utils.c b/src/nmtui/nmt-utils.c new file mode 100644 index 00000000..bac763f6 --- /dev/null +++ b/src/nmtui/nmt-utils.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-utils + * @short_description: Miscellaneous nmtui-specific utilities + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-utils.h" + +/** + * NmtSyncOp: + * + * A helper object used when synchronously waiting for an asynchronous + * operation to complete. + * + * The caller first does: + * + * |[ + * NmtSyncOp op; + * + * nmt_sync_op_init (&op); + * ]| + * + * It then passes the op as the user_data to the async operation's + * callback function, and then calls nmt_sync_op_wait_boolean() or + * nmt_sync_op_wait_pointer() to wait for the result. + * + * When the async callback is invoked, it should call + * nmt_sync_op_complete_boolean() or nmt_sync_op_complete_pointer() to + * return a result or an error to the caller. + * + * There is no free/clear function; any memory that needs to be freed + * will have been returned to the caller from + * nmt_sync_op_wait_boolean() or nmt_sync_op_wait_pointer(), so there + * is nothing left that needs to be freed. + */ + +typedef struct { + gpointer result; + GError * error; + gpointer complete; +} NmtSyncOpReal; + +/** + * nmt_sync_op_init: + * @op: pointer to a stack-allocated #NmtSyncOp + * + * Initializes @op before use. + */ +void +nmt_sync_op_init(NmtSyncOp *op) +{ + memset(op, 0, sizeof(*op)); +} + +/** + * nmt_sync_op_wait_boolean: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gboolean +nmt_sync_op_wait_boolean(NmtSyncOp *op, GError **error) +{ + return GPOINTER_TO_UINT(nmt_sync_op_wait_pointer(op, error)); +} + +/** + * nmt_sync_op_complete_boolean: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_boolean(NmtSyncOp *op, gboolean result, GError *error) +{ + nmt_sync_op_complete_pointer(op, GUINT_TO_POINTER(result), error); +} + +/** + * nmt_sync_op_wait_pointer: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gpointer +nmt_sync_op_wait_pointer(NmtSyncOp *op, GError **error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *) op; + + while (!real->complete) + g_main_context_iteration(NULL, TRUE); + + if (real->error) + g_propagate_error(error, real->error); + return real->result; +} + +/** + * nmt_sync_op_complete_pointer: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_pointer(NmtSyncOp *op, gpointer result, GError *error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *) op; + + real->result = result; + real->error = error ? g_error_copy(error) : NULL; + real->complete = GUINT_TO_POINTER(TRUE); +} diff --git a/src/nmtui/nmt-utils.h b/src/nmtui/nmt-utils.h new file mode 100644 index 00000000..3b780ae7 --- /dev/null +++ b/src/nmtui/nmt-utils.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_UTILS_H +#define NMT_UTILS_H + +typedef struct { + gpointer private[3]; +} NmtSyncOp; + +void nmt_sync_op_init(NmtSyncOp *op); + +gboolean nmt_sync_op_wait_boolean(NmtSyncOp *op, GError **error); +void nmt_sync_op_complete_boolean(NmtSyncOp *op, gboolean result, GError *error); + +gpointer nmt_sync_op_wait_pointer(NmtSyncOp *op, GError **error); +void nmt_sync_op_complete_pointer(NmtSyncOp *op, gpointer result, GError *error); + +#endif /* NMT_UTILS_H */ diff --git a/src/nmtui/nmt-widget-list.c b/src/nmtui/nmt-widget-list.c new file mode 100644 index 00000000..cd0c6c3c --- /dev/null +++ b/src/nmtui/nmt-widget-list.c @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-widget-list + * @short_description: A list of widgets, with Add and Remove buttons + * + * #NmtWidgetList presents a homogeneous list of widgets, with "Remove" + * buttons next to each one, and an "Add" button at the button to add + * new ones. + * + * It is the base class for #NmtAddressList, and is used internally by + * #NmtRouteTable. + * + * FIXME: The way this works is sort of weird. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-widget-list.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include "libnmt-newt/nmt-newt.h" + +G_DEFINE_TYPE(NmtWidgetList, nmt_widget_list, NMT_TYPE_NEWT_GRID) + +#define NMT_WIDGET_LIST_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_WIDGET_LIST, NmtWidgetListPrivate)) + +typedef struct { + int length; + + NmtWidgetListCallback create_callback; + gpointer user_data; + GDestroyNotify destroy_notify; + + NmtNewtWidget *empty_widget; + + GPtrArray *widgets; + GPtrArray *remove_buttons; + + NmtNewtWidget *add_button; + GBinding * add_sensitivity; +} NmtWidgetListPrivate; + +enum { + PROP_0, + PROP_CREATE_CALLBACK, + PROP_USER_DATA, + PROP_DESTROY_NOTIFY, + PROP_EMPTY_WIDGET, + PROP_LENGTH, + + LAST_PROP +}; + +enum { + ADD_CLICKED, + REMOVE_CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void add_clicked(NmtNewtButton *button, gpointer user_data); +static void remove_clicked(NmtNewtButton *button, gpointer user_data); + +/** + * NmtWidgetListCallback: + * @list: the #NmtWidgetList + * @n: the number of the widget being added + * @user_data: the callback's user data + * + * Called by #NmtWidgetList to ask for a new widget to be created. + * + * Note that the widget is not created to go with any particular + * value, but rather is created to be at a certain spot in the list. + * When an element is deleted from the list, it is actually always + * the last widget in the list that is removed, but it is assumed + * that the widget list is bound to some array-valued property, and + * so when an element is deleted from that array, the widgets will + * all update themselves automatically when the array changes. + * + * Returns: a new widget for the list + */ + +/** + * nmt_widget_list_new: + * @create_callback: callback to create new widgets + * @user_data: user data for @create_callback + * @destroy_notify: #GDestroyNotify for @user_data + * @empty_widget: (allow-none): a widget to display when there are + * no "real" widgets in the list. + * + * Creates a new #NmtWidgetList. + * + * Returns: a new #NmtWidgetList. + */ +NmtNewtWidget * +nmt_widget_list_new(NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget * empty_widget) +{ + return g_object_new(NMT_TYPE_WIDGET_LIST, + "create-callback", + create_callback, + "user-data", + user_data, + "destroy-notify", + destroy_notify, + "empty-widget", + empty_widget, + NULL); +} + +static void +nmt_widget_list_init(NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + + priv->widgets = g_ptr_array_new(); + priv->remove_buttons = g_ptr_array_new(); + + priv->add_button = nmt_newt_button_new(_("Add...")); + g_signal_connect(priv->add_button, "clicked", G_CALLBACK(add_clicked), list); + nmt_newt_grid_add(NMT_NEWT_GRID(list), priv->add_button, 0, 0); +} + +static void +nmt_widget_list_constructed(GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(object); + + if (priv->length == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible(priv->empty_widget, TRUE); + nmt_newt_grid_move(NMT_NEWT_GRID(object), priv->add_button, 0, 1); + } + + G_OBJECT_CLASS(nmt_widget_list_parent_class)->constructed(object); +} + +static void +nmt_widget_list_finalize(GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(object); + + g_ptr_array_unref(priv->widgets); + g_ptr_array_unref(priv->remove_buttons); + + if (priv->user_data && priv->destroy_notify) + priv->destroy_notify(priv->user_data); + + g_clear_object(&priv->empty_widget); + + G_OBJECT_CLASS(nmt_widget_list_parent_class)->finalize(object); +} + +static void +ensure_widgets(NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + NmtNewtWidget * widget, *button, *focus; + gboolean was_empty; + NmtNewtForm * form; + int i; + + was_empty = priv->widgets->len == 0; + + if (priv->length < priv->widgets->len) { + /* remove excess widgets */ + for (i = priv->length; i < priv->widgets->len; i++) { + nmt_newt_container_remove(NMT_NEWT_CONTAINER(list), priv->widgets->pdata[i]); + nmt_newt_container_remove(NMT_NEWT_CONTAINER(list), priv->remove_buttons->pdata[i]); + } + g_ptr_array_set_size(priv->widgets, priv->length); + g_ptr_array_set_size(priv->remove_buttons, priv->length); + + } else if (priv->length > priv->widgets->len) { + /* add new widgets */ + for (i = priv->widgets->len; i < priv->length; i++) { + widget = NMT_WIDGET_LIST_GET_CLASS(list)->create_widget(list, i); + + nmt_newt_grid_add(NMT_NEWT_GRID(list), widget, 0, i); + g_ptr_array_add(priv->widgets, widget); + + button = nmt_newt_button_new(_("Remove")); + g_signal_connect(button, "clicked", G_CALLBACK(remove_clicked), list); + + nmt_newt_grid_add(NMT_NEWT_GRID(list), button, 1, i); + nmt_newt_widget_set_padding(button, 1, 0, 0, 0); + g_ptr_array_add(priv->remove_buttons, button); + } + + } else + return; + + if (priv->widgets->len == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible(priv->empty_widget, TRUE); + nmt_newt_grid_move(NMT_NEWT_GRID(list), priv->add_button, 0, 1); + } else { + if (was_empty && priv->empty_widget) + nmt_newt_widget_set_visible(priv->empty_widget, FALSE); + nmt_newt_grid_move(NMT_NEWT_GRID(list), priv->add_button, 0, priv->length); + } + + form = nmt_newt_widget_get_form(NMT_NEWT_WIDGET(list)); + if (form) { + if (priv->widgets->len) { + if (was_empty) + focus = priv->widgets->pdata[0]; + else + focus = priv->widgets->pdata[priv->widgets->len - 1]; + } else + focus = priv->add_button; + nmt_newt_form_set_focus(form, focus); + } + + g_clear_object(&priv->add_sensitivity); + if (priv->widgets->len) { + widget = priv->widgets->pdata[priv->widgets->len - 1]; + priv->add_sensitivity = g_object_bind_property(widget, + "valid", + priv->add_button, + "sensitive", + G_BINDING_SYNC_CREATE); + g_object_add_weak_pointer(G_OBJECT(priv->add_sensitivity), + (gpointer *) &priv->add_sensitivity); + } else + nmt_newt_component_set_sensitive(NMT_NEWT_COMPONENT(priv->add_button), TRUE); +} + +static void +add_clicked(NmtNewtButton *button, gpointer list) +{ + g_signal_emit(G_OBJECT(list), signals[ADD_CLICKED], 0, NULL); +} + +static void +remove_clicked(NmtNewtButton *button, gpointer list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + int i; + + for (i = 0; i < priv->remove_buttons->len; i++) { + if (priv->remove_buttons->pdata[i] == (gpointer) button) + break; + } + g_return_if_fail(i < priv->remove_buttons->len); + + g_signal_emit(G_OBJECT(list), signals[REMOVE_CLICKED], 0, i, NULL); +} + +static NmtNewtWidget * +nmt_widget_list_real_create_widget(NmtWidgetList *list, int n) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + + g_return_val_if_fail(priv->create_callback != NULL, NULL); + + return priv->create_callback(list, n, priv->user_data); +} + +/** + * nmt_widget_list_get_length: + * @list: the #NmtNewtWidgetList + * + * Gets the number of widgets in the list. + * + * Returns: the number of widgets in the list. + */ +int +nmt_widget_list_get_length(NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + + return priv->length; +} + +/** + * nmt_widget_list_set_length: + * @list: the #NmtNewtWidgetList + * @length: the new length + * + * Changes the number of widgets in the list. Widgets will be added or + * deleted as necessary. + */ +void +nmt_widget_list_set_length(NmtWidgetList *list, int length) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(list); + + if (priv->length != length) { + priv->length = length; + g_object_notify(G_OBJECT(list), "length"); + } + + ensure_widgets(list); +} + +static void +nmt_widget_list_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + priv->create_callback = g_value_get_pointer(value); + break; + case PROP_USER_DATA: + priv->user_data = g_value_get_pointer(value); + break; + case PROP_DESTROY_NOTIFY: + priv->destroy_notify = g_value_get_pointer(value); + break; + case PROP_LENGTH: + priv->length = g_value_get_int(value); + ensure_widgets(NMT_WIDGET_LIST(object)); + break; + case PROP_EMPTY_WIDGET: + priv->empty_widget = g_value_get_object(value); + if (priv->empty_widget) { + g_object_ref_sink(priv->empty_widget); + nmt_newt_grid_add(NMT_NEWT_GRID(object), priv->empty_widget, 0, 0); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + g_value_set_pointer(value, priv->create_callback); + break; + case PROP_USER_DATA: + g_value_set_pointer(value, priv->user_data); + break; + case PROP_DESTROY_NOTIFY: + g_value_set_pointer(value, priv->destroy_notify); + break; + case PROP_LENGTH: + g_value_set_int(value, priv->length); + break; + case PROP_EMPTY_WIDGET: + g_value_set_object(value, priv->empty_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_class_init(NmtWidgetListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(list_class); + + g_type_class_add_private(list_class, sizeof(NmtWidgetListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_widget_list_constructed; + object_class->set_property = nmt_widget_list_set_property; + object_class->get_property = nmt_widget_list_get_property; + object_class->finalize = nmt_widget_list_finalize; + + list_class->create_widget = nmt_widget_list_real_create_widget; + + /* signals */ + + /** + * NmtNewtWidget::add-clicked: + * @list: the #NmtNewtWidgetList + * + * Emitted when the user clicks the "Add" button. The caller can + * decide whether or not to add a new widget, and call + * nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Add" button should be insensitive if it's + * not going to work. + */ + signals[ADD_CLICKED] = g_signal_new("add-clicked", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtWidgetListClass, add_clicked), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + /** + * NmtNewtWidget::remove-clicked: + * @list: the #NmtNewtWidgetList + * @n: the widget being removed + * + * Emitted when the user clicks one of the "Remove" buttons. The + * caller can decide whether or not to remove the widget, and + * call nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Remove" button should be insensitive if it's not + * going to work. + */ + signals[REMOVE_CLICKED] = g_signal_new("remove-clicked", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtWidgetListClass, remove_clicked), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + /* properties */ + + /** + * NmtWidgetList:create-callback: + * + * Callback called to create a new widget. + */ + g_object_class_install_property( + object_class, + PROP_CREATE_CALLBACK, + g_param_spec_pointer("create-callback", + "", + "", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:user-data: + * + * User data for #NmtWidgetList:create-callback + */ + g_object_class_install_property( + object_class, + PROP_USER_DATA, + g_param_spec_pointer("user-data", "", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:destroy-notify: + * + * #GDestroyNotify for #NmtWidgetList:user-data + */ + g_object_class_install_property( + object_class, + PROP_DESTROY_NOTIFY, + g_param_spec_pointer("destroy-notify", "", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:length: + * + * The length of the widget list; changing this value will add or + * remove widgets from the list. + */ + g_object_class_install_property(object_class, + PROP_LENGTH, + g_param_spec_int("length", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:empty-widget: + * + * If non-%NULL, this widget will be displayed when there are + * no "real" widgets in the list. + */ + g_object_class_install_property( + object_class, + PROP_EMPTY_WIDGET, + g_param_spec_object("empty-widget", + "", + "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/nmtui/nmt-widget-list.h b/src/nmtui/nmt-widget-list.h new file mode 100644 index 00000000..1825c884 --- /dev/null +++ b/src/nmtui/nmt-widget-list.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_WIDGET_LIST_H +#define NMT_WIDGET_LIST_H + +#include "libnmt-newt/nmt-newt-grid.h" + +#define NMT_TYPE_WIDGET_LIST (nmt_widget_list_get_type()) +#define NMT_WIDGET_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList)) +#define NMT_WIDGET_LIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) +#define NMT_IS_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_WIDGET_LIST)) +#define NMT_IS_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_WIDGET_LIST)) +#define NMT_WIDGET_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtWidgetList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_clicked)(NmtWidgetList *list); + void (*remove_clicked)(NmtWidgetList *list, int n); + + /* methods */ + NmtNewtWidget *(*create_widget)(NmtWidgetList *list, int n); + +} NmtWidgetListClass; + +GType nmt_widget_list_get_type(void); + +typedef NmtNewtWidget *(*NmtWidgetListCallback)(NmtWidgetList *list, int n, gpointer user_data); + +NmtNewtWidget *nmt_widget_list_new(NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget * empty_widget); + +int nmt_widget_list_get_length(NmtWidgetList *list); +void nmt_widget_list_set_length(NmtWidgetList *list, int length); + +#endif /* NMT_WIDGET_LIST_H */ diff --git a/src/nmtui/nmtui-connect.c b/src/nmtui/nmtui-connect.c new file mode 100644 index 00000000..4aa91acb --- /dev/null +++ b/src/nmtui/nmtui-connect.c @@ -0,0 +1,488 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-connect + * @short_description: nm-applet-like functionality + * + * nmtui-connect implements activating and deactivating #NMConnections, + * including presenting a password dialog if necessary. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "libnmt-newt/nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-connect.h" +#include "nmt-connect-connection-list.h" +#include "nmt-password-dialog.h" +#include "libnmc-base/nm-secret-agent-simple.h" +#include "libnmc-base/nm-vpn-helpers.h" +#include "libnmc-base/nm-client-utils.h" +#include "nmt-utils.h" + +/** + * Runs openconnect to authenticate. The current screen state is saved + * before starting the command and restored after it returns. + */ +static gboolean +openconnect_authenticate(NMConnection *connection, char **cookie, char **gateway, char **gwcert) +{ + GError * error = NULL; + NMSettingVpn *s_vpn; + gboolean ret; + int status = 0; + const char * gw, *port; + + nmt_newt_message_dialog( + _("openconnect will be run to authenticate.\nIt will return to nmtui when completed.")); + + /* Get port */ + s_vpn = nm_connection_get_setting_vpn(connection); + gw = nm_setting_vpn_get_data_item(s_vpn, "gateway"); + port = gw ? strrchr(gw, ':') : NULL; + + newtSuspend(); + + ret = nm_vpn_openconnect_authenticate_helper(gw, cookie, gateway, gwcert, &status, &error); + + newtResume(); + + if (!ret) { + nmt_newt_message_dialog(_("Error: openconnect failed: %s"), error->message); + g_clear_error(&error); + return FALSE; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + nmt_newt_message_dialog(_("openconnect failed with status %d"), WEXITSTATUS(status)); + return FALSE; + } + } else if (WIFSIGNALED(status)) { + nmt_newt_message_dialog(_("openconnect failed with signal %d"), WTERMSIG(status)); + return FALSE; + } + + if (gateway && *gateway && port) { + char *tmp = *gateway; + *gateway = g_strdup_printf("%s%s", *gateway, port); + g_free(tmp); + } + + return TRUE; +} + +static void +secrets_requested(NMSecretAgentSimple *agent, + const char * request_id, + const char * title, + const char * msg, + GPtrArray * secrets, + gpointer user_data) +{ + NmtNewtForm * form; + NMConnection *connection = NM_CONNECTION(user_data); + int i; + + /* Get secrets for OpenConnect VPN */ + if (connection && nm_connection_is_type(connection, NM_SETTING_VPN_SETTING_NAME)) { + NMSettingVpn *s_vpn = nm_connection_get_setting_vpn(connection); + + if (nm_streq0(nm_setting_vpn_get_service_type(s_vpn), + NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT)) { + gs_free char *cookie = NULL; + gs_free char *gateway = NULL; + gs_free char *gwcert = NULL; + + openconnect_authenticate(connection, &cookie, &gateway, &gwcert); + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + + if (secret->secret_type != NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET) + continue; + if (!nm_streq0(secret->vpn_type, NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT)) + continue; + if (nm_streq0(secret->entry_id, + NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "cookie")) { + g_free(secret->value); + secret->value = g_steal_pointer(&cookie); + } else if (nm_streq0(secret->entry_id, + NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "gateway")) { + g_free(secret->value); + secret->value = g_steal_pointer(&gateway); + } else if (nm_streq0(secret->entry_id, + NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "gwcert")) { + g_free(secret->value); + secret->value = g_steal_pointer(&gwcert); + } + } + } + } + + form = nmt_password_dialog_new(request_id, title, msg, secrets); + nmt_newt_form_run_sync(form); + + if (nmt_password_dialog_succeeded(NMT_PASSWORD_DIALOG(form))) + nm_secret_agent_simple_response(agent, request_id, secrets); + else + nm_secret_agent_simple_response(agent, request_id, NULL); + + g_object_unref(form); +} + +typedef struct { + NMDevice * device; + NMActiveConnection *active; + NmtSyncOp * op; +} ActivateConnectionInfo; + +static void +connect_cancelled(NmtNewtForm *form, gpointer user_data) +{ + ActivateConnectionInfo *info = user_data; + GError * error = NULL; + + error = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + nmt_sync_op_complete_boolean(info->op, FALSE, error); + g_clear_error(&error); +} + +static void +check_activated(ActivateConnectionInfo *info) +{ + NMActiveConnectionState ac_state; + const char * reason = NULL; + gs_free_error GError *error = NULL; + + ac_state = nmc_activation_get_effective_state(info->active, info->device, &reason); + if (!NM_IN_SET(ac_state, + NM_ACTIVE_CONNECTION_STATE_ACTIVATED, + NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) + return; + + if (ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { + nm_assert(reason); + error = g_error_new(NM_CLIENT_ERROR, + NM_CLIENT_ERROR_FAILED, + _("Activation failed: %s"), + reason); + } + + nmt_sync_op_complete_boolean(info->op, error == NULL, error); +} + +static void +activate_ac_state_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + check_activated(user_data); +} + +static void +activate_device_state_changed(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + check_activated(user_data); +} + +static void +activate_callback(GObject *client, GAsyncResult *result, gpointer user_data) +{ + NmtSyncOp * op = user_data; + NMActiveConnection *ac; + GError * error = NULL; + + ac = nm_client_activate_connection_finish(NM_CLIENT(client), result, &error); + if (error) + nmt_sync_op_complete_pointer(op, NULL, error); + else + nmt_sync_op_complete_pointer(op, ac, NULL); +} + +static void +add_and_activate_callback(GObject *client, GAsyncResult *result, gpointer user_data) +{ + NmtSyncOp * op = user_data; + NMActiveConnection *ac; + GError * error = NULL; + + ac = nm_client_add_and_activate_connection_finish(NM_CLIENT(client), result, &error); + if (error) + nmt_sync_op_complete_pointer(op, NULL, error); + else + nmt_sync_op_complete_pointer(op, ac, NULL); +} + +static void +deactivate_connection(NMActiveConnection *ac) +{ + GError *error = NULL; + + if (!nm_client_deactivate_connection(nm_client, ac, NULL, &error)) { + nmt_newt_message_dialog(_("Could not deactivate connection: %s"), error->message); + g_clear_error(&error); + } +} + +static void +activate_connection(NMConnection *connection, NMDevice *device, NMObject *specific_object) +{ + NmtNewtForm * form; + gs_unref_object NMSecretAgentSimple *agent = NULL; + NmtNewtWidget * label; + NmtSyncOp op; + const char * specific_object_path; + NMActiveConnection * ac; + GError * error = NULL; + ActivateConnectionInfo info = {}; + + form = g_object_new(NMT_TYPE_NEWT_FORM, "escape-exits", TRUE, NULL); + label = nmt_newt_label_new(_("Connecting...")); + nmt_newt_form_set_content(form, label); + + agent = nm_secret_agent_simple_new("nmtui"); + if (agent) { + if (connection) { + nm_secret_agent_simple_enable(agent, nm_object_get_path(NM_OBJECT(connection))); + } + g_signal_connect(agent, + NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS, + G_CALLBACK(secrets_requested), + connection); + } + + specific_object_path = specific_object ? nm_object_get_path(specific_object) : NULL; + + /* There's no way to cancel an nm_client_activate_connection() / + * nm_client_add_and_activate_connection() call, so we always let them + * complete, even if the user hits Esc; they shouldn't normally take long + * to complete anyway. + */ + + nmt_sync_op_init(&op); + if (connection) { + nm_client_activate_connection_async(nm_client, + connection, + device, + specific_object_path, + NULL, + activate_callback, + &op); + } else { + nm_client_add_and_activate_connection_async(nm_client, + NULL, + device, + specific_object_path, + NULL, + add_and_activate_callback, + &op); + } + + nmt_newt_form_show(form); + + ac = nmt_sync_op_wait_pointer(&op, &error); + if (!ac) { + nmt_newt_message_dialog(_("Could not activate connection: %s"), error->message); + g_clear_error(&error); + goto done; + } else if (nm_active_connection_get_state(ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* Already active */ + goto done; + } else if (!nmt_newt_widget_get_realized(NMT_NEWT_WIDGET(form))) { + /* User already hit Esc */ + goto done; + } + + if (agent && !connection) { + connection = NM_CONNECTION(nm_active_connection_get_connection(ac)); + if (connection) { + nm_secret_agent_simple_enable(agent, nm_object_get_path(NM_OBJECT(connection))); + } + } + + /* Now wait for the connection to actually reach the ACTIVATED state, + * allowing the user to cancel if it takes too long. + */ + nmt_sync_op_init(&op); + info.active = ac; + info.device = device; + info.op = &op; + + g_signal_connect(form, "quit", G_CALLBACK(connect_cancelled), &info); + g_signal_connect(ac, + "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK(activate_ac_state_changed), + &info); + if (device) { + g_signal_connect(device, + "notify::" NM_DEVICE_STATE, + G_CALLBACK(activate_device_state_changed), + &info); + } + + if (!nmt_sync_op_wait_boolean(&op, &error)) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + nmt_newt_message_dialog(_("Could not activate connection: %s"), error->message); + g_clear_error(&error); + } + + g_signal_handlers_disconnect_by_func(form, G_CALLBACK(connect_cancelled), &info); + g_signal_handlers_disconnect_by_func(ac, G_CALLBACK(activate_ac_state_changed), &info); + if (device) + g_signal_handlers_disconnect_by_func(device, + G_CALLBACK(activate_device_state_changed), + &info); + +done: + if (nmt_newt_widget_get_realized(NMT_NEWT_WIDGET(form))) + nmt_newt_form_quit(form); + g_object_unref(form); + + if (agent) + nm_secret_agent_old_unregister(NM_SECRET_AGENT_OLD(agent), NULL, NULL); +} + +static void +listbox_activated(NmtNewtListbox *listbox, gpointer user_data) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST(listbox); + NMConnection * connection; + NMDevice * device; + NMObject * specific_object; + NMActiveConnection * ac; + + if (!nmt_connect_connection_list_get_selection(list, + &connection, + &device, + &specific_object, + &ac)) + return; + + if (ac) + deactivate_connection(ac); + else + activate_connection(connection, device, specific_object); +} + +static void +activate_clicked(NmtNewtButton *button, gpointer listbox) +{ + listbox_activated(listbox, NULL); +} + +static void +listbox_active_changed(GObject *object, GParamSpec *pspec, gpointer button) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST(object); + static const char * activate, *deactivate; + static int deactivate_padding, activate_padding; + NMActiveConnection * ac; + gboolean has_selection; + + if (G_UNLIKELY(activate == NULL)) { + int activate_width, deactivate_width; + + activate = _("Activate"); + activate_width = nmt_newt_text_width(activate); + deactivate = _("Deactivate"); + deactivate_width = nmt_newt_text_width(deactivate); + + activate_padding = MAX(0, deactivate_width - activate_width); + deactivate_padding = MAX(0, activate_width - deactivate_width); + } + + has_selection = nmt_connect_connection_list_get_selection(list, NULL, NULL, NULL, &ac); + + nmt_newt_component_set_sensitive(button, has_selection); + if (has_selection && ac) { + nmt_newt_button_set_label(button, deactivate); + nmt_newt_widget_set_padding(button, 0, 0, deactivate_padding, 0); + } else { + nmt_newt_button_set_label(button, activate); + nmt_newt_widget_set_padding(button, 0, 0, activate_padding, 0); + } +} + +static NmtNewtForm * +nmt_connect_connection_list(gboolean is_top) +{ + int screen_width, screen_height; + NmtNewtForm * form; + NmtNewtWidget *list, *activate, *quit, *bbox, *grid; + + newtGetScreenSize(&screen_width, &screen_height); + + form = g_object_new(NMT_TYPE_NEWT_FORM, + "y", + 2, + "height", + screen_height - 4, + "escape-exits", + TRUE, + NULL); + + grid = nmt_newt_grid_new(); + + list = nmt_connect_connection_list_new(); + nmt_newt_grid_add(NMT_NEWT_GRID(grid), list, 0, 0); + nmt_newt_grid_set_flags(NMT_NEWT_GRID(grid), + list, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | NMT_NEWT_GRID_EXPAND_X + | NMT_NEWT_GRID_EXPAND_Y); + g_signal_connect(list, "activated", G_CALLBACK(listbox_activated), NULL); + + bbox = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_VERTICAL); + nmt_newt_grid_add(NMT_NEWT_GRID(grid), bbox, 1, 0); + nmt_newt_widget_set_padding(bbox, 1, 1, 0, 1); + + activate = nmt_newt_button_box_add_start(NMT_NEWT_BUTTON_BOX(bbox), _("Activate")); + g_signal_connect(list, "notify::active", G_CALLBACK(listbox_active_changed), activate); + listbox_active_changed(G_OBJECT(list), NULL, activate); + g_signal_connect(activate, "clicked", G_CALLBACK(activate_clicked), list); + + quit = nmt_newt_button_box_add_end(NMT_NEWT_BUTTON_BOX(bbox), is_top ? _("Quit") : _("Back")); + nmt_newt_widget_set_exit_on_activate(quit, TRUE); + + nmt_newt_form_set_content(form, grid); + return form; +} + +static NmtNewtForm * +nmt_connect_connection(const char *identifier) +{ + NmtNewtWidget * list; + NMConnection * connection; + NMDevice * device; + NMObject * specific_object; + NMActiveConnection *ac; + + list = nmt_connect_connection_list_new(); + if (!nmt_connect_connection_list_get_connection(NMT_CONNECT_CONNECTION_LIST(list), + identifier, + &connection, + &device, + &specific_object, + &ac)) + nmt_newt_message_dialog(_("No such connection '%s'"), identifier); + else if (ac) + nmt_newt_message_dialog(_("Connection is already active")); + else + activate_connection(connection, device, specific_object); + g_object_unref(list); + + return NULL; +} + +NmtNewtForm * +nmtui_connect(gboolean is_top, int argc, char **argv) +{ + if (argc == 2) + return nmt_connect_connection(argv[1]); + else + return nmt_connect_connection_list(is_top); +} diff --git a/src/nmtui/nmtui-connect.h b/src/nmtui/nmtui-connect.h new file mode 100644 index 00000000..811893e8 --- /dev/null +++ b/src/nmtui/nmtui-connect.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_CONNECT_H +#define NMTUI_CONNECT_H + +NmtNewtForm *nmtui_connect(gboolean is_top, int argc, char **argv); + +#endif /* NMTUI_CONNECT_H */ diff --git a/src/nmtui/nmtui-edit.c b/src/nmtui/nmtui-edit.c new file mode 100644 index 00000000..534643a7 --- /dev/null +++ b/src/nmtui/nmtui-edit.c @@ -0,0 +1,568 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-edit + * @short_description: nm-connection-editor-like functionality + * + * nmtui-edit implements editing #NMConnections. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdlib.h> + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" +#include "nmt-utils.h" + +#include "nm-editor-utils.h" + +static void +list_add_connection(NmtEditConnectionList *list, gpointer form) +{ + nmt_add_connection(); + nmt_newt_form_set_focus(form, NMT_NEWT_WIDGET(list)); +} + +static void +list_edit_connection(NmtEditConnectionList *list, NMConnection *connection, gpointer form) +{ + nmt_edit_connection(connection); + nmt_newt_form_set_focus(form, NMT_NEWT_WIDGET(list)); +} + +static void +list_remove_connection(NmtEditConnectionList *list, NMRemoteConnection *connection, gpointer form) +{ + nmt_remove_connection(connection); + nmt_newt_form_set_focus(form, NMT_NEWT_WIDGET(list)); +} + +static gboolean +edit_connection_list_filter(NmtEditConnectionList *list, + NMConnection * connection, + gpointer user_data) +{ + NMSettingConnection *s_con; + const char * master, *slave_type; + const char * uuid, *ifname; + const GPtrArray * conns; + int i; + gboolean found_master = FALSE; + + s_con = nm_connection_get_setting_connection(connection); + g_return_val_if_fail(s_con != NULL, FALSE); + + master = nm_setting_connection_get_master(s_con); + if (!master) + return TRUE; + slave_type = nm_setting_connection_get_slave_type(s_con); + if (g_strcmp0(slave_type, NM_SETTING_BOND_SETTING_NAME) != 0 + && g_strcmp0(slave_type, NM_SETTING_TEAM_SETTING_NAME) != 0 + && g_strcmp0(slave_type, NM_SETTING_BRIDGE_SETTING_NAME) != 0) + return TRUE; + + conns = nm_client_get_connections(nm_client); + for (i = 0; i < conns->len; i++) { + NMConnection *candidate = conns->pdata[i]; + + uuid = nm_connection_get_uuid(candidate); + ifname = nm_connection_get_interface_name(candidate); + if (!g_strcmp0(master, uuid) || !g_strcmp0(master, ifname)) { + found_master = TRUE; + break; + } + } + + return !found_master; +} + +static NmtNewtForm * +nmt_edit_main_connection_list(gboolean is_top) +{ + int screen_width, screen_height; + NmtNewtForm * form; + NmtNewtWidget *quit, *list; + + newtGetScreenSize(&screen_width, &screen_height); + + form = g_object_new(NMT_TYPE_NEWT_FORM, + "y", + 2, + "height", + screen_height - 4, + "escape-exits", + TRUE, + NULL); + + quit = nmt_newt_button_new(is_top ? _("Quit") : _("Back")); + nmt_newt_widget_set_exit_on_activate(quit, TRUE); + + list = g_object_new(NMT_TYPE_EDIT_CONNECTION_LIST, + "extra-widget", + quit, + "connection-filter", + edit_connection_list_filter, + NULL); + + g_signal_connect(list, "add-connection", G_CALLBACK(list_add_connection), form); + g_signal_connect(list, "edit-connection", G_CALLBACK(list_edit_connection), form); + g_signal_connect(list, "remove-connection", G_CALLBACK(list_remove_connection), form); + + nmt_newt_form_set_content(form, list); + return form; +} + +#define NMT_TYPE_ADD_CONNECTION (nmt_add_connection_get_type()) +#define NMT_ADD_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_ADD_CONNECTION, NmtAddConnection)) +#define NMT_IS_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_ADD_CONNECTION)) + +typedef NmtNewtForm NmtAddConnection; +typedef NmtNewtFormClass NmtAddConnectionClass; + +GType nmt_add_connection_get_type(void); + +G_DEFINE_TYPE(NmtAddConnection, nmt_add_connection, NMT_TYPE_NEWT_FORM) + +#define NMT_ADD_CONNECTION_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ADD_CONNECTION, NmtAddConnectionPrivate)) + +typedef struct { + NmtNewtTextbox *textbox; + NmtNewtListbox *listbox; + + char * primary_text; + char * secondary_text; + NMConnection * master; + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; + + gboolean single_type; +} NmtAddConnectionPrivate; + +enum { + PROP_0, + + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static void +create_connection(NmtNewtWidget *widget, gpointer list) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(list); + GType type = (GType) GPOINTER_TO_SIZE(nmt_newt_listbox_get_active_key(priv->listbox)); + NMConnection *connection; + + connection = nm_editor_utils_create_connection(type, priv->master, nm_client); + nmt_edit_connection(connection); + g_object_unref(connection); +} + +static void +create_connection_and_quit(NmtNewtWidget *widget, gpointer list) +{ + create_connection(widget, list); + nmt_newt_form_quit(list); +} + +static void +nmt_add_connection_init(NmtAddConnection *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(form); + NmtNewtWidget * textbox, *listbox, *button; + NmtNewtGrid * grid, *buttons; + + grid = NMT_NEWT_GRID(nmt_newt_grid_new()); + + textbox = nmt_newt_textbox_new(0, 60); + priv->textbox = NMT_NEWT_TEXTBOX(textbox); + nmt_newt_grid_add(grid, textbox, 0, 0); + + listbox = nmt_newt_listbox_new(5, NMT_NEWT_LISTBOX_SCROLL); + priv->listbox = NMT_NEWT_LISTBOX(listbox); + g_signal_connect(priv->listbox, "activated", G_CALLBACK(create_connection_and_quit), form); + nmt_newt_grid_add(grid, listbox, 0, 1); + nmt_newt_widget_set_padding(listbox, 0, 1, 0, 0); + nmt_newt_grid_set_flags(grid, listbox, NMT_NEWT_GRID_EXPAND_X); + + // FIXME: VPN description textbox + + buttons = NMT_NEWT_GRID(nmt_newt_grid_new()); + nmt_newt_grid_add(grid, NMT_NEWT_WIDGET(buttons), 0, 2); + nmt_newt_widget_set_padding(NMT_NEWT_WIDGET(buttons), 0, 1, 0, 0); + + button = g_object_ref_sink(nmt_newt_button_new(_("Cancel"))); + nmt_newt_widget_set_exit_on_activate(button, TRUE); + nmt_newt_grid_add(NMT_NEWT_GRID(buttons), button, 0, 0); + nmt_newt_widget_set_padding(button, 0, 0, 1, 0); + nmt_newt_grid_set_flags(NMT_NEWT_GRID(buttons), + button, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT + | NMT_NEWT_GRID_FILL_Y); + + button = g_object_ref_sink(nmt_newt_button_new(_("Create"))); + g_signal_connect(button, "clicked", G_CALLBACK(create_connection_and_quit), form); + nmt_newt_grid_add(NMT_NEWT_GRID(buttons), button, 1, 0); + + nmt_newt_form_set_content(NMT_NEWT_FORM(form), NMT_NEWT_WIDGET(grid)); +} + +static void +nmt_add_connection_constructed(GObject *object) +{ + NmtAddConnectionPrivate * priv = NMT_ADD_CONNECTION_GET_PRIVATE(object); + NMEditorConnectionTypeData **types; + char * text; + int i, num_types; + + if (priv->secondary_text) { + text = g_strdup_printf("%s\n\n%s", priv->primary_text, priv->secondary_text); + } else + text = g_strdup(priv->primary_text); + nmt_newt_textbox_set_text(priv->textbox, text); + g_free(text); + + types = nm_editor_utils_get_connection_type_list(); + for (i = num_types = 0; types[i]; i++) { + if (priv->type_filter && !priv->type_filter(types[i]->setting_type, priv->type_filter_data)) + continue; + nmt_newt_listbox_append(priv->listbox, + types[i]->name, + GSIZE_TO_POINTER(types[i]->setting_type)); + num_types++; + } + + if (num_types == 1) + priv->single_type = TRUE; + + G_OBJECT_CLASS(nmt_add_connection_parent_class)->constructed(object); +} + +static void +nmt_add_connection_show(NmtNewtForm *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(form); + + if (priv->single_type) { + nmt_newt_listbox_set_active(priv->listbox, 0); + create_connection(NMT_NEWT_WIDGET(priv->listbox), g_object_ref(form)); + } else + NMT_NEWT_FORM_CLASS(nmt_add_connection_parent_class)->show(form); +} + +static void +nmt_add_connection_finalize(GObject *object) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(object); + + g_free(priv->primary_text); + g_free(priv->secondary_text); + g_clear_object(&priv->master); + + G_OBJECT_CLASS(nmt_add_connection_parent_class)->finalize(object); +} + +static void +nmt_add_connection_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + priv->primary_text = g_value_dup_string(value); + break; + case PROP_SECONDARY_TEXT: + priv->secondary_text = g_value_dup_string(value); + break; + case PROP_MASTER: + priv->master = g_value_dup_object(value); + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer(value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + g_value_set_string(value, priv->primary_text); + break; + case PROP_SECONDARY_TEXT: + g_value_set_string(value, priv->secondary_text); + break; + case PROP_MASTER: + g_value_set_object(value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer(value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer(value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_class_init(NmtAddConnectionClass *add_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(add_class); + NmtNewtFormClass *form_class = NMT_NEWT_FORM_CLASS(add_class); + + g_type_class_add_private(add_class, sizeof(NmtAddConnectionPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_add_connection_constructed; + object_class->set_property = nmt_add_connection_set_property; + object_class->get_property = nmt_add_connection_get_property; + object_class->finalize = nmt_add_connection_finalize; + + form_class->show = nmt_add_connection_show; + + g_object_class_install_property( + object_class, + PROP_PRIMARY_TEXT, + g_param_spec_string("primary-text", + "", + "", + _("Select the type of connection you wish to create."), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + object_class, + PROP_SECONDARY_TEXT, + g_param_spec_string("secondary-text", + "", + "", +#if 0 + _("If you are creating a VPN, and the VPN connection you " + "wish to create does not appear in the list, you may " + "not have the correct VPN plugin installed."), +#else + NULL, +#endif + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + object_class, + PROP_MASTER, + g_param_spec_object("master", + "", + "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + object_class, + PROP_TYPE_FILTER, + g_param_spec_pointer("type-filter", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + object_class, + PROP_TYPE_FILTER_DATA, + g_param_spec_pointer("type-filter-data", + "", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +void +nmt_add_connection(void) +{ + NmtNewtForm *form; + + form = g_object_new(NMT_TYPE_ADD_CONNECTION, "title", _("New Connection"), NULL); + nmt_newt_form_show(form); + g_object_unref(form); +} + +void +nmt_add_connection_full(const char * primary_text, + const char * secondary_text, + NMConnection * master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + NmtNewtForm *form; + + form = g_object_new(NMT_TYPE_ADD_CONNECTION, + "title", + _("New Connection"), + "primary-text", + primary_text, + "secondary-text", + secondary_text, + "master", + master, + "type-filter", + type_filter, + "type-filter-data", + type_filter_data, + NULL); + nmt_newt_form_show(form); + g_object_unref(form); +} + +void +nmt_edit_connection(NMConnection *connection) +{ + NmtNewtForm *editor; + + editor = nmt_editor_new(connection); + if (!editor) + return; + + nmt_newt_form_show(editor); + g_object_unref(editor); +} + +typedef struct { + NmtSyncOp op; + gboolean got_callback, got_signal; + NMRemoteConnection *connection; +} ConnectionDeleteData; + +static void +connection_deleted_callback(GObject *connection, GAsyncResult *result, gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + GError * error = NULL; + + if (!nm_remote_connection_delete_finish(data->connection, result, &error)) { + nmt_newt_message_dialog(_("Unable to delete connection: %s"), error->message); + } else + data->got_callback = TRUE; + + if (error || (data->got_callback && data->got_signal)) + nmt_sync_op_complete_boolean(&data->op, error == NULL, error); + g_clear_error(&error); +} + +static void +connection_removed_signal(NMClient *client, NMRemoteConnection *connection, gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + + if (connection == data->connection) { + data->got_signal = TRUE; + if (data->got_callback && data->got_signal) + nmt_sync_op_complete_boolean(&data->op, TRUE, NULL); + } +} + +static void +remove_one_connection(NMRemoteConnection *connection) +{ + ConnectionDeleteData data; + GError * error = NULL; + + data.got_callback = data.got_signal = FALSE; + nmt_sync_op_init(&data.op); + + data.connection = connection; + g_signal_connect(nm_client, + NM_CLIENT_CONNECTION_REMOVED, + G_CALLBACK(connection_removed_signal), + &data); + nm_remote_connection_delete_async(connection, NULL, connection_deleted_callback, &data); + + if (!nmt_sync_op_wait_boolean(&data.op, &error)) { + nmt_newt_message_dialog(_("Could not delete connection '%s': %s"), + nm_connection_get_id(NM_CONNECTION(connection)), + error->message); + g_error_free(error); + } + + g_signal_handlers_disconnect_by_func(nm_client, G_CALLBACK(connection_removed_signal), &data); +} + +void +nmt_remove_connection(NMRemoteConnection *connection) +{ + const GPtrArray * all_conns; + GSList * slaves, *iter; + int i; + NMRemoteConnection * slave; + NMSettingConnection *s_con; + const char * uuid, *iface, *master; + int choice; + + choice = nmt_newt_choice_dialog(_("Cancel"), + _("Delete"), + _("Are you sure you want to delete the connection '%s'?"), + nm_connection_get_id(NM_CONNECTION(connection))); + if (choice == 1) + return; + + g_object_ref(connection); + remove_one_connection(connection); + + uuid = nm_connection_get_uuid(NM_CONNECTION(connection)); + iface = nm_connection_get_interface_name(NM_CONNECTION(connection)); + + all_conns = nm_client_get_connections(nm_client); + slaves = NULL; + for (i = 0; i < all_conns->len; i++) { + slave = all_conns->pdata[i]; + s_con = nm_connection_get_setting_connection(NM_CONNECTION(slave)); + master = nm_setting_connection_get_master(s_con); + if (master) { + if (!g_strcmp0(master, uuid) || !g_strcmp0(master, iface)) + slaves = g_slist_prepend(slaves, g_object_ref(slave)); + } + } + + for (iter = slaves; iter; iter = iter->next) + remove_one_connection(iter->data); + g_slist_free_full(slaves, g_object_unref); + + g_object_unref(connection); +} + +NmtNewtForm * +nmtui_edit(gboolean is_top, int argc, char **argv) +{ + NMConnection *conn = NULL; + + if (argc == 2) { + if (nm_utils_is_uuid(argv[1])) + conn = NM_CONNECTION(nm_client_get_connection_by_uuid(nm_client, argv[1])); + if (!conn) + conn = NM_CONNECTION(nm_client_get_connection_by_id(nm_client, argv[1])); + + if (!conn) { + nmt_newt_message_dialog("%s: no such connection '%s'\n", argv[0], argv[1]); + return NULL; + } + + return nmt_editor_new(conn); + } else + return nmt_edit_main_connection_list(is_top); +} diff --git a/src/nmtui/nmtui-edit.h b/src/nmtui/nmtui-edit.h new file mode 100644 index 00000000..99e370b4 --- /dev/null +++ b/src/nmtui/nmtui-edit.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_EDIT_H +#define NMTUI_EDIT_H + +#include "libnmt-newt/nmt-newt.h" + +typedef gboolean (*NmtAddConnectionTypeFilter)(GType connection_type, gpointer user_data); + +NmtNewtForm *nmtui_edit(gboolean is_top, int argc, char **argv); + +void nmt_add_connection(void); +void nmt_add_connection_full(const char * primary_text, + const char * secondary_text, + NMConnection * master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +void nmt_edit_connection(NMConnection *connection); + +void nmt_remove_connection(NMRemoteConnection *connection); + +#endif /* NMTUI_EDIT_H */ diff --git a/src/nmtui/nmtui-hostname.c b/src/nmtui/nmtui-hostname.c new file mode 100644 index 00000000..2f252428 --- /dev/null +++ b/src/nmtui/nmtui-hostname.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-hostname + * @short_description: hostname-setting functionality + * + * nmtui-hostname implements the "set hostname" functionality + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "libnmt-newt/nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-hostname.h" +#include "nmt-utils.h" + +static char * +nmtui_hostname_run_dialog(void) +{ + NmtNewtForm * form; + NmtNewtWidget * widget, *ok, *cancel; + NmtNewtGrid * grid; + NmtNewtEntry * entry; + NmtNewtButtonBox *bbox; + char * hostname, *ret = NULL; + + form = g_object_new(NMT_TYPE_NEWT_FORM, "title", _("Set Hostname"), "escape-exits", TRUE, NULL); + + widget = nmt_newt_grid_new(); + nmt_newt_form_set_content(form, widget); + grid = NMT_NEWT_GRID(widget); + + widget = nmt_newt_label_new(_("Hostname")); + nmt_newt_grid_add(grid, widget, 0, 0); + + widget = nmt_newt_entry_new(40, 0); + nmt_newt_widget_set_exit_on_activate(widget, TRUE); + nmt_newt_grid_add(grid, widget, 1, 0); + nmt_newt_widget_set_padding(widget, 1, 0, 0, 0); + entry = NMT_NEWT_ENTRY(widget); + + widget = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add(grid, widget, 1, 1); + nmt_newt_widget_set_padding(widget, 0, 1, 0, 0); + bbox = NMT_NEWT_BUTTON_BOX(widget); + + cancel = nmt_newt_button_box_add_end(bbox, _("Cancel")); + nmt_newt_widget_set_exit_on_activate(cancel, TRUE); + ok = nmt_newt_button_box_add_end(bbox, _("OK")); + nmt_newt_widget_set_exit_on_activate(ok, TRUE); + + g_object_get(G_OBJECT(nm_client), NM_CLIENT_HOSTNAME, &hostname, NULL); + nmt_newt_entry_set_text(entry, hostname); + g_free(hostname); + + widget = nmt_newt_form_run_sync(form); + if (widget == (NmtNewtWidget *) entry || widget == ok) + ret = g_strdup(nmt_newt_entry_get_text(entry)); + + g_object_unref(form); + return ret; +} + +static void +hostname_set(GObject *object, GAsyncResult *result, gpointer op) +{ + GError *error = NULL; + + nm_client_save_hostname_finish(NM_CLIENT(object), result, &error); + nmt_sync_op_complete_boolean(op, error == NULL, error); + g_clear_error(&error); +} + +NmtNewtForm * +nmtui_hostname(gboolean is_top, int argc, char **argv) +{ + const char *hostname; + char * tmp = NULL; + NmtSyncOp op; + GError * error = NULL; + + if (argc == 2) + hostname = argv[1]; + else + hostname = tmp = nmtui_hostname_run_dialog(); + + if (hostname) { + nmt_sync_op_init(&op); + nm_client_save_hostname_async(nm_client, hostname, NULL, hostname_set, &op); + if (nmt_sync_op_wait_boolean(&op, &error)) { + /* TRANSLATORS: this indicates the result. ie, "I have set the hostname to ..." */ + nmt_newt_message_dialog(_("Set hostname to '%s'"), hostname); + } else { + nmt_newt_message_dialog(_("Unable to set hostname: %s"), error->message); + g_error_free(error); + } + + g_free(tmp); + } + + return NULL; +} diff --git a/src/nmtui/nmtui-hostname.h b/src/nmtui/nmtui-hostname.h new file mode 100644 index 00000000..51abef34 --- /dev/null +++ b/src/nmtui/nmtui-hostname.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_HOSTNAME_H +#define NMTUI_HOSTNAME_H + +NmtNewtForm *nmtui_hostname(gboolean is_top, int argc, char **argv); + +#endif /* NMTUI_HOSTNAME_H */ diff --git a/src/nmtui/nmtui.c b/src/nmtui/nmtui.c new file mode 100644 index 00000000..5edf70e5 --- /dev/null +++ b/src/nmtui/nmtui.c @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui + * @short_description: nmtui toplevel + * + * The top level of nmtui. Exists mostly just to call nmtui_connect(), + * nmtui_edit(), and nmtui_hostname(). + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmtui.h" + +#include <locale.h> +#include <stdlib.h> + +#include "libnm-client-aux-extern/nm-libnm-aux.h" + +#include "libnmt-newt/nmt-newt.h" +#include "nm-editor-bindings.h" + +#include "nmtui-edit.h" +#include "nmtui-connect.h" +#include "nmtui-hostname.h" + +NMClient * nm_client; +static GMainLoop *loop; + +typedef NmtNewtForm *(*NmtuiSubprogram)(gboolean is_top, int argc, char **argv); + +static const struct { + const char * name, *shortcut, *arg; + const char * display_name; + NmtuiSubprogram func; +} subprograms[] = { + {"edit", "nmtui-edit", N_("connection"), N_("Edit a connection"), nmtui_edit}, + {"connect", "nmtui-connect", N_("connection"), N_("Activate a connection"), nmtui_connect}, + {"hostname", "nmtui-hostname", N_("new hostname"), N_("Set system hostname"), nmtui_hostname}}; +static const int num_subprograms = G_N_ELEMENTS(subprograms); +static NmtNewtForm *toplevel_form; + +static NmtNewtForm * +quit_func(int argc, char **argv) +{ + if (toplevel_form) + nmt_newt_form_quit(toplevel_form); + + nmtui_quit(); + + return NULL; +} + +static void +main_list_activated(NmtNewtWidget *widget, NmtNewtListbox *listbox) +{ + NmtNewtForm * form; + NmtuiSubprogram sub; + + sub = nmt_newt_listbox_get_active_key(listbox); + if (sub) { + form = sub(FALSE, 0, NULL); + if (form) { + nmt_newt_form_show(form); + g_object_unref(form); + } + } +} + +static NmtNewtForm * +nmtui_main(gboolean is_top, int argc, char **argv) +{ + NmtNewtForm * form; + NmtNewtWidget * widget, *ok; + NmtNewtGrid * grid; + NmtNewtListbox * listbox; + NmtNewtButtonBox *bbox; + int i; + + form = g_object_new(NMT_TYPE_NEWT_FORM, + "title", + _("NetworkManager TUI"), + "escape-exits", + TRUE, + NULL); + + widget = nmt_newt_grid_new(); + nmt_newt_form_set_content(form, widget); + grid = NMT_NEWT_GRID(widget); + + widget = nmt_newt_label_new(_("Please select an option")); + nmt_newt_grid_add(grid, widget, 0, 0); + + widget = g_object_new(NMT_TYPE_NEWT_LISTBOX, + "height", + num_subprograms + 2, + "skip-null-keys", + TRUE, + NULL); + nmt_newt_grid_add(grid, widget, 0, 1); + nmt_newt_widget_set_padding(widget, 0, 1, 0, 1); + listbox = NMT_NEWT_LISTBOX(widget); + g_signal_connect(widget, "activated", G_CALLBACK(main_list_activated), listbox); + + for (i = 0; i < num_subprograms; i++) { + nmt_newt_listbox_append(listbox, _(subprograms[i].display_name), subprograms[i].func); + } + nmt_newt_listbox_append(listbox, "", NULL); + nmt_newt_listbox_append(listbox, _("Quit"), quit_func); + + widget = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add(grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX(widget); + + ok = nmt_newt_button_box_add_end(bbox, _("OK")); + g_signal_connect(ok, "activated", G_CALLBACK(main_list_activated), listbox); + + toplevel_form = form; + + return form; +} + +/** + * nmtui_quit: + * + * Causes nmtui to exit. + */ +void +nmtui_quit(void) +{ + g_main_loop_quit(loop); +} + +static void +usage(void) +{ + const char *argv0 = g_get_prgname(); + const char *usage_str = _("Usage"); + int i; + + for (i = 0; i < num_subprograms; i++) { + if (!strcmp(argv0, subprograms[i].shortcut)) { + g_printerr("%s: %s [%s]\n", usage_str, argv0, _(subprograms[i].arg)); + exit(1); + } + } + + g_printerr("%s: nmtui\n", usage_str); + for (i = 0; i < num_subprograms; i++) { + g_printerr("%*s nmtui %s [%s]\n", + nmt_newt_text_width(usage_str), + " ", + subprograms[i].name, + _(subprograms[i].arg)); + } + exit(1); +} + +typedef struct { + NmtuiSubprogram subprogram; + int argc; + char ** argv; +} NmtuiStartupData; + +static void +toplevel_form_quit(NmtNewtForm *form, gpointer user_data) +{ + nmtui_quit(); +} + +static gboolean +idle_run_subprogram(gpointer user_data) +{ + NmtuiStartupData *data = user_data; + NmtNewtForm * form; + + form = data->subprogram(TRUE, data->argc, data->argv); + if (form) { + g_signal_connect(form, "quit", G_CALLBACK(toplevel_form_quit), NULL); + nmt_newt_form_show(form); + g_object_unref(form); + } else + nmtui_quit(); + + return FALSE; +} + +gboolean sleep_on_startup = FALSE; +gboolean noinit = FALSE; + +GOptionEntry entries[] = {{"sleep", + 's', + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, + &sleep_on_startup, + "Sleep on startup", + NULL}, + {"noinit", + 'n', + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, + &noinit, + "Don't initialize newt", + NULL}, + {NULL}}; + +int +main(int argc, char **argv) +{ + GOptionContext * opts; + GError * error = NULL; + NmtuiStartupData startup_data; + const char * prgname; + int i; + + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, NMLOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + opts = g_option_context_new(NULL); + g_option_context_add_main_entries(opts, entries, NULL); + + if (!g_option_context_parse(opts, &argc, &argv, &error)) { + g_printerr("%s: %s: %s\n", argv[0], _("Could not parse arguments"), error->message); + exit(1); + } + g_option_context_free(opts); + + nm_editor_bindings_init(); + + if (!nmc_client_new_waitsync(NULL, + &nm_client, + &error, + NM_CLIENT_INSTANCE_FLAGS, + (guint) NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS, + NULL)) { + g_printerr(_("Could not contact NetworkManager: %s.\n"), error->message); + g_error_free(error); + exit(1); + } + if (!nm_client_get_nm_running(nm_client)) { + g_printerr("%s\n", _("NetworkManager is not running.")); + exit(1); + } + + if (sleep_on_startup) + sleep(5); + + startup_data.subprogram = NULL; + prgname = g_get_prgname(); + if (g_str_has_prefix(prgname, "lt-")) + prgname += 3; + if (!strcmp(prgname, "nmtui")) { + if (argc > 1) { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp(argv[1], subprograms[i].name)) { + argc--; + argv[0] = (char *) subprograms[i].shortcut; + memmove(&argv[1], &argv[2], argc * sizeof(char *)); + startup_data.subprogram = subprograms[i].func; + break; + } + } + } else + startup_data.subprogram = nmtui_main; + } else { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp(prgname, subprograms[i].shortcut)) { + startup_data.subprogram = subprograms[i].func; + break; + } + } + } + if (!startup_data.subprogram) + usage(); + + if (!noinit) + nmt_newt_init(); + + startup_data.argc = argc; + startup_data.argv = argv; + g_idle_add(idle_run_subprogram, &startup_data); + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + if (!noinit) + nmt_newt_finished(); + + g_object_unref(nm_client); + + return 0; +} diff --git a/src/nmtui/nmtui.h b/src/nmtui/nmtui.h new file mode 100644 index 00000000..db67ff57 --- /dev/null +++ b/src/nmtui/nmtui.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_H +#define NMTUI_H + +extern NMClient *nm_client; + +void nmtui_quit(void); + +#endif /* NMTUI_H */ |