vpn-helpers.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /*
  3. * This program is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU General Public License as
  5. * published by the Free Software Foundation; either version 2 of the
  6. * License, or (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful, but
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. * General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * Copyright 2013 Red Hat, Inc.
  17. */
  18. /**
  19. * SECTION:vpn-helpers
  20. * @short_description: VPN-related utilities
  21. *
  22. * This is copied directly from libnm-gtk and should probably
  23. * eventually move into libnm-glib.
  24. *
  25. * It is also currently unused in nmtui.
  26. *
  27. * FIXME.
  28. */
  29. #include <string.h>
  30. #include <glib.h>
  31. #include <gmodule.h>
  32. #include <glib/gi18n.h>
  33. #include <nm-connection.h>
  34. #include <nm-setting-connection.h>
  35. #include <nm-setting-vpn.h>
  36. #include "vpn-helpers.h"
  37. #define NM_VPN_API_SUBJECT_TO_CHANGE
  38. #include "nm-vpn-plugin-ui-interface.h"
  39. #define VPN_NAME_FILES_DIR SYSCONFDIR"/NetworkManager/VPN"
  40. static GHashTable *plugins = NULL;
  41. G_DEFINE_QUARK (NMA_ERROR, nma_error)
  42. #define NMA_ERROR nma_error_quark ()
  43. #define NMA_ERROR_GENERIC 0
  44. NMVpnPluginUiInterface *
  45. vpn_get_plugin_by_service (const char *service)
  46. {
  47. g_return_val_if_fail (service != NULL, NULL);
  48. return g_hash_table_lookup (plugins, service);
  49. }
  50. GHashTable *
  51. vpn_get_plugins (GError **error)
  52. {
  53. GDir *dir;
  54. const char *f;
  55. if (error)
  56. g_return_val_if_fail (*error == NULL, NULL);
  57. if (plugins)
  58. return plugins;
  59. dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
  60. if (!dir) {
  61. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Couldn't read VPN .name files directory " VPN_NAME_FILES_DIR ".");
  62. return NULL;
  63. }
  64. plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
  65. (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
  66. while ((f = g_dir_read_name (dir))) {
  67. char *path = NULL, *service = NULL;
  68. char *so_path = NULL, *so_name = NULL;
  69. GKeyFile *keyfile = NULL;
  70. GModule *module;
  71. NMVpnPluginUiFactory factory = NULL;
  72. if (!g_str_has_suffix (f, ".name"))
  73. continue;
  74. path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f);
  75. keyfile = g_key_file_new ();
  76. if (!g_key_file_load_from_file (keyfile, path, 0, NULL))
  77. goto next;
  78. service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL);
  79. if (!service)
  80. goto next;
  81. so_path = g_key_file_get_string (keyfile, "GNOME", "properties", NULL);
  82. if (!so_path)
  83. goto next;
  84. /* Remove any path and extension components, then reconstruct path
  85. * to the SO in LIBDIR
  86. */
  87. so_name = g_path_get_basename (so_path);
  88. g_free (so_path);
  89. so_path = g_strdup_printf ("%s/NetworkManager/%s", LIBDIR, so_name);
  90. g_free (so_name);
  91. module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
  92. if (!module) {
  93. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Cannot load the VPN plugin which provides the "
  94. "service '%s'.", service);
  95. goto next;
  96. }
  97. if (g_module_symbol (module, "nm_vpn_plugin_ui_factory", (gpointer) &factory)) {
  98. NMVpnPluginUiInterface *plugin;
  99. GError *factory_error = NULL;
  100. gboolean success = FALSE;
  101. plugin = factory (&factory_error);
  102. if (plugin) {
  103. char *plug_name = NULL, *plug_service = NULL;
  104. /* Validate plugin properties */
  105. g_object_get (G_OBJECT (plugin),
  106. NM_VPN_PLUGIN_UI_INTERFACE_NAME, &plug_name,
  107. NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, &plug_service,
  108. NULL);
  109. if (!plug_name || !strlen (plug_name)) {
  110. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': missing plugin name",
  111. g_module_name (module));
  112. } else if (!plug_service || strcmp (plug_service, service)) {
  113. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': invalid service name",
  114. g_module_name (module));
  115. } else {
  116. /* Success! */
  117. g_object_set_data_full (G_OBJECT (plugin), "gmodule", module,
  118. (GDestroyNotify) g_module_close);
  119. g_hash_table_insert (plugins, g_strdup (service), plugin);
  120. success = TRUE;
  121. }
  122. g_free (plug_name);
  123. g_free (plug_service);
  124. } else {
  125. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': %s",
  126. g_module_name (module), g_module_error ());
  127. }
  128. if (!success)
  129. g_module_close (module);
  130. } else {
  131. g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot locate nm_vpn_plugin_ui_factory() in '%s': %s",
  132. g_module_name (module), g_module_error ());
  133. g_module_close (module);
  134. }
  135. next:
  136. g_free (so_path);
  137. g_free (service);
  138. g_key_file_free (keyfile);
  139. g_free (path);
  140. }
  141. g_dir_close (dir);
  142. return plugins;
  143. }
  144. #if 0
  145. typedef struct {
  146. VpnImportSuccessCallback callback;
  147. gpointer user_data;
  148. } ActionInfo;
  149. static void
  150. import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
  151. {
  152. char *filename = NULL;
  153. ActionInfo *info = (ActionInfo *) user_data;
  154. GHashTableIter iter;
  155. gpointer key;
  156. NMVpnPluginUiInterface *plugin;
  157. NMConnection *connection = NULL;
  158. GError *error = NULL;
  159. if (response != GTK_RESPONSE_ACCEPT)
  160. goto out;
  161. filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
  162. if (!filename) {
  163. g_warning ("%s: didn't get a filename back from the chooser!", __func__);
  164. goto out;
  165. }
  166. g_hash_table_iter_init (&iter, plugins);
  167. while (!connection && g_hash_table_iter_next (&iter, &key, (gpointer *)&plugin)) {
  168. g_clear_error (&error);
  169. connection = nm_vpn_plugin_ui_interface_import (plugin, filename, &error);
  170. }
  171. if (connection)
  172. info->callback (connection, info->user_data);
  173. else {
  174. GtkWidget *err_dialog;
  175. char *bname = g_path_get_basename (filename);
  176. err_dialog = gtk_message_dialog_new (NULL,
  177. GTK_DIALOG_DESTROY_WITH_PARENT,
  178. GTK_MESSAGE_ERROR,
  179. GTK_BUTTONS_OK,
  180. _("Cannot import VPN connection"));
  181. gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog),
  182. _("The file '%s' could not be read or does not contain recognized VPN connection information\n\nError: %s."),
  183. bname, error ? error->message : "unknown error");
  184. g_free (bname);
  185. g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL);
  186. g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
  187. gtk_widget_show_all (err_dialog);
  188. gtk_window_present (GTK_WINDOW (err_dialog));
  189. }
  190. g_clear_error (&error);
  191. g_free (filename);
  192. out:
  193. gtk_widget_hide (dialog);
  194. gtk_widget_destroy (dialog);
  195. g_free (info);
  196. }
  197. static void
  198. destroy_import_chooser (GtkWidget *dialog, gpointer user_data)
  199. {
  200. g_free (user_data);
  201. gtk_widget_destroy (dialog);
  202. }
  203. void
  204. vpn_import (VpnImportSuccessCallback callback, gpointer user_data)
  205. {
  206. GtkWidget *dialog;
  207. ActionInfo *info;
  208. const char *home_folder;
  209. dialog = gtk_file_chooser_dialog_new (_("Select file to import"),
  210. NULL,
  211. GTK_FILE_CHOOSER_ACTION_OPEN,
  212. GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
  213. GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
  214. NULL);
  215. home_folder = g_get_home_dir ();
  216. gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder);
  217. info = g_malloc0 (sizeof (ActionInfo));
  218. info->callback = callback;
  219. info->user_data = user_data;
  220. g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (destroy_import_chooser), info);
  221. g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info);
  222. gtk_widget_show_all (dialog);
  223. gtk_window_present (GTK_WINDOW (dialog));
  224. }
  225. static void
  226. export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
  227. {
  228. NMConnection *connection = NM_CONNECTION (user_data);
  229. char *filename = NULL;
  230. GError *error = NULL;
  231. NMVpnPluginUiInterface *plugin;
  232. NMSettingConnection *s_con = NULL;
  233. NMSettingVPN *s_vpn = NULL;
  234. const char *service_type;
  235. const char *id = NULL;
  236. gboolean success = FALSE;
  237. if (response != GTK_RESPONSE_ACCEPT)
  238. goto out;
  239. filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
  240. if (!filename) {
  241. g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "no filename");
  242. goto done;
  243. }
  244. if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
  245. int replace_response;
  246. GtkWidget *replace_dialog;
  247. char *bname;
  248. bname = g_path_get_basename (filename);
  249. replace_dialog = gtk_message_dialog_new (NULL,
  250. GTK_DIALOG_DESTROY_WITH_PARENT,
  251. GTK_MESSAGE_QUESTION,
  252. GTK_BUTTONS_CANCEL,
  253. _("A file named \"%s\" already exists."),
  254. bname);
  255. gtk_dialog_add_buttons (GTK_DIALOG (replace_dialog), _("_Replace"), GTK_RESPONSE_OK, NULL);
  256. gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (replace_dialog),
  257. _("Do you want to replace %s with the VPN connection you are saving?"), bname);
  258. g_free (bname);
  259. replace_response = gtk_dialog_run (GTK_DIALOG (replace_dialog));
  260. gtk_widget_destroy (replace_dialog);
  261. if (replace_response != GTK_RESPONSE_OK)
  262. goto out;
  263. }
  264. s_con = nm_connection_get_setting_connection (connection);
  265. id = s_con ? nm_setting_connection_get_id (s_con) : NULL;
  266. if (!id) {
  267. g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "connection setting invalid");
  268. goto done;
  269. }
  270. s_vpn = nm_connection_get_setting_vpn (connection);
  271. service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL;
  272. if (!service_type) {
  273. g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "VPN setting invalid");
  274. goto done;
  275. }
  276. plugin = vpn_get_plugin_by_service (service_type);
  277. if (plugin)
  278. success = nm_vpn_plugin_ui_interface_export (plugin, filename, connection, &error);
  279. done:
  280. if (!success) {
  281. GtkWidget *err_dialog;
  282. char *bname = filename ? g_path_get_basename (filename) : g_strdup ("(none)");
  283. err_dialog = gtk_message_dialog_new (NULL,
  284. GTK_DIALOG_DESTROY_WITH_PARENT,
  285. GTK_MESSAGE_ERROR,
  286. GTK_BUTTONS_OK,
  287. _("Cannot export VPN connection"));
  288. gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog),
  289. _("The VPN connection '%s' could not be exported to %s.\n\nError: %s."),
  290. id ? id : "(unknown)", bname, error ? error->message : "unknown error");
  291. g_free (bname);
  292. g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL);
  293. g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
  294. gtk_widget_show_all (err_dialog);
  295. gtk_window_present (GTK_WINDOW (err_dialog));
  296. }
  297. out:
  298. if (error)
  299. g_error_free (error);
  300. g_object_unref (connection);
  301. gtk_widget_hide (dialog);
  302. gtk_widget_destroy (dialog);
  303. }
  304. void
  305. vpn_export (NMConnection *connection)
  306. {
  307. GtkWidget *dialog;
  308. NMVpnPluginUiInterface *plugin;
  309. NMSettingVPN *s_vpn = NULL;
  310. const char *service_type;
  311. const char *home_folder;
  312. s_vpn = nm_connection_get_setting_vpn (connection);
  313. service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL;
  314. if (!service_type) {
  315. g_warning ("%s: invalid VPN connection!", __func__);
  316. return;
  317. }
  318. dialog = gtk_file_chooser_dialog_new (_("Export VPN connection..."),
  319. NULL,
  320. GTK_FILE_CHOOSER_ACTION_SAVE,
  321. GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
  322. GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
  323. NULL);
  324. home_folder = g_get_home_dir ();
  325. gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder);
  326. plugin = vpn_get_plugin_by_service (service_type);
  327. if (plugin) {
  328. char *suggested = NULL;
  329. suggested = nm_vpn_plugin_ui_interface_get_suggested_name (plugin, connection);
  330. if (suggested) {
  331. gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested);
  332. g_free (suggested);
  333. }
  334. }
  335. g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (gtk_widget_destroy), NULL);
  336. g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection));
  337. gtk_widget_show_all (dialog);
  338. gtk_window_present (GTK_WINDOW (dialog));
  339. }
  340. #endif
  341. gboolean
  342. vpn_supports_ipv6 (NMConnection *connection)
  343. {
  344. NMSettingVPN *s_vpn;
  345. const char *service_type;
  346. NMVpnPluginUiInterface *plugin;
  347. guint32 capabilities;
  348. s_vpn = nm_connection_get_setting_vpn (connection);
  349. g_return_val_if_fail (s_vpn != NULL, FALSE);
  350. service_type = nm_setting_vpn_get_service_type (s_vpn);
  351. g_return_val_if_fail (service_type != NULL, FALSE);
  352. plugin = vpn_get_plugin_by_service (service_type);
  353. g_return_val_if_fail (plugin != NULL, FALSE);
  354. capabilities = nm_vpn_plugin_ui_interface_get_capabilities (plugin);
  355. return (capabilities & NM_VPN_PLUGIN_UI_CAPABILITY_IPV6) != 0;
  356. }