From 13e3f32aa7b3b483642477d92fd226f9458f5ba4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 8 Jun 2016 13:23:49 +0200 Subject: c-e: support multiple "Add VPN connection" entires per service-type For one given service-type, the UI shall be able to show multiple entires in the list for creating a new connection. This works by asking the plugin whether it supports multiple "add-details". And if so, create an entry for each add-detail. When adding a VPN connection, the editor creates a NMConnection with initializing vpn.service-type and setting the add-detail to a key according to the plugin's instruction. For example, nm-openvpn supports multiple "vpn.connection-type" such as "tls", "static-key", "password", "password-tls". With this change, it could generate an entry for each type in nm-c-e's connection-add list. To do that, it should announce add-details to be "tls", "static-key", "password", "password-tls", and the "add-detail-key" to be "connection-type". It also works to specify the add-detail on command line to create a particular VPN connection: $ nm-connection-editor -c -t vpn:: For example: $ nm-connection-editor -c -t vpn:openconnect:nc --- src/connection-editor/ce-new-connection.ui | 4 ++ src/connection-editor/connection-helpers.c | 91 ++++++++++++++++++++++++------ src/connection-editor/page-vpn.c | 53 ++++++++++++++++- src/connection-editor/page-vpn.h | 5 ++ 4 files changed, 135 insertions(+), 18 deletions(-) diff --git a/src/connection-editor/ce-new-connection.ui b/src/connection-editor/ce-new-connection.ui index c9dfa547..39de66b5 100644 --- a/src/connection-editor/ce-new-connection.ui +++ b/src/connection-editor/ce-new-connection.ui @@ -16,6 +16,10 @@ + + + + diff --git a/src/connection-editor/connection-helpers.c b/src/connection-editor/connection-helpers.c index 0094376b..b99cd629 100644 --- a/src/connection-editor/connection-helpers.c +++ b/src/connection-editor/connection-helpers.c @@ -43,6 +43,8 @@ #define COL_DESCRIPTION 3 #define COL_VPN_PLUGIN 4 #define COL_VPN_SERVICE_TYPE 5 +#define COL_VPN_ADD_DETAIL_KEY 6 +#define COL_VPN_ADD_DETAIL_VAL 7 static gint sort_types (gconstpointer a, gconstpointer b) @@ -273,6 +275,9 @@ set_up_connection_type_combo (GtkComboBox *combo, gs_free char *pretty_name = NULL; gs_free char *description = NULL; NMVpnEditorPluginServiceFlags flags; + gs_strfreev char **add_details_free = NULL; + char **add_details; + const char *i_add_detail; if (!nm_vpn_editor_plugin_get_service_info (plugin, service_type, NULL, &pretty_name, &description, &flags)) { if (is_alias) @@ -288,21 +293,61 @@ set_up_connection_type_combo (GtkComboBox *combo, if (!NM_FLAGS_HAS (flags, NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD)) goto next; - if (show_headers) - markup = g_markup_printf_escaped (" %s", pretty_name); - else - markup = g_markup_escape_text (pretty_name, -1); - - gtk_list_store_append (model, &iter); - gtk_list_store_set (model, &iter, - COL_MARKUP, markup, - COL_SENSITIVE, TRUE, - COL_NEW_FUNC, list[vpn_index].new_connection_func, - COL_DESCRIPTION, description, - COL_VPN_PLUGIN, plugin, - COL_VPN_SERVICE_TYPE, service_type, - -1); - g_free (markup); + add_details_free = nm_vpn_editor_plugin_get_service_add_details (plugin, service_type); + add_details = add_details_free; + i_add_detail = add_details ? add_details[0] : NULL; + do { + const char *i_pretty_name, *i_description; + gs_free char *i_pretty_name_free = NULL; + gs_free char *i_description_free = NULL; + gs_free char *i_add_detail_key = NULL; + gs_free char *i_add_detail_val = NULL; + + if (i_add_detail) { + if (i_add_detail[0] == '\0') + goto i_next; + if (!nm_vpn_editor_plugin_get_service_add_detail (plugin, service_type, i_add_detail, + &i_pretty_name_free, &i_description_free, + &i_add_detail_key, &i_add_detail_val, NULL)) + goto i_next; + if (!i_pretty_name_free) + goto i_next; + if (i_add_detail_key && !i_add_detail_key[0]) + goto i_next; + if (i_add_detail_val && !i_add_detail_val[0]) + goto next; + if (!i_add_detail_key ^ !i_add_detail_val) + goto next; + i_pretty_name = i_pretty_name_free; + i_description = i_description_free; + } else { + i_pretty_name = pretty_name; + i_description = description; + } + + if (show_headers) + markup = g_markup_printf_escaped (" %s", i_pretty_name); + else + markup = g_markup_escape_text (i_pretty_name, -1); + + gtk_list_store_append (model, &iter); + gtk_list_store_set (model, &iter, + COL_MARKUP, markup, + COL_SENSITIVE, TRUE, + COL_NEW_FUNC, list[vpn_index].new_connection_func, + COL_DESCRIPTION, i_description, + COL_VPN_PLUGIN, plugin, + COL_VPN_SERVICE_TYPE, service_type, + COL_VPN_ADD_DETAIL_KEY, i_add_detail_key, + COL_VPN_ADD_DETAIL_VAL, i_add_detail_val, + -1); + g_free (markup); + +i_next: + if (!add_details) + break; + i_add_detail = (++add_details)[0]; + } while (i_add_detail); next: if (!aliases || !aliases[0]) @@ -426,8 +471,12 @@ new_connection_dialog_full (GtkWindow *parent_window, int response; PageNewConnectionFunc new_func = NULL; gs_free char *vpn_service_type = NULL; + gs_free char *vpn_add_detail_key = NULL; + gs_free char *vpn_add_detail_val = NULL; const char *detail = NULL; + gpointer detail_data = NULL; GError *error = NULL; + CEPageVpnDetailData vpn_data; /* load GUI */ gui = gtk_builder_new (); @@ -462,16 +511,24 @@ new_connection_dialog_full (GtkWindow *parent_window, gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter, COL_NEW_FUNC, &new_func, COL_VPN_SERVICE_TYPE, &vpn_service_type, + COL_VPN_ADD_DETAIL_KEY, &vpn_add_detail_key, + COL_VPN_ADD_DETAIL_VAL, &vpn_add_detail_val, -1); - if (vpn_service_type) + if (vpn_service_type) { + memset (&vpn_data, 0, sizeof (vpn_data)); + vpn_data.add_detail_key = vpn_add_detail_key; + vpn_data.add_detail_val = vpn_add_detail_val; + detail = vpn_service_type; + detail_data = &vpn_data; + } } gtk_widget_destroy (GTK_WIDGET (type_dialog)); g_object_unref (gui); if (new_func) - new_connection_of_type (parent_window, detail, NULL, client, new_func, result_func, user_data); + new_connection_of_type (parent_window, detail, detail_data, client, new_func, result_func, user_data); else result_func (NULL, user_data); } diff --git a/src/connection-editor/page-vpn.c b/src/connection-editor/page-vpn.c index 4b4a45dd..44c96843 100644 --- a/src/connection-editor/page-vpn.c +++ b/src/connection-editor/page-vpn.c @@ -28,6 +28,7 @@ #include "connection-helpers.h" #include "nm-connection-editor.h" #include "vpn-helpers.h" +#include "nm-vpn-editor-plugin-call.h" G_DEFINE_TYPE (CEPageVpn, ce_page_vpn, CE_TYPE_PAGE) @@ -290,6 +291,12 @@ vpn_connection_new (GtkWindow *parent, NMSetting *s_vpn; const char *service_type; gs_free char *service_type_free = NULL; + gs_free char *add_detail_key_free = NULL; + gs_free char *add_detail_val_free = NULL; + const CEPageVpnDetailData *vpn_data = detail_data; + gssize split_idx, l; + const char *add_detail_key = NULL; + const char *add_detail_val = NULL; if (!detail) { NewVpnInfo *info; @@ -309,8 +316,48 @@ vpn_connection_new (GtkWindow *parent, return; } + service_type = detail; + add_detail_key = vpn_data ? vpn_data->add_detail_key : NULL; + add_detail_val = vpn_data ? vpn_data->add_detail_val : NULL; + service_type_free = nm_vpn_plugin_info_list_find_service_type (vpn_get_plugin_infos (), detail); - service_type = service_type_free ?: detail; + if (service_type_free) + service_type = service_type_free; + else if (!vpn_data) { + /* when called without @vpn_data, it means that @detail may contain ":". + * Try to parse them by spliting @detail at the colons and try to interpret the first part as + * @service_type and the remainder as add-detail. */ + l = strlen (detail); + for (split_idx = 1; split_idx < l - 1; split_idx++) { + if (detail[split_idx] == ':') { + gs_free char *detail_main = g_strndup (detail, split_idx); + NMVpnEditorPlugin *plugin; + + service_type_free = nm_vpn_plugin_info_list_find_service_type (vpn_get_plugin_infos (), detail_main); + if (!service_type_free) + continue; + plugin = vpn_get_plugin_by_service (service_type_free); + if (!plugin) { + g_clear_pointer (&service_type_free, g_free); + continue; + } + + /* we found a @service_type. Try to use the remainder as add-detail. */ + service_type = service_type_free; + if (nm_vpn_editor_plugin_get_service_add_detail (plugin, service_type, &detail[split_idx + 1], + NULL, NULL, + &add_detail_key_free, &add_detail_val_free, NULL) + && add_detail_key_free && add_detail_key_free[0] + && add_detail_val_free && add_detail_val_free[0]) { + add_detail_key = add_detail_key_free; + add_detail_val = add_detail_val_free; + } + break; + } + } + } + if (!service_type) + service_type = detail; connection = ce_page_new_connection (_("VPN connection %d"), NM_SETTING_VPN_SETTING_NAME, @@ -319,6 +366,10 @@ vpn_connection_new (GtkWindow *parent, user_data); s_vpn = nm_setting_vpn_new (); g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); + + if (add_detail_key) + nm_setting_vpn_add_data_item ((NMSettingVpn *) s_vpn, add_detail_key, add_detail_val); + nm_connection_add_setting (connection, s_vpn); (*result_func) (connection, FALSE, NULL, user_data); diff --git a/src/connection-editor/page-vpn.h b/src/connection-editor/page-vpn.h index e8dddf0e..4564efdd 100644 --- a/src/connection-editor/page-vpn.h +++ b/src/connection-editor/page-vpn.h @@ -43,6 +43,11 @@ typedef struct { CEPageClass parent; } CEPageVpnClass; +typedef struct { + char *add_detail_key; + char *add_detail_val; +} CEPageVpnDetailData; + GType ce_page_vpn_get_type (void); CEPage *ce_page_vpn_new (NMConnectionEditor *editor, -- cgit v1.2.1