// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2013 Red Hat, Inc. */ /** * SECTION:nmtui-edit * @short_description: nm-connection-editor-like functionality * * nmtui-edit implements editing #NMConnections. */ #include "nm-default.h" #include #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); }