nm-openvpn.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /***************************************************************************
  3. * CVSID: $Id: nm-openvpn.c 4232 2008-10-29 09:13:40Z tambeti $
  4. *
  5. * nm-openvpn.c : GNOME UI dialogs for configuring openvpn VPN connections
  6. *
  7. * Copyright (C) 2005 Tim Niemueller <tim@niemueller.de>
  8. * Copyright (C) 2008 - 2010 Dan Williams, <dcbw@redhat.com>
  9. * Copyright (C) 2008 - 2011 Red Hat, Inc.
  10. * Based on work by David Zeuthen, <davidz@redhat.com>
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License along
  23. * with this program; if not, write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  25. *
  26. **************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. #include <config.h>
  29. #endif
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <errno.h>
  33. #include <stdlib.h>
  34. #include <glib/gi18n-lib.h>
  35. #include <string.h>
  36. #include <gtk/gtk.h>
  37. #define NM_VPN_API_SUBJECT_TO_CHANGE
  38. #include <nm-vpn-plugin-ui-interface.h>
  39. #include <nm-setting-vpn.h>
  40. #include <nm-setting-connection.h>
  41. #include <nm-setting-ip4-config.h>
  42. #include "src/nm-openvpn-service.h"
  43. #include "nm-openvpn.h"
  44. #include "auth-helpers.h"
  45. #include "import-export.h"
  46. #define OPENVPN_PLUGIN_NAME _("OpenVPN")
  47. #define OPENVPN_PLUGIN_DESC _("Compatible with the OpenVPN server.")
  48. #define OPENVPN_PLUGIN_SERVICE NM_DBUS_SERVICE_OPENVPN
  49. /************** plugin class **************/
  50. static void openvpn_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class);
  51. G_DEFINE_TYPE_EXTENDED (OpenvpnPluginUi, openvpn_plugin_ui, G_TYPE_OBJECT, 0,
  52. G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_INTERFACE,
  53. openvpn_plugin_ui_interface_init))
  54. /************** UI widget class **************/
  55. static void openvpn_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class);
  56. G_DEFINE_TYPE_EXTENDED (OpenvpnPluginUiWidget, openvpn_plugin_ui_widget, G_TYPE_OBJECT, 0,
  57. G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE,
  58. openvpn_plugin_ui_widget_interface_init))
  59. #define OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), OPENVPN_TYPE_PLUGIN_UI_WIDGET, OpenvpnPluginUiWidgetPrivate))
  60. typedef struct {
  61. GtkBuilder *builder;
  62. GtkWidget *widget;
  63. GtkSizeGroup *group;
  64. GtkWindowGroup *window_group;
  65. gboolean window_added;
  66. GHashTable *advanced;
  67. gboolean new_connection;
  68. } OpenvpnPluginUiWidgetPrivate;
  69. #define COL_AUTH_NAME 0
  70. #define COL_AUTH_PAGE 1
  71. #define COL_AUTH_TYPE 2
  72. GQuark
  73. openvpn_plugin_ui_error_quark (void)
  74. {
  75. static GQuark error_quark = 0;
  76. if (G_UNLIKELY (error_quark == 0))
  77. error_quark = g_quark_from_static_string ("openvpn-plugin-ui-error-quark");
  78. return error_quark;
  79. }
  80. /* This should really be standard. */
  81. #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
  82. GType
  83. openvpn_plugin_ui_error_get_type (void)
  84. {
  85. static GType etype = 0;
  86. if (etype == 0) {
  87. static const GEnumValue values[] = {
  88. /* Unknown error. */
  89. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_UNKNOWN, "UnknownError"),
  90. /* The connection was missing invalid. */
  91. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_INVALID_CONNECTION, "InvalidConnection"),
  92. /* The specified property was invalid. */
  93. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY, "InvalidProperty"),
  94. /* The specified property was missing and is required. */
  95. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_MISSING_PROPERTY, "MissingProperty"),
  96. /* The file to import could not be read. */
  97. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_READABLE, "FileNotReadable"),
  98. /* The file to import could was not an OpenVPN client file. */
  99. ENUM_ENTRY (OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN, "FileNotOpenVPN"),
  100. { 0, 0, 0 }
  101. };
  102. etype = g_enum_register_static ("OpenvpnPluginUiError", values);
  103. }
  104. return etype;
  105. }
  106. /* Example: abc.com:1234:udp, ovpnserver.company.com:443, vpn.example.com::tcp */
  107. static gboolean
  108. check_gateway_entry (const char *str)
  109. {
  110. char **list, **iter;
  111. char *host, *port, *proto;
  112. long int port_int;
  113. gboolean success = FALSE;
  114. if (!str || !*str)
  115. return FALSE;
  116. list = g_strsplit_set (str, " \t,", -1);
  117. for (iter = list; iter && *iter; iter++) {
  118. if (!**iter)
  119. continue;
  120. host = g_strstrip (*iter);
  121. port = strchr (host, ':');
  122. proto = port ? strchr (port + 1, ':') : NULL;
  123. if (port)
  124. *port++ = '\0';
  125. if (proto)
  126. *proto++ = '\0';
  127. /* check hostname */
  128. if (!host || !*host)
  129. goto out;
  130. /* check port */
  131. if (port && *port) {
  132. char *end;
  133. errno = 0;
  134. port_int = strtol (port, &end, 10);
  135. if (errno != 0 || *end != '\0' || port_int < 1 || port_int > 65535)
  136. goto out;
  137. }
  138. /* check proto */
  139. if (proto && strcmp (proto, "udp") && strcmp (proto, "tcp"))
  140. goto out;
  141. }
  142. success = TRUE;
  143. out:
  144. g_strfreev (list);
  145. return success;
  146. }
  147. static gboolean
  148. check_validity (OpenvpnPluginUiWidget *self, GError **error)
  149. {
  150. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  151. GtkWidget *widget;
  152. const char *str;
  153. GtkTreeModel *model;
  154. GtkTreeIter iter;
  155. const char *contype = NULL;
  156. GdkRGBA rgba;
  157. gboolean gateway_valid;
  158. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  159. str = gtk_entry_get_text (GTK_ENTRY (widget));
  160. gateway_valid = check_gateway_entry (str);
  161. /* Change entry background colour while editing */
  162. if (!gateway_valid)
  163. gdk_rgba_parse (&rgba, "red3");
  164. gtk_widget_override_background_color (widget, GTK_STATE_NORMAL, !gateway_valid ? &rgba : NULL);
  165. if (!gateway_valid) {
  166. g_set_error (error,
  167. OPENVPN_PLUGIN_UI_ERROR,
  168. OPENVPN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
  169. NM_OPENVPN_KEY_REMOTE);
  170. return FALSE;
  171. }
  172. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_combo"));
  173. model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
  174. g_assert (model);
  175. g_assert (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter));
  176. gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &contype, -1);
  177. if (!auth_widget_check_validity (priv->builder, contype, error))
  178. return FALSE;
  179. return TRUE;
  180. }
  181. static void
  182. stuff_changed_cb (GtkWidget *widget, gpointer user_data)
  183. {
  184. g_signal_emit_by_name (OPENVPN_PLUGIN_UI_WIDGET (user_data), "changed");
  185. }
  186. static void
  187. auth_combo_changed_cb (GtkWidget *combo, gpointer user_data)
  188. {
  189. OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
  190. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  191. GtkWidget *auth_notebook;
  192. GtkWidget *show_passwords;
  193. GtkTreeModel *model;
  194. GtkTreeIter iter;
  195. gint new_page = 0;
  196. auth_notebook = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_notebook"));
  197. g_assert (auth_notebook);
  198. show_passwords = GTK_WIDGET (gtk_builder_get_object (priv->builder, "show_passwords"));
  199. g_assert (auth_notebook);
  200. model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
  201. g_assert (model);
  202. g_assert (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter));
  203. gtk_tree_model_get (model, &iter, COL_AUTH_PAGE, &new_page, -1);
  204. /* Static key page doesn't have any passwords */
  205. gtk_widget_set_sensitive (show_passwords, new_page != 3);
  206. gtk_notebook_set_current_page (GTK_NOTEBOOK (auth_notebook), new_page);
  207. stuff_changed_cb (combo, self);
  208. }
  209. static void
  210. advanced_dialog_close_cb (GtkWidget *dialog, gpointer user_data)
  211. {
  212. gtk_widget_hide (dialog);
  213. /* gtk_widget_destroy() will remove the window from the window group */
  214. gtk_widget_destroy (dialog);
  215. }
  216. static void
  217. advanced_dialog_response_cb (GtkWidget *dialog, gint response, gpointer user_data)
  218. {
  219. OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
  220. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  221. GError *error = NULL;
  222. if (response != GTK_RESPONSE_OK) {
  223. advanced_dialog_close_cb (dialog, self);
  224. return;
  225. }
  226. if (priv->advanced)
  227. g_hash_table_destroy (priv->advanced);
  228. priv->advanced = advanced_dialog_new_hash_from_dialog (dialog, &error);
  229. if (!priv->advanced) {
  230. g_message ("%s: error reading advanced settings: %s", __func__, error->message);
  231. g_error_free (error);
  232. }
  233. advanced_dialog_close_cb (dialog, self);
  234. stuff_changed_cb (NULL, self);
  235. }
  236. static void
  237. advanced_button_clicked_cb (GtkWidget *button, gpointer user_data)
  238. {
  239. OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (user_data);
  240. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  241. GtkWidget *dialog, *toplevel, *widget;
  242. GtkTreeModel *model;
  243. GtkTreeIter iter;
  244. const char *contype = NULL;
  245. toplevel = gtk_widget_get_toplevel (priv->widget);
  246. g_return_if_fail (gtk_widget_is_toplevel (toplevel));
  247. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_combo"));
  248. model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
  249. if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
  250. gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &contype, -1);
  251. dialog = advanced_dialog_new (priv->advanced, contype);
  252. if (!dialog) {
  253. g_warning ("%s: failed to create the Advanced dialog!", __func__);
  254. return;
  255. }
  256. gtk_window_group_add_window (priv->window_group, GTK_WINDOW (dialog));
  257. if (!priv->window_added) {
  258. gtk_window_group_add_window (priv->window_group, GTK_WINDOW (toplevel));
  259. priv->window_added = TRUE;
  260. }
  261. gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
  262. g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (advanced_dialog_response_cb), self);
  263. g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (advanced_dialog_close_cb), self);
  264. gtk_widget_show_all (dialog);
  265. }
  266. static gboolean
  267. init_plugin_ui (OpenvpnPluginUiWidget *self, NMConnection *connection, GError **error)
  268. {
  269. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  270. NMSettingVPN *s_vpn;
  271. GtkWidget *widget;
  272. GtkListStore *store;
  273. GtkTreeIter iter;
  274. int active = -1;
  275. const char *value;
  276. const char *contype = NM_OPENVPN_CONTYPE_TLS;
  277. s_vpn = nm_connection_get_setting_vpn (connection);
  278. priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  279. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  280. g_return_val_if_fail (widget != NULL, FALSE);
  281. gtk_size_group_add_widget (priv->group, widget);
  282. if (s_vpn) {
  283. value = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE);
  284. if (value)
  285. gtk_entry_set_text (GTK_ENTRY (widget), value);
  286. }
  287. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  288. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_combo"));
  289. g_return_val_if_fail (widget != NULL, FALSE);
  290. gtk_size_group_add_widget (priv->group, widget);
  291. store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
  292. if (s_vpn) {
  293. contype = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE);
  294. if (contype) {
  295. if ( strcmp (contype, NM_OPENVPN_CONTYPE_TLS)
  296. && strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY)
  297. && strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)
  298. && strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
  299. contype = NM_OPENVPN_CONTYPE_TLS;
  300. } else
  301. contype = NM_OPENVPN_CONTYPE_TLS;
  302. }
  303. /* TLS auth widget */
  304. tls_pw_init_auth_widget (priv->builder, priv->group, s_vpn,
  305. NM_OPENVPN_CONTYPE_TLS, "tls",
  306. stuff_changed_cb, self);
  307. gtk_list_store_append (store, &iter);
  308. gtk_list_store_set (store, &iter,
  309. COL_AUTH_NAME, _("Certificates (TLS)"),
  310. COL_AUTH_PAGE, 0,
  311. COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_TLS,
  312. -1);
  313. /* Password auth widget */
  314. tls_pw_init_auth_widget (priv->builder, priv->group, s_vpn,
  315. NM_OPENVPN_CONTYPE_PASSWORD, "pw",
  316. stuff_changed_cb, self);
  317. gtk_list_store_append (store, &iter);
  318. gtk_list_store_set (store, &iter,
  319. COL_AUTH_NAME, _("Password"),
  320. COL_AUTH_PAGE, 1,
  321. COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_PASSWORD,
  322. -1);
  323. if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD))
  324. active = 1;
  325. /* Password+TLS auth widget */
  326. tls_pw_init_auth_widget (priv->builder, priv->group, s_vpn,
  327. NM_OPENVPN_CONTYPE_PASSWORD_TLS, "pw_tls",
  328. stuff_changed_cb, self);
  329. gtk_list_store_append (store, &iter);
  330. gtk_list_store_set (store, &iter,
  331. COL_AUTH_NAME, _("Password with Certificates (TLS)"),
  332. COL_AUTH_PAGE, 2,
  333. COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_PASSWORD_TLS,
  334. -1);
  335. if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS))
  336. active = 2;
  337. /* Static key auth widget */
  338. sk_init_auth_widget (priv->builder, priv->group, s_vpn, stuff_changed_cb, self);
  339. gtk_list_store_append (store, &iter);
  340. gtk_list_store_set (store, &iter,
  341. COL_AUTH_NAME, _("Static Key"),
  342. COL_AUTH_PAGE, 3,
  343. COL_AUTH_TYPE, NM_OPENVPN_CONTYPE_STATIC_KEY,
  344. -1);
  345. if ((active < 0) && !strcmp (contype, NM_OPENVPN_CONTYPE_STATIC_KEY))
  346. active = 3;
  347. gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
  348. g_object_unref (store);
  349. g_signal_connect (widget, "changed", G_CALLBACK (auth_combo_changed_cb), self);
  350. gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? 0 : active);
  351. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "advanced_button"));
  352. g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (advanced_button_clicked_cb), self);
  353. return TRUE;
  354. }
  355. static GObject *
  356. get_widget (NMVpnPluginUiWidgetInterface *iface)
  357. {
  358. OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (iface);
  359. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  360. return G_OBJECT (priv->widget);
  361. }
  362. static void
  363. hash_copy_advanced (gpointer key, gpointer data, gpointer user_data)
  364. {
  365. NMSettingVPN *s_vpn = NM_SETTING_VPN (user_data);
  366. const char *value = (const char *) data;
  367. g_return_if_fail (value && strlen (value));
  368. /* HTTP Proxy password is a secret, not a data item */
  369. if (!strcmp (key, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD))
  370. nm_setting_vpn_add_secret (s_vpn, (const char *) key, value);
  371. else
  372. nm_setting_vpn_add_data_item (s_vpn, (const char *) key, value);
  373. }
  374. static char *
  375. get_auth_type (GtkBuilder *builder)
  376. {
  377. GtkComboBox *combo;
  378. GtkTreeModel *model;
  379. GtkTreeIter iter;
  380. char *auth_type = NULL;
  381. gboolean success;
  382. combo = GTK_COMBO_BOX (GTK_WIDGET (gtk_builder_get_object (builder, "auth_combo")));
  383. model = gtk_combo_box_get_model (combo);
  384. success = gtk_combo_box_get_active_iter (combo, &iter);
  385. g_return_val_if_fail (success == TRUE, NULL);
  386. gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &auth_type, -1);
  387. return auth_type;
  388. }
  389. static gboolean
  390. update_connection (NMVpnPluginUiWidgetInterface *iface,
  391. NMConnection *connection,
  392. GError **error)
  393. {
  394. OpenvpnPluginUiWidget *self = OPENVPN_PLUGIN_UI_WIDGET (iface);
  395. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  396. NMSettingVPN *s_vpn;
  397. GtkWidget *widget;
  398. char *str, *auth_type;
  399. gboolean valid = FALSE;
  400. if (!check_validity (self, error))
  401. return FALSE;
  402. s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
  403. g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_DBUS_SERVICE_OPENVPN, NULL);
  404. /* Gateway */
  405. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  406. str = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
  407. if (str && strlen (str))
  408. nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_REMOTE, str);
  409. auth_type = get_auth_type (priv->builder);
  410. if (auth_type) {
  411. nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE, auth_type);
  412. auth_widget_update_connection (priv->builder, auth_type, s_vpn);
  413. g_free (auth_type);
  414. }
  415. if (priv->advanced)
  416. g_hash_table_foreach (priv->advanced, hash_copy_advanced, s_vpn);
  417. /* Default to agent-owned secrets for new connections */
  418. if (priv->new_connection) {
  419. if (nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD)) {
  420. nm_setting_set_secret_flags (NM_SETTING (s_vpn),
  421. NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD,
  422. NM_SETTING_SECRET_FLAG_AGENT_OWNED,
  423. NULL);
  424. }
  425. if (nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD)) {
  426. nm_setting_set_secret_flags (NM_SETTING (s_vpn),
  427. NM_OPENVPN_KEY_PASSWORD,
  428. NM_SETTING_SECRET_FLAG_AGENT_OWNED,
  429. NULL);
  430. }
  431. if (nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS)) {
  432. nm_setting_set_secret_flags (NM_SETTING (s_vpn),
  433. NM_OPENVPN_KEY_CERTPASS,
  434. NM_SETTING_SECRET_FLAG_AGENT_OWNED,
  435. NULL);
  436. }
  437. }
  438. nm_connection_add_setting (connection, NM_SETTING (s_vpn));
  439. valid = TRUE;
  440. return valid;
  441. }
  442. static void
  443. is_new_func (const char *key, const char *value, gpointer user_data)
  444. {
  445. gboolean *is_new = user_data;
  446. /* If there are any VPN data items the connection isn't new */
  447. *is_new = FALSE;
  448. }
  449. static NMVpnPluginUiWidgetInterface *
  450. nm_vpn_plugin_ui_widget_interface_new (NMConnection *connection, GError **error)
  451. {
  452. NMVpnPluginUiWidgetInterface *object;
  453. OpenvpnPluginUiWidgetPrivate *priv;
  454. char *ui_file;
  455. gboolean new = TRUE;
  456. NMSettingVPN *s_vpn;
  457. if (error)
  458. g_return_val_if_fail (*error == NULL, NULL);
  459. object = NM_VPN_PLUGIN_UI_WIDGET_INTERFACE (g_object_new (OPENVPN_TYPE_PLUGIN_UI_WIDGET, NULL));
  460. if (!object) {
  461. g_set_error_literal (error, OPENVPN_PLUGIN_UI_ERROR, 0, _("could not create openvpn object"));
  462. return NULL;
  463. }
  464. priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (object);
  465. ui_file = g_strdup_printf ("%s/%s", UIDIR, "nm-openvpn-dialog.ui");
  466. priv->builder = gtk_builder_new ();
  467. gtk_builder_set_translation_domain (priv->builder, GETTEXT_PACKAGE);
  468. if (!gtk_builder_add_from_file (priv->builder, ui_file, error)) {
  469. g_warning ("Couldn't load builder file: %s",
  470. error && *error ? (*error)->message : "(unknown)");
  471. g_clear_error (error);
  472. g_set_error (error, OPENVPN_PLUGIN_UI_ERROR, 0,
  473. "could not load required resources from %s", ui_file);
  474. g_free (ui_file);
  475. g_object_unref (object);
  476. return NULL;
  477. }
  478. g_free (ui_file);
  479. priv->widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "openvpn-vbox"));
  480. if (!priv->widget) {
  481. g_set_error_literal (error, OPENVPN_PLUGIN_UI_ERROR, 0, _("could not load UI widget"));
  482. g_object_unref (object);
  483. return NULL;
  484. }
  485. g_object_ref_sink (priv->widget);
  486. priv->window_group = gtk_window_group_new ();
  487. s_vpn = nm_connection_get_setting_vpn (connection);
  488. if (s_vpn)
  489. nm_setting_vpn_foreach_data_item (s_vpn, is_new_func, &new);
  490. priv->new_connection = new;
  491. if (!init_plugin_ui (OPENVPN_PLUGIN_UI_WIDGET (object), connection, error)) {
  492. g_object_unref (object);
  493. return NULL;
  494. }
  495. priv->advanced = advanced_dialog_new_hash_from_connection (connection, error);
  496. if (!priv->advanced) {
  497. g_object_unref (object);
  498. return NULL;
  499. }
  500. return object;
  501. }
  502. static void
  503. dispose (GObject *object)
  504. {
  505. OpenvpnPluginUiWidget *plugin = OPENVPN_PLUGIN_UI_WIDGET (object);
  506. OpenvpnPluginUiWidgetPrivate *priv = OPENVPN_PLUGIN_UI_WIDGET_GET_PRIVATE (plugin);
  507. if (priv->group)
  508. g_object_unref (priv->group);
  509. if (priv->window_group)
  510. g_object_unref (priv->window_group);
  511. if (priv->widget)
  512. g_object_unref (priv->widget);
  513. if (priv->builder)
  514. g_object_unref (priv->builder);
  515. if (priv->advanced)
  516. g_hash_table_destroy (priv->advanced);
  517. G_OBJECT_CLASS (openvpn_plugin_ui_widget_parent_class)->dispose (object);
  518. }
  519. static void
  520. openvpn_plugin_ui_widget_class_init (OpenvpnPluginUiWidgetClass *req_class)
  521. {
  522. GObjectClass *object_class = G_OBJECT_CLASS (req_class);
  523. g_type_class_add_private (req_class, sizeof (OpenvpnPluginUiWidgetPrivate));
  524. object_class->dispose = dispose;
  525. }
  526. static void
  527. openvpn_plugin_ui_widget_init (OpenvpnPluginUiWidget *plugin)
  528. {
  529. }
  530. static void
  531. openvpn_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class)
  532. {
  533. /* interface implementation */
  534. iface_class->get_widget = get_widget;
  535. iface_class->update_connection = update_connection;
  536. }
  537. static NMConnection *
  538. import (NMVpnPluginUiInterface *iface, const char *path, GError **error)
  539. {
  540. NMConnection *connection = NULL;
  541. char *contents = NULL;
  542. char **lines = NULL;
  543. char *ext;
  544. ext = strrchr (path, '.');
  545. if (!ext || ( !g_str_has_suffix (ext, ".ovpn")
  546. && !g_str_has_suffix (ext, ".conf")
  547. && !g_str_has_suffix (ext, ".cnf")
  548. && !g_str_has_suffix (ext, ".ovpntest"))) { /* Special extension for testcases */
  549. g_set_error_literal (error,
  550. OPENVPN_PLUGIN_UI_ERROR,
  551. OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_OPENVPN,
  552. _("unknown OpenVPN file extension"));
  553. goto out;
  554. }
  555. if (!g_file_get_contents (path, &contents, NULL, error))
  556. return NULL;
  557. if (!g_utf8_validate (contents, -1, NULL)) {
  558. char *tmp;
  559. GError *conv_error = NULL;
  560. tmp = g_locale_to_utf8 (contents, -1, NULL, NULL, &conv_error);
  561. if (conv_error) {
  562. /* ignore the error, we tried at least. */
  563. g_error_free (conv_error);
  564. g_free (tmp);
  565. } else {
  566. g_assert (tmp);
  567. g_free (contents);
  568. contents = tmp; /* update contents with the UTF-8 safe text */
  569. }
  570. }
  571. lines = g_strsplit_set (contents, "\r\n", 0);
  572. if (g_strv_length (lines) <= 1) {
  573. g_set_error_literal (error,
  574. OPENVPN_PLUGIN_UI_ERROR,
  575. OPENVPN_PLUGIN_UI_ERROR_FILE_NOT_READABLE,
  576. _("not a valid OpenVPN configuration file"));
  577. goto out;
  578. }
  579. connection = do_import (path, lines, error);
  580. out:
  581. if (lines)
  582. g_strfreev (lines);
  583. g_free (contents);
  584. return connection;
  585. }
  586. static gboolean
  587. export (NMVpnPluginUiInterface *iface,
  588. const char *path,
  589. NMConnection *connection,
  590. GError **error)
  591. {
  592. return do_export (path, connection, error);
  593. }
  594. static char *
  595. get_suggested_name (NMVpnPluginUiInterface *iface, NMConnection *connection)
  596. {
  597. NMSettingConnection *s_con;
  598. const char *id;
  599. g_return_val_if_fail (connection != NULL, NULL);
  600. s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
  601. g_return_val_if_fail (s_con != NULL, NULL);
  602. id = nm_setting_connection_get_id (s_con);
  603. g_return_val_if_fail (id != NULL, NULL);
  604. return g_strdup_printf ("%s (openvpn).conf", id);
  605. }
  606. static guint32
  607. get_capabilities (NMVpnPluginUiInterface *iface)
  608. {
  609. return (NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT |
  610. NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT |
  611. NM_VPN_PLUGIN_UI_CAPABILITY_IPV6);
  612. }
  613. static NMVpnPluginUiWidgetInterface *
  614. ui_factory (NMVpnPluginUiInterface *iface, NMConnection *connection, GError **error)
  615. {
  616. return nm_vpn_plugin_ui_widget_interface_new (connection, error);
  617. }
  618. static void
  619. get_property (GObject *object, guint prop_id,
  620. GValue *value, GParamSpec *pspec)
  621. {
  622. switch (prop_id) {
  623. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME:
  624. g_value_set_string (value, OPENVPN_PLUGIN_NAME);
  625. break;
  626. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC:
  627. g_value_set_string (value, OPENVPN_PLUGIN_DESC);
  628. break;
  629. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE:
  630. g_value_set_string (value, OPENVPN_PLUGIN_SERVICE);
  631. break;
  632. default:
  633. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  634. break;
  635. }
  636. }
  637. static void
  638. openvpn_plugin_ui_class_init (OpenvpnPluginUiClass *req_class)
  639. {
  640. GObjectClass *object_class = G_OBJECT_CLASS (req_class);
  641. object_class->get_property = get_property;
  642. g_object_class_override_property (object_class,
  643. NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME,
  644. NM_VPN_PLUGIN_UI_INTERFACE_NAME);
  645. g_object_class_override_property (object_class,
  646. NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC,
  647. NM_VPN_PLUGIN_UI_INTERFACE_DESC);
  648. g_object_class_override_property (object_class,
  649. NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE,
  650. NM_VPN_PLUGIN_UI_INTERFACE_SERVICE);
  651. }
  652. static void
  653. openvpn_plugin_ui_init (OpenvpnPluginUi *plugin)
  654. {
  655. }
  656. static void
  657. openvpn_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class)
  658. {
  659. /* interface implementation */
  660. iface_class->ui_factory = ui_factory;
  661. iface_class->get_capabilities = get_capabilities;
  662. iface_class->import_from_file = import;
  663. iface_class->export_to_file = export;
  664. iface_class->get_suggested_name = get_suggested_name;
  665. }
  666. G_MODULE_EXPORT NMVpnPluginUiInterface *
  667. nm_vpn_plugin_ui_factory (GError **error)
  668. {
  669. if (error)
  670. g_return_val_if_fail (*error == NULL, NULL);
  671. bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  672. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  673. return NM_VPN_PLUGIN_UI_INTERFACE (g_object_new (OPENVPN_TYPE_PLUGIN_UI, NULL));
  674. }