summary refs log tree commit diff
path: root/src/nmtui/nmtui-edit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nmtui/nmtui-edit.c')
-rw-r--r--src/nmtui/nmtui-edit.c568
1 files changed, 568 insertions, 0 deletions
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);
+}