nm-ssh.c 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /***************************************************************************
  3. * CVSID: $Id: nm-ssh.c 4232 2008-10-29 09:13:40Z tambeti $
  4. *
  5. * nm-ssh.c : GNOME UI dialogs for configuring ssh VPN connections
  6. *
  7. * Copyright (C) 2013 Dan Fruehauf, <malkodan@gmail.com>
  8. * Based on work by Dan Williams, <dcbw@redhat.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License along
  21. * with this program; if not, write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23. *
  24. **************************************************************************/
  25. #ifdef HAVE_CONFIG_H
  26. #include <config.h>
  27. #endif
  28. #include <netinet/in.h>
  29. #include <arpa/inet.h>
  30. #include <errno.h>
  31. #include <stdlib.h>
  32. #include <glib/gi18n-lib.h>
  33. #include <string.h>
  34. #include <gtk/gtk.h>
  35. #define NM_VPN_API_SUBJECT_TO_CHANGE
  36. #include <nm-vpn-plugin-ui-interface.h>
  37. #include <nm-setting-vpn.h>
  38. #include <nm-setting-connection.h>
  39. #include <nm-setting-ip4-config.h>
  40. #include "src/nm-ssh-service.h"
  41. #include "nm-ssh.h"
  42. #include "advanced-dialog.h"
  43. #define SSH_PLUGIN_NAME _("SSH")
  44. #define SSH_PLUGIN_DESC _("Compatible with the SSH server.")
  45. #define SSH_PLUGIN_SERVICE NM_DBUS_SERVICE_SSH
  46. #define PW_TYPE_SAVE 0
  47. #define PW_TYPE_ASK 1
  48. #define PARSE_IMPORT_KEY(IMPORT_KEY, NM_SSH_KEY, ITEMS, VPN_CONN) \
  49. if (!strncmp (ITEMS[0], IMPORT_KEY, strlen (ITEMS[0]))) { \
  50. nm_setting_vpn_add_data_item (VPN_CONN, NM_SSH_KEY, ITEMS[1]); \
  51. g_free(ITEMS); \
  52. continue; \
  53. }
  54. #define PARSE_IMPORT_KEY_BOOL(IMPORT_KEY, NM_SSH_KEY, ITEMS, VPN_CONN, VALUE) \
  55. if (!strncmp (ITEMS[0], IMPORT_KEY, strlen (ITEMS[0]))) { \
  56. if (!strncmp(ITEMS[1], VALUE, strlen(ITEMS[1]))) { \
  57. g_message("%s=%s", NM_SSH_KEY, ITEMS[1]); \
  58. nm_setting_vpn_add_data_item (VPN_CONN, NM_SSH_KEY, YES); \
  59. } \
  60. g_free (ITEMS); \
  61. continue; \
  62. }
  63. #define PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_STR(IMPORT_KEY, NM_SSH_KEY, ITEMS, VPN_CONN, DEFAULT_VALUE) \
  64. if (!strncmp (ITEMS[0], IMPORT_KEY, strlen (ITEMS[0]))) { \
  65. if (strncmp(ITEMS[1], DEFAULT_VALUE, strlen(ITEMS[1]))) \
  66. nm_setting_vpn_add_data_item (VPN_CONN, NM_SSH_KEY, ITEMS[1]); \
  67. g_free (ITEMS); \
  68. continue; \
  69. }
  70. #define PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_INT(IMPORT_KEY, NM_SSH_KEY, ITEMS, VPN_CONN, DEFAULT_VALUE) \
  71. if (!strncmp (ITEMS[0], IMPORT_KEY, strlen (ITEMS[0]))) { \
  72. char *tmp = g_strdup_printf("%d", DEFAULT_VALUE); \
  73. if (strncmp(ITEMS[1], tmp, strlen(ITEMS[1]))) \
  74. nm_setting_vpn_add_data_item (VPN_CONN, NM_SSH_KEY, ITEMS[1]); \
  75. g_free (ITEMS); \
  76. g_free (tmp); \
  77. continue; \
  78. }
  79. /************** plugin class **************/
  80. static void ssh_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class);
  81. G_DEFINE_TYPE_EXTENDED (SshPluginUi, ssh_plugin_ui, G_TYPE_OBJECT, 0,
  82. G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_INTERFACE,
  83. ssh_plugin_ui_interface_init))
  84. /************** UI widget class **************/
  85. static void ssh_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class);
  86. G_DEFINE_TYPE_EXTENDED (SshPluginUiWidget, ssh_plugin_ui_widget, G_TYPE_OBJECT, 0,
  87. G_IMPLEMENT_INTERFACE (NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE,
  88. ssh_plugin_ui_widget_interface_init))
  89. #define SSH_PLUGIN_UI_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SSH_TYPE_PLUGIN_UI_WIDGET, SshPluginUiWidgetPrivate))
  90. typedef struct {
  91. GtkBuilder *builder;
  92. GtkWidget *widget;
  93. GtkSizeGroup *group;
  94. GtkWindowGroup *window_group;
  95. gboolean window_added;
  96. GHashTable *advanced;
  97. gboolean new_connection;
  98. } SshPluginUiWidgetPrivate;
  99. #define COL_AUTH_NAME 0
  100. #define COL_AUTH_PAGE 1
  101. #define COL_AUTH_TYPE 2
  102. GQuark
  103. ssh_plugin_ui_error_quark (void)
  104. {
  105. static GQuark error_quark = 0;
  106. if (G_UNLIKELY (error_quark == 0))
  107. error_quark = g_quark_from_static_string ("ssh-plugin-ui-error-quark");
  108. return error_quark;
  109. }
  110. /* This should really be standard. */
  111. #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
  112. GType
  113. ssh_plugin_ui_error_get_type (void)
  114. {
  115. static GType etype = 0;
  116. if (etype == 0) {
  117. static const GEnumValue values[] = {
  118. /* Unknown error. */
  119. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_UNKNOWN, "UnknownError"),
  120. /* The connection was missing invalid. */
  121. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_INVALID_CONNECTION, "InvalidConnection"),
  122. /* The specified property was invalid. */
  123. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_INVALID_PROPERTY, "InvalidProperty"),
  124. /* The specified property was missing and is required. */
  125. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_MISSING_PROPERTY, "MissingProperty"),
  126. /* The file to import could not be read. */
  127. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_FILE_NOT_READABLE, "FileNotReadable"),
  128. /* The file to import could was not an SSH client file. */
  129. ENUM_ENTRY (SSH_PLUGIN_UI_ERROR_FILE_NOT_SSH, "FileNotSSH"),
  130. { 0, 0, 0 }
  131. };
  132. etype = g_enum_register_static ("SshPluginUiError", values);
  133. }
  134. return etype;
  135. }
  136. static gboolean
  137. check_validity (SshPluginUiWidget *self, GError **error)
  138. {
  139. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  140. GtkWidget *widget;
  141. const char *str;
  142. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  143. str = gtk_entry_get_text (GTK_ENTRY (widget));
  144. if (!str || !strlen (str)) {
  145. g_set_error (error,
  146. SSH_PLUGIN_UI_ERROR,
  147. SSH_PLUGIN_UI_ERROR_INVALID_PROPERTY,
  148. NM_SSH_KEY_REMOTE);
  149. return FALSE;
  150. }
  151. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "remote_ip_entry"));
  152. str = gtk_entry_get_text (GTK_ENTRY (widget));
  153. if (!str || !strlen (str)) {
  154. g_set_error (error,
  155. SSH_PLUGIN_UI_ERROR,
  156. SSH_PLUGIN_UI_ERROR_INVALID_PROPERTY,
  157. NM_SSH_KEY_REMOTE_IP);
  158. return FALSE;
  159. }
  160. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "local_ip_entry"));
  161. str = gtk_entry_get_text (GTK_ENTRY (widget));
  162. if (!str || !strlen (str)) {
  163. g_set_error (error,
  164. SSH_PLUGIN_UI_ERROR,
  165. SSH_PLUGIN_UI_ERROR_INVALID_PROPERTY,
  166. NM_SSH_KEY_LOCAL_IP);
  167. return FALSE;
  168. }
  169. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "netmask_entry"));
  170. str = gtk_entry_get_text (GTK_ENTRY (widget));
  171. if (!str || !strlen (str)) {
  172. g_set_error (error,
  173. SSH_PLUGIN_UI_ERROR,
  174. SSH_PLUGIN_UI_ERROR_INVALID_PROPERTY,
  175. NM_SSH_KEY_NETMASK);
  176. return FALSE;
  177. }
  178. return TRUE;
  179. }
  180. static void
  181. show_password_toggled (GtkToggleButton *togglebutton, GtkEntry *password_entry)
  182. {
  183. gtk_entry_set_visibility (password_entry, gtk_toggle_button_get_active (togglebutton));
  184. }
  185. static void
  186. stuff_changed_cb (GtkWidget *widget, gpointer user_data)
  187. {
  188. g_signal_emit_by_name (SSH_PLUGIN_UI_WIDGET (user_data), "changed");
  189. }
  190. static void
  191. auth_combo_changed_cb (GtkWidget *combo, gpointer user_data)
  192. {
  193. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (user_data);
  194. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  195. GtkWidget *auth_notebook;
  196. GtkWidget *show_password;
  197. GtkWidget *file_chooser;
  198. GtkTreeModel *model;
  199. GtkTreeIter iter;
  200. gint new_page = 0;
  201. auth_notebook = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_notebook"));
  202. g_assert (auth_notebook);
  203. show_password = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_password_show_password_checkbutton"));
  204. g_assert (show_password);
  205. file_chooser = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_keyfile_filechooserbutton"));
  206. g_assert (file_chooser);
  207. model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
  208. g_assert (model);
  209. g_assert (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter));
  210. gtk_tree_model_get (model, &iter, COL_AUTH_PAGE, &new_page, -1);
  211. /* Password entry relevant only to password page (1) */
  212. gtk_widget_set_sensitive (show_password, new_page == 1);
  213. /* Key file entry relevant only to key page (2) */
  214. gtk_widget_set_sensitive (file_chooser, new_page == 2);
  215. gtk_notebook_set_current_page (GTK_NOTEBOOK (auth_notebook), new_page);
  216. stuff_changed_cb (combo, self);
  217. }
  218. static void
  219. advanced_dialog_close_cb (GtkWidget *dialog, gpointer user_data)
  220. {
  221. gtk_widget_hide (dialog);
  222. /* gtk_widget_destroy() will remove the window from the window group */
  223. gtk_widget_destroy (dialog);
  224. }
  225. static void
  226. advanced_dialog_response_cb (GtkWidget *dialog, gint response, gpointer user_data)
  227. {
  228. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (user_data);
  229. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  230. GError *error = NULL;
  231. if (response != GTK_RESPONSE_OK) {
  232. advanced_dialog_close_cb (dialog, self);
  233. return;
  234. }
  235. if (priv->advanced)
  236. g_hash_table_destroy (priv->advanced);
  237. priv->advanced = advanced_dialog_new_hash_from_dialog (dialog, &error);
  238. if (!priv->advanced) {
  239. g_message ("%s: error reading advanced settings: %s", __func__, error->message);
  240. g_error_free (error);
  241. }
  242. advanced_dialog_close_cb (dialog, self);
  243. stuff_changed_cb (NULL, self);
  244. }
  245. static void
  246. advanced_button_clicked_cb (GtkWidget *button, gpointer user_data)
  247. {
  248. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (user_data);
  249. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  250. GtkWidget *dialog, *toplevel;
  251. toplevel = gtk_widget_get_toplevel (priv->widget);
  252. g_return_if_fail (gtk_widget_is_toplevel (toplevel));
  253. dialog = advanced_dialog_new (priv->advanced);
  254. if (!dialog) {
  255. g_warning ("%s: failed to create the Advanced dialog!", __func__);
  256. return;
  257. }
  258. gtk_window_group_add_window (priv->window_group, GTK_WINDOW (dialog));
  259. if (!priv->window_added) {
  260. gtk_window_group_add_window (priv->window_group, GTK_WINDOW (toplevel));
  261. priv->window_added = TRUE;
  262. }
  263. gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
  264. g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (advanced_dialog_response_cb), self);
  265. g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (advanced_dialog_close_cb), self);
  266. gtk_widget_show_all (dialog);
  267. }
  268. static void
  269. ipv6_toggled_cb (GtkWidget *check, gpointer user_data)
  270. {
  271. GtkBuilder *builder = (GtkBuilder *) user_data;
  272. GtkWidget *widget;
  273. widget = GTK_WIDGET (gtk_builder_get_object (builder, "remote_ip_6_entry"));
  274. gtk_widget_set_sensitive (widget, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)));
  275. widget = GTK_WIDGET (gtk_builder_get_object (builder, "local_ip_6_entry"));
  276. gtk_widget_set_sensitive (widget, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)));
  277. widget = GTK_WIDGET (gtk_builder_get_object (builder, "netmask_6_entry"));
  278. gtk_widget_set_sensitive (widget, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)));
  279. }
  280. void
  281. init_auth_widget (GtkBuilder *builder,
  282. GtkSizeGroup *group,
  283. NMSettingVPN *s_vpn,
  284. const char *contype,
  285. const char *prefix,
  286. ChangedCallback changed_cb,
  287. gpointer user_data)
  288. {
  289. GtkWidget *widget, *widget2;
  290. g_return_if_fail (builder != NULL);
  291. g_return_if_fail (group != NULL);
  292. g_return_if_fail (changed_cb != NULL);
  293. g_return_if_fail (prefix != NULL);
  294. /* Three major connection types here: ssh-agent, key file, password */
  295. if (!strncmp (contype, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  296. const gchar* password;
  297. NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
  298. /* Show password button - by default don't show the password */
  299. widget = GTK_WIDGET (gtk_builder_get_object (builder, "auth_password_show_password_checkbutton"));
  300. g_assert (widget);
  301. widget2 = GTK_WIDGET (gtk_builder_get_object (builder, "auth_password_entry"));
  302. g_assert (widget2);
  303. g_signal_connect (widget, "toggled", G_CALLBACK (show_password_toggled), widget2);
  304. show_password_toggled (GTK_TOGGLE_BUTTON (widget), GTK_ENTRY (widget2));
  305. /* Load password */
  306. g_signal_connect (G_OBJECT (widget2), "changed", G_CALLBACK (changed_cb), user_data);
  307. if (s_vpn) {
  308. password = nm_setting_vpn_get_secret (s_vpn, NM_SSH_KEY_PASSWORD);
  309. if (password)
  310. gtk_entry_set_text (GTK_ENTRY (widget2), password);
  311. nm_setting_get_secret_flags (NM_SETTING (s_vpn), NM_SSH_KEY_PASSWORD, &pw_flags, NULL);
  312. /* FIXME */
  313. //g_object_set_data (G_OBJECT (widget2), "flags", GUINT_TO_POINTER (pw_flags));
  314. }
  315. }
  316. else if (!strncmp (contype, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY))) {
  317. /* Get key filename and set it */
  318. const gchar *filename;
  319. widget = GTK_WIDGET (gtk_builder_get_object (builder, "auth_keyfile_filechooserbutton"));
  320. /* FIXME add filter */
  321. //gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
  322. gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
  323. if (s_vpn) {
  324. filename = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_KEY_FILE);
  325. if (filename && strlen (filename))
  326. gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), filename);
  327. }
  328. g_signal_connect (G_OBJECT (widget), "selection-changed", G_CALLBACK (changed_cb), user_data);
  329. } else if (!strncmp (contype, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))) {
  330. /* ssh-agent is the default */
  331. /* Not much to do here! No options for ssh-agent :) */
  332. } else {
  333. /* FIXME FATAL ERROR */
  334. }
  335. }
  336. static void
  337. pw_type_combo_changed_cb (GtkWidget *combo, gpointer user_data)
  338. {
  339. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (user_data);
  340. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  341. GtkWidget *entry;
  342. entry = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_password_entry"));
  343. g_assert (entry);
  344. /* If the user chose "Not required", desensitize and clear the correct
  345. * password entry.
  346. */
  347. switch (gtk_combo_box_get_active (GTK_COMBO_BOX (combo))) {
  348. case PW_TYPE_ASK:
  349. gtk_entry_set_text (GTK_ENTRY (entry), "");
  350. gtk_widget_set_sensitive (entry, FALSE);
  351. break;
  352. default:
  353. gtk_widget_set_sensitive (entry, TRUE);
  354. break;
  355. }
  356. stuff_changed_cb (combo, self);
  357. }
  358. static void
  359. init_one_pw_combo (
  360. SshPluginUiWidget *self,
  361. NMSettingVPN *s_vpn,
  362. const char *combo_name,
  363. const char *secret_key,
  364. const char *entry_name)
  365. {
  366. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  367. int active = -1;
  368. GtkWidget *widget;
  369. GtkListStore *store;
  370. GtkTreeIter iter;
  371. const char *value = NULL;
  372. guint32 default_idx = 1;
  373. NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
  374. /* If there's already a password and the password type can't be found in
  375. * the VPN settings, default to saving it. Otherwise, always ask for it.
  376. */
  377. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, entry_name));
  378. g_assert (widget);
  379. value = gtk_entry_get_text (GTK_ENTRY (widget));
  380. if (value && strlen (value))
  381. default_idx = 0;
  382. store = gtk_list_store_new (1, G_TYPE_STRING);
  383. if (s_vpn)
  384. nm_setting_get_secret_flags (NM_SETTING (s_vpn), secret_key, &pw_flags, NULL);
  385. gtk_list_store_append (store, &iter);
  386. gtk_list_store_set (store, &iter, 0, _("Saved"), -1);
  387. if ( (active < 0)
  388. && !(pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
  389. active = PW_TYPE_SAVE;
  390. }
  391. gtk_list_store_append (store, &iter);
  392. gtk_list_store_set (store, &iter, 0, _("Always Ask"), -1);
  393. if ((active < 0) && (pw_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
  394. active = PW_TYPE_ASK;
  395. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, combo_name));
  396. g_assert (widget);
  397. gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
  398. g_object_unref (store);
  399. gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? default_idx : active);
  400. pw_type_combo_changed_cb (widget, self);
  401. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (pw_type_combo_changed_cb), self);
  402. }
  403. /* FIXME break into smaller functions */
  404. static gboolean
  405. init_plugin_ui (SshPluginUiWidget *self, NMConnection *connection, GError **error)
  406. {
  407. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  408. NMSettingVPN *s_vpn;
  409. GtkWidget *widget;
  410. GtkListStore *store;
  411. GtkTreeIter iter;
  412. const char *value;
  413. const char *auth_type = NM_SSH_AUTH_TYPE_SSH_AGENT;
  414. int active = -1;
  415. s_vpn = nm_connection_get_setting_vpn (connection);
  416. priv->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  417. /* Remote GW (SSH host) */
  418. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  419. g_return_val_if_fail (widget != NULL, FALSE);
  420. gtk_size_group_add_widget (priv->group, widget);
  421. if (s_vpn) {
  422. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE);
  423. if (value)
  424. gtk_entry_set_text (GTK_ENTRY (widget), value);
  425. }
  426. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  427. /* Remote IP */
  428. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "remote_ip_entry"));
  429. g_return_val_if_fail (widget != NULL, FALSE);
  430. gtk_size_group_add_widget (priv->group, widget);
  431. if (s_vpn) {
  432. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP);
  433. if (value)
  434. gtk_entry_set_text (GTK_ENTRY (widget), value);
  435. }
  436. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  437. /* Local IP */
  438. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "local_ip_entry"));
  439. g_return_val_if_fail (widget != NULL, FALSE);
  440. gtk_size_group_add_widget (priv->group, widget);
  441. if (s_vpn) {
  442. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP);
  443. if (value)
  444. gtk_entry_set_text (GTK_ENTRY (widget), value);
  445. }
  446. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  447. /* Netmask */
  448. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "netmask_entry"));
  449. g_return_val_if_fail (widget != NULL, FALSE);
  450. gtk_size_group_add_widget (priv->group, widget);
  451. if (s_vpn) {
  452. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK);
  453. if (value)
  454. gtk_entry_set_text (GTK_ENTRY (widget), value);
  455. }
  456. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  457. /* Remote IP IPv6 */
  458. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "remote_ip_6_entry"));
  459. g_return_val_if_fail (widget != NULL, FALSE);
  460. gtk_size_group_add_widget (priv->group, widget);
  461. if (s_vpn) {
  462. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP_6);
  463. if (value)
  464. gtk_entry_set_text (GTK_ENTRY (widget), value);
  465. }
  466. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  467. /* Local IP IPv6 */
  468. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "local_ip_6_entry"));
  469. g_return_val_if_fail (widget != NULL, FALSE);
  470. gtk_size_group_add_widget (priv->group, widget);
  471. if (s_vpn) {
  472. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP_6);
  473. if (value)
  474. gtk_entry_set_text (GTK_ENTRY (widget), value);
  475. }
  476. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  477. /* Netmask IPv6 */
  478. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "netmask_6_entry"));
  479. g_return_val_if_fail (widget != NULL, FALSE);
  480. gtk_size_group_add_widget (priv->group, widget);
  481. if (s_vpn) {
  482. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK_6);
  483. if (value)
  484. gtk_entry_set_text (GTK_ENTRY (widget), value);
  485. }
  486. g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (stuff_changed_cb), self);
  487. /* IPv6 options */
  488. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "ipv6_checkbutton"));
  489. g_assert (widget);
  490. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_IP_6);
  491. if (value && IS_YES(value)) {
  492. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
  493. } else {
  494. gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
  495. }
  496. /* Call the callback to show/hide IPv6 options */
  497. ipv6_toggled_cb (widget, priv->builder);
  498. g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (ipv6_toggled_cb), priv->builder);
  499. g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (stuff_changed_cb), self);
  500. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "ipv6_label"));
  501. g_assert (widget);
  502. gtk_widget_show (widget);
  503. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "ipv6_alignment"));
  504. g_assert (widget);
  505. gtk_widget_show (widget);
  506. /* Authentication combo box */
  507. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "auth_auth_type_combobox"));
  508. g_return_val_if_fail (widget != NULL, FALSE);
  509. gtk_size_group_add_widget (priv->group, widget);
  510. store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
  511. if (s_vpn) {
  512. auth_type = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_AUTH_TYPE);
  513. if (auth_type) {
  514. if (strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))
  515. && strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))
  516. && strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY)))
  517. auth_type = NM_SSH_AUTH_TYPE_SSH_AGENT;
  518. } else
  519. auth_type = NM_SSH_AUTH_TYPE_SSH_AGENT;
  520. }
  521. /* SSH Agent auth widget */
  522. init_auth_widget (priv->builder, priv->group, s_vpn,
  523. NM_SSH_KEY_AUTH_TYPE, "ssh-agent",
  524. stuff_changed_cb, self);
  525. gtk_list_store_append (store, &iter);
  526. gtk_list_store_set (store, &iter,
  527. COL_AUTH_NAME, _("SSH Agent"),
  528. COL_AUTH_PAGE, 0,
  529. COL_AUTH_TYPE, NM_SSH_AUTH_TYPE_SSH_AGENT,
  530. -1);
  531. if ((active < 0) && !strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT)))
  532. active = 0;
  533. /* Password auth widget */
  534. init_auth_widget (priv->builder, priv->group, s_vpn,
  535. NM_SSH_AUTH_TYPE_PASSWORD, "pw",
  536. stuff_changed_cb, self);
  537. gtk_list_store_append (store, &iter);
  538. gtk_list_store_set (store, &iter,
  539. COL_AUTH_NAME, _("Password"),
  540. COL_AUTH_PAGE, 1,
  541. COL_AUTH_TYPE, NM_SSH_AUTH_TYPE_PASSWORD,
  542. -1);
  543. if ((active < 0) && !strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD)))
  544. active = 1;
  545. /* Key auth widget */
  546. init_auth_widget (priv->builder, priv->group, s_vpn,
  547. NM_SSH_AUTH_TYPE_KEY, "key",
  548. stuff_changed_cb, self);
  549. gtk_list_store_append (store, &iter);
  550. gtk_list_store_set (store, &iter,
  551. COL_AUTH_NAME, _("Key Authentication"),
  552. COL_AUTH_PAGE, 2,
  553. COL_AUTH_TYPE, NM_SSH_AUTH_TYPE_KEY,
  554. -1);
  555. if ((active < 0) && !strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY)))
  556. active = 2;
  557. gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
  558. g_object_unref (store);
  559. g_signal_connect (widget, "changed", G_CALLBACK (auth_combo_changed_cb), self);
  560. gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active < 0 ? 0 : active);
  561. /* Combo box for save/don't save password */
  562. init_one_pw_combo (
  563. self,
  564. s_vpn,
  565. "auth_password_save_password_combobox",
  566. NM_SSH_KEY_PASSWORD,
  567. "auth_password_entry");
  568. /* Advanced button */
  569. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "advanced_button"));
  570. g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (advanced_button_clicked_cb), self);
  571. return TRUE;
  572. }
  573. static GObject *
  574. get_widget (NMVpnPluginUiWidgetInterface *iface)
  575. {
  576. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (iface);
  577. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  578. return G_OBJECT (priv->widget);
  579. }
  580. static void
  581. hash_copy_advanced (gpointer key, gpointer data, gpointer user_data)
  582. {
  583. NMSettingVPN *s_vpn = NM_SETTING_VPN (user_data);
  584. const char *value = (const char *) data;
  585. g_return_if_fail (value && strlen (value));
  586. nm_setting_vpn_add_data_item (s_vpn, (const char *) key, value);
  587. }
  588. static gboolean auth_widget_update_connection (
  589. GtkBuilder *builder,
  590. NMSettingVPN *s_vpn)
  591. {
  592. /* This function populates s_vpn with the auth properties */
  593. GtkWidget *widget;
  594. GtkWidget *combo_password;
  595. GtkComboBox *combo;
  596. GtkTreeModel *model;
  597. GtkTreeIter iter;
  598. char *auth_type = NULL;
  599. gboolean success = TRUE;
  600. combo = GTK_COMBO_BOX (GTK_WIDGET (gtk_builder_get_object (builder, "auth_auth_type_combobox")));
  601. model = gtk_combo_box_get_model (combo);
  602. success = gtk_combo_box_get_active_iter (combo, &iter);
  603. g_return_val_if_fail (success == TRUE, FALSE);
  604. gtk_tree_model_get (model, &iter, COL_AUTH_TYPE, &auth_type, -1);
  605. /* Set auth type */
  606. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_AUTH_TYPE, auth_type);
  607. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  608. /* Password auth */
  609. const gchar *password;
  610. NMSettingSecretFlags pw_flags = NM_SETTING_SECRET_FLAG_NONE;
  611. /* Grab original password flags */
  612. widget = GTK_WIDGET (gtk_builder_get_object (builder, "auth_password_entry"));
  613. pw_flags = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "flags"));
  614. combo_password = GTK_WIDGET (gtk_builder_get_object (builder, "auth_password_save_password_combobox"));
  615. /* And set new ones based on the type combo */
  616. switch (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_password))) {
  617. case PW_TYPE_SAVE:
  618. password = gtk_entry_get_text (GTK_ENTRY (widget));
  619. if (password && strlen (password))
  620. nm_setting_vpn_add_secret (s_vpn, NM_SSH_KEY_PASSWORD, password);
  621. break;
  622. case PW_TYPE_ASK:
  623. default:
  624. pw_flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
  625. break;
  626. }
  627. /* Set new secret flags */
  628. nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_SSH_KEY_PASSWORD, pw_flags, NULL);
  629. }
  630. else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY))) {
  631. /* Key auth */
  632. gchar *filename;
  633. widget = GTK_WIDGET (gtk_builder_get_object (builder, "auth_keyfile_filechooserbutton"));
  634. filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
  635. if (filename && strlen (filename)) {
  636. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_KEY_FILE, filename);
  637. }
  638. g_free (filename);
  639. }
  640. else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))) {
  641. /* SSH AGENT auth */
  642. /* Nothing to set here, honestly!! It's here just for the sake of order */
  643. }
  644. else {
  645. success = FALSE;
  646. }
  647. g_free (auth_type);
  648. return success;
  649. }
  650. static gboolean
  651. update_connection (NMVpnPluginUiWidgetInterface *iface,
  652. NMConnection *connection,
  653. GError **error)
  654. {
  655. SshPluginUiWidget *self = SSH_PLUGIN_UI_WIDGET (iface);
  656. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (self);
  657. NMSettingVPN *s_vpn;
  658. GtkWidget *widget;
  659. const char *str;
  660. gboolean valid = FALSE;
  661. if (!check_validity (self, error))
  662. return FALSE;
  663. s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
  664. g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_DBUS_SERVICE_SSH, NULL);
  665. /* Gateway */
  666. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "gateway_entry"));
  667. str = gtk_entry_get_text (GTK_ENTRY (widget));
  668. if (str && strlen (str))
  669. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_REMOTE, str);
  670. /* Remote IP */
  671. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "remote_ip_entry"));
  672. str = gtk_entry_get_text (GTK_ENTRY (widget));
  673. if (str && strlen (str))
  674. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP, str);
  675. /* Local IP */
  676. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "local_ip_entry"));
  677. str = gtk_entry_get_text (GTK_ENTRY (widget));
  678. if (str && strlen (str))
  679. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP, str);
  680. /* Netmask */
  681. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "netmask_entry"));
  682. str = gtk_entry_get_text (GTK_ENTRY (widget));
  683. if (str && strlen (str))
  684. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_NETMASK, str);
  685. /* IPv6 enabled */
  686. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "ipv6_checkbutton"));
  687. if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
  688. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_IP_6, YES);
  689. /* Remote IP IPv6 */
  690. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "remote_ip_6_entry"));
  691. str = gtk_entry_get_text (GTK_ENTRY (widget));
  692. if (str && strlen (str))
  693. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP_6, str);
  694. /* Local IP IPv6 */
  695. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "local_ip_6_entry"));
  696. str = gtk_entry_get_text (GTK_ENTRY (widget));
  697. if (str && strlen (str))
  698. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP_6, str);
  699. /* Prefix IPv6 */
  700. widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "netmask_6_entry"));
  701. str = gtk_entry_get_text (GTK_ENTRY (widget));
  702. if (str && strlen (str))
  703. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_NETMASK_6, str);
  704. }
  705. /* Authentication type */
  706. valid &= auth_widget_update_connection (priv->builder, s_vpn);
  707. if (priv->advanced)
  708. g_hash_table_foreach (priv->advanced, hash_copy_advanced, s_vpn);
  709. nm_connection_add_setting (connection, NM_SETTING (s_vpn));
  710. valid = TRUE;
  711. return valid;
  712. }
  713. static void
  714. is_new_func (const char *key, const char *value, gpointer user_data)
  715. {
  716. gboolean *is_new = user_data;
  717. /* If there are any VPN data items the connection isn't new */
  718. *is_new = FALSE;
  719. }
  720. static NMVpnPluginUiWidgetInterface *
  721. nm_vpn_plugin_ui_widget_interface_new (NMConnection *connection, GError **error)
  722. {
  723. NMVpnPluginUiWidgetInterface *object;
  724. SshPluginUiWidgetPrivate *priv;
  725. char *ui_file;
  726. gboolean new = TRUE;
  727. NMSettingVPN *s_vpn;
  728. if (error)
  729. g_return_val_if_fail (*error == NULL, NULL);
  730. object = NM_VPN_PLUGIN_UI_WIDGET_INTERFACE (g_object_new (SSH_TYPE_PLUGIN_UI_WIDGET, NULL));
  731. if (!object) {
  732. g_set_error (error, SSH_PLUGIN_UI_ERROR, 0, "could not create ssh object");
  733. return NULL;
  734. }
  735. priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (object);
  736. ui_file = g_strdup_printf ("%s/%s", UIDIR, "nm-ssh-dialog.ui");
  737. priv->builder = gtk_builder_new ();
  738. gtk_builder_set_translation_domain (priv->builder, GETTEXT_PACKAGE);
  739. if (!gtk_builder_add_from_file (priv->builder, ui_file, error)) {
  740. g_warning ("Couldn't load builder file: %s",
  741. error && *error ? (*error)->message : "(unknown)");
  742. g_clear_error (error);
  743. g_set_error (error, SSH_PLUGIN_UI_ERROR, 0,
  744. "could not load required resources from %s", ui_file);
  745. g_free (ui_file);
  746. g_object_unref (object);
  747. return NULL;
  748. }
  749. g_free (ui_file);
  750. priv->widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "ssh_main_vbox"));
  751. if (!priv->widget) {
  752. g_set_error (error, SSH_PLUGIN_UI_ERROR, 0, "could not load UI widget");
  753. g_object_unref (object);
  754. return NULL;
  755. }
  756. g_object_ref_sink (priv->widget);
  757. priv->window_group = gtk_window_group_new ();
  758. s_vpn = nm_connection_get_setting_vpn (connection);
  759. if (s_vpn)
  760. nm_setting_vpn_foreach_data_item (s_vpn, is_new_func, &new);
  761. priv->new_connection = new;
  762. if (!init_plugin_ui (SSH_PLUGIN_UI_WIDGET (object), connection, error)) {
  763. g_object_unref (object);
  764. return NULL;
  765. }
  766. priv->advanced = advanced_dialog_new_hash_from_connection (connection, error);
  767. if (!priv->advanced) {
  768. g_object_unref (object);
  769. return NULL;
  770. }
  771. return object;
  772. }
  773. static void
  774. dispose (GObject *object)
  775. {
  776. SshPluginUiWidget *plugin = SSH_PLUGIN_UI_WIDGET (object);
  777. SshPluginUiWidgetPrivate *priv = SSH_PLUGIN_UI_WIDGET_GET_PRIVATE (plugin);
  778. if (priv->group)
  779. g_object_unref (priv->group);
  780. if (priv->window_group)
  781. g_object_unref (priv->window_group);
  782. if (priv->widget)
  783. g_object_unref (priv->widget);
  784. if (priv->builder)
  785. g_object_unref (priv->builder);
  786. if (priv->advanced)
  787. g_hash_table_destroy (priv->advanced);
  788. G_OBJECT_CLASS (ssh_plugin_ui_widget_parent_class)->dispose (object);
  789. }
  790. static void
  791. ssh_plugin_ui_widget_class_init (SshPluginUiWidgetClass *req_class)
  792. {
  793. GObjectClass *object_class = G_OBJECT_CLASS (req_class);
  794. g_type_class_add_private (req_class, sizeof (SshPluginUiWidgetPrivate));
  795. object_class->dispose = dispose;
  796. }
  797. static void
  798. ssh_plugin_ui_widget_init (SshPluginUiWidget *plugin)
  799. {
  800. }
  801. static void
  802. ssh_plugin_ui_widget_interface_init (NMVpnPluginUiWidgetInterface *iface_class)
  803. {
  804. /* interface implementation */
  805. iface_class->get_widget = get_widget;
  806. iface_class->update_connection = update_connection;
  807. }
  808. static NMConnection *
  809. import (NMVpnPluginUiInterface *iface, const char *path, GError **error)
  810. {
  811. NMConnection *connection = NULL;
  812. NMSettingConnection *s_con;
  813. NMSettingVPN *s_vpn;
  814. char *contents = NULL;
  815. char **lines = NULL;
  816. char *ext;
  817. char **line;
  818. ext = strrchr (path, '.');
  819. if (!ext) {
  820. g_set_error (error,
  821. SSH_PLUGIN_UI_ERROR,
  822. SSH_PLUGIN_UI_ERROR_FILE_NOT_SSH,
  823. "unknown OpenVPN file extension, should be .sh");
  824. goto out;
  825. }
  826. if (strncmp (ext, ".sh", strlen(".sh"))) {
  827. g_set_error (error,
  828. SSH_PLUGIN_UI_ERROR,
  829. SSH_PLUGIN_UI_ERROR_FILE_NOT_SSH,
  830. "unknown SSH file extension, should be .sh");
  831. goto out;
  832. }
  833. if (!g_file_get_contents (path, &contents, NULL, error))
  834. return NULL;
  835. if (!g_utf8_validate (contents, -1, NULL)) {
  836. char *tmp;
  837. GError *conv_error = NULL;
  838. tmp = g_locale_to_utf8 (contents, -1, NULL, NULL, &conv_error);
  839. if (conv_error) {
  840. /* ignore the error, we tried at least. */
  841. g_error_free (conv_error);
  842. g_free (tmp);
  843. } else {
  844. g_assert (tmp);
  845. g_free (contents);
  846. contents = tmp; /* update contents with the UTF-8 safe text */
  847. }
  848. }
  849. lines = g_strsplit_set (contents, "\r\n", 0);
  850. if (g_strv_length (lines) <= 1) {
  851. g_set_error (error,
  852. SSH_PLUGIN_UI_ERROR,
  853. SSH_PLUGIN_UI_ERROR_FILE_NOT_READABLE,
  854. "not a valid OpenVPN configuration file");
  855. goto out;
  856. }
  857. connection = nm_connection_new ();
  858. s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
  859. nm_connection_add_setting (connection, NM_SETTING (s_con));
  860. s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
  861. g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, NM_DBUS_SERVICE_SSH, NULL);
  862. for (line = lines; *line; line++) {
  863. char *comment;
  864. char **items = NULL;
  865. if ((comment = strchr (*line, '#')))
  866. *comment = '\0';
  867. if ((comment = strchr (*line, ';')))
  868. *comment = '\0';
  869. if (!strlen (*line))
  870. continue;
  871. items = g_strsplit_set (*line, "=", 0);
  872. if (!items) {
  873. continue;
  874. } else {
  875. /* Uncomment if you'd like to debug parsing of items */
  876. /* g_message("%s = %s", items[0], items[1]); */
  877. }
  878. /* the PARSE_IMPORT_KEY will save heaps of lines of code, it's
  879. * on the top of the file if you're looking for it */
  880. PARSE_IMPORT_KEY (REMOTE_KEY, NM_SSH_KEY_REMOTE, items, s_vpn)
  881. PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_STR (AUTH_TYPE_KEY, NM_SSH_KEY_AUTH_TYPE, items, s_vpn, NM_SSH_AUTH_TYPE_SSH_AGENT)
  882. PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_STR (REMOTE_USERNAME_KEY, NM_SSH_KEY_REMOTE_USERNAME, items, s_vpn, NM_SSH_DEFAULT_REMOTE_USERNAME);
  883. PARSE_IMPORT_KEY (KEY_FILE_KEY, NM_SSH_KEY_KEY_FILE, items, s_vpn)
  884. PARSE_IMPORT_KEY (REMOTE_IP_KEY, NM_SSH_KEY_REMOTE_IP, items, s_vpn)
  885. PARSE_IMPORT_KEY (LOCAL_IP_KEY, NM_SSH_KEY_LOCAL_IP, items, s_vpn)
  886. PARSE_IMPORT_KEY (NETMASK_KEY, NM_SSH_KEY_NETMASK, items, s_vpn)
  887. PARSE_IMPORT_KEY (IP_6_KEY, NM_SSH_KEY_IP_6, items, s_vpn)
  888. PARSE_IMPORT_KEY (REMOTE_IP_6_KEY, NM_SSH_KEY_REMOTE_IP_6, items, s_vpn)
  889. PARSE_IMPORT_KEY (LOCAL_IP_6_KEY, NM_SSH_KEY_LOCAL_IP_6, items, s_vpn)
  890. PARSE_IMPORT_KEY (NETMASK_6_KEY, NM_SSH_KEY_NETMASK_6, items, s_vpn)
  891. PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_INT (PORT_KEY, NM_SSH_KEY_PORT, items, s_vpn, NM_SSH_DEFAULT_PORT)
  892. PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_INT (MTU_KEY, NM_SSH_KEY_TUNNEL_MTU, items, s_vpn, NM_SSH_DEFAULT_MTU)
  893. PARSE_IMPORT_KEY_WITH_DEFAULT_VALUE_INT (REMOTE_DEV_KEY, NM_SSH_KEY_REMOTE_DEV, items, s_vpn, NM_SSH_DEFAULT_REMOTE_DEV)
  894. PARSE_IMPORT_KEY_BOOL (DEV_TYPE_KEY, NM_SSH_KEY_TAP_DEV, items, s_vpn, "tap")
  895. PARSE_IMPORT_KEY_BOOL (NO_DEFAULT_ROUTE_KEY, NM_SSH_KEY_NO_DEFAULT_ROUTE, items, s_vpn, YES)
  896. /* Some extra care required with extra_opts as we need to:
  897. * 1. Use the whole line (might contain = chars in it)
  898. * 2. Strip the single/double quotes */
  899. if (!strncmp (items[0], EXTRA_OPTS_KEY, strlen (items[0]))) {
  900. gchar *parsed_extra_opts = NULL;
  901. gchar *unquoted_extra_opts = NULL;
  902. /* Read the whole line, witout the EXTRA_OPTS= part */
  903. parsed_extra_opts = g_strdup(*line + strlen(EXTRA_OPTS_KEY) + 1);
  904. /* Check if string is quoted */
  905. if ( (parsed_extra_opts[0] == '"' && parsed_extra_opts[strlen(parsed_extra_opts)-1] == '"') ||
  906. /* String is quoted (would usually be), lets strip the quotes */
  907. (parsed_extra_opts[0] == '\'' && parsed_extra_opts[strlen(parsed_extra_opts)-1] == '\'') ) {
  908. /* Unquote string */
  909. parsed_extra_opts[strlen(parsed_extra_opts)-1] = '\0';
  910. unquoted_extra_opts = parsed_extra_opts + 1;
  911. }
  912. /* After all this effort, try to compare to the default value */
  913. if (strncmp(unquoted_extra_opts, NM_SSH_DEFAULT_EXTRA_OPTS, strlen(unquoted_extra_opts)))
  914. nm_setting_vpn_add_data_item (s_vpn, NM_SSH_KEY_EXTRA_OPTS, unquoted_extra_opts);
  915. g_free (items);
  916. g_free (parsed_extra_opts);
  917. continue;
  918. }
  919. }
  920. if (connection)
  921. nm_connection_add_setting (connection, NM_SETTING (s_vpn));
  922. else if (s_vpn)
  923. g_object_unref (s_vpn);
  924. out:
  925. if (lines)
  926. g_strfreev (lines);
  927. g_free (contents);
  928. return connection;
  929. }
  930. static gboolean
  931. export (NMVpnPluginUiInterface *iface,
  932. const char *path,
  933. NMConnection *connection,
  934. GError **error)
  935. {
  936. NMSettingConnection *s_con;
  937. NMSettingVPN *s_vpn;
  938. FILE *f;
  939. const char *value;
  940. const char *auth_type = NULL;
  941. const char *key_file = NULL;
  942. const char *gateway = NULL;
  943. const char *port = NULL;
  944. const char *local_ip = NULL;
  945. const char *remote_ip = NULL;
  946. const char *netmask = NULL;
  947. const char *local_ip_6 = NULL;
  948. const char *remote_ip_6 = NULL;
  949. const char *netmask_6 = NULL;
  950. const char *extra_opts = NULL;
  951. const char *remote_dev = NULL;
  952. const char *mtu = NULL;
  953. const char *remote_username = NULL;
  954. char *device_type = NULL;
  955. char *tunnel_type = NULL;
  956. char *ifconfig_cmd_local_6 = NULL;
  957. char *ifconfig_cmd_remote_6 = NULL;
  958. char *preferred_authentication = NULL;
  959. unsigned password_prompt_nr = 0;
  960. gboolean ipv6 = FALSE;
  961. gboolean no_default_route = FALSE;
  962. gboolean success = FALSE;
  963. s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
  964. g_assert (s_con);
  965. s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
  966. f = fopen (path, "w");
  967. if (!f) {
  968. g_set_error (error, 0, 0, "could not open file for writing");
  969. return FALSE;
  970. }
  971. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE);
  972. if (value && strlen (value))
  973. gateway = value;
  974. else {
  975. g_set_error (error, 0, 0, "connection was incomplete (missing gateway)");
  976. goto done;
  977. }
  978. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP);
  979. if (value && strlen (value))
  980. remote_ip = value;
  981. else {
  982. g_set_error (error, 0, 0, "connection was incomplete (missing remote IP)");
  983. goto done;
  984. }
  985. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP);
  986. if (value && strlen (value))
  987. local_ip = value;
  988. else {
  989. g_set_error (error, 0, 0, "connection was incomplete (missing local IP)");
  990. goto done;
  991. }
  992. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK);
  993. if (value && strlen (value))
  994. netmask = value;
  995. else {
  996. g_set_error (error, 0, 0, "connection was incomplete (missing netmask)");
  997. goto done;
  998. }
  999. /* Auth type */
  1000. auth_type = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_AUTH_TYPE);
  1001. if (auth_type) {
  1002. if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_PASSWORD, strlen(NM_SSH_AUTH_TYPE_PASSWORD))) {
  1003. password_prompt_nr = 1;
  1004. preferred_authentication = g_strdup("password");
  1005. } else if (!strncmp (auth_type, NM_SSH_AUTH_TYPE_KEY, strlen(NM_SSH_AUTH_TYPE_KEY))) {
  1006. key_file = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_KEY_FILE);
  1007. preferred_authentication = g_strdup("publickey");
  1008. } else { // (!strncmp (auth_type, NM_SSH_AUTH_TYPE_SSH_AGENT, strlen(NM_SSH_AUTH_TYPE_SSH_AGENT))) {
  1009. // Nothing to be done for ssh-agent, the wise choice...
  1010. preferred_authentication = g_strdup("publickey");
  1011. }
  1012. }
  1013. /* Auth type */
  1014. /* Advanced values start */
  1015. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_PORT);
  1016. if (value && strlen (value))
  1017. port = value;
  1018. else
  1019. port = g_strdup_printf("%d", NM_SSH_DEFAULT_PORT);
  1020. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_TUNNEL_MTU);
  1021. if (value && strlen (value))
  1022. mtu = value;
  1023. else
  1024. mtu = g_strdup_printf("%d", NM_SSH_DEFAULT_MTU);
  1025. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_EXTRA_OPTS);
  1026. if (value && strlen (value))
  1027. extra_opts = value;
  1028. else
  1029. extra_opts = g_strdup(NM_SSH_DEFAULT_EXTRA_OPTS);
  1030. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_DEV);
  1031. if (value && strlen (value))
  1032. remote_dev = value;
  1033. else
  1034. remote_dev = g_strdup_printf("%d", NM_SSH_DEFAULT_REMOTE_DEV);
  1035. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_USERNAME);
  1036. if (value && strlen (value))
  1037. remote_username = value;
  1038. else
  1039. remote_username = g_strdup(NM_SSH_DEFAULT_REMOTE_USERNAME);
  1040. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_TAP_DEV);
  1041. if (value && IS_YES(value)) {
  1042. device_type = g_strdup("tap");
  1043. tunnel_type = g_strdup("ethernet");
  1044. } else {
  1045. device_type = g_strdup("tun");
  1046. tunnel_type = g_strdup("point-to-point");
  1047. }
  1048. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NO_DEFAULT_ROUTE);
  1049. if (value && IS_YES(value)) {
  1050. no_default_route = TRUE;
  1051. }
  1052. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_IP_6);
  1053. if (value && IS_YES(value)) {
  1054. ipv6 = TRUE;
  1055. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_REMOTE_IP_6);
  1056. if (value && strlen (value))
  1057. remote_ip_6 = value;
  1058. else {
  1059. g_set_error (error, 0, 0, "connection was incomplete (missing IPv6 remote IP)");
  1060. goto done;
  1061. }
  1062. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_LOCAL_IP_6);
  1063. if (value && strlen (value))
  1064. local_ip_6 = value;
  1065. else {
  1066. g_set_error (error, 0, 0, "connection was incomplete (missing IPv6 local IP)");
  1067. goto done;
  1068. }
  1069. value = nm_setting_vpn_get_data_item (s_vpn, NM_SSH_KEY_NETMASK_6);
  1070. if (value && strlen (value))
  1071. netmask_6 = value;
  1072. else {
  1073. g_set_error (error, 0, 0, "connection was incomplete (missing IPv6 netmask)");
  1074. goto done;
  1075. }
  1076. ifconfig_cmd_local_6 = g_strdup_printf("%s $DEV_TYPE$LOCAL_DEV add $LOCAL_IP_6/$NETMASK_6", IFCONFIG);
  1077. ifconfig_cmd_remote_6 = g_strdup_printf("%s $DEV_TYPE$REMOTE_DEV add $REMOTE_IP_6/$NETMASK_6", IFCONFIG);
  1078. } else {
  1079. ipv6 = FALSE;
  1080. ifconfig_cmd_local_6 = g_strdup("");
  1081. ifconfig_cmd_remote_6 = g_strdup("");
  1082. }
  1083. /* Advanced values end */
  1084. /* Serialize everything to a file */
  1085. fprintf (f, "#!/bin/bash\n");
  1086. /* Make my life easier and just add the AUTH_TYPE= key, not used though */
  1087. fprintf (f, "%s=%s\n", AUTH_TYPE_KEY, auth_type);
  1088. if (key_file) {
  1089. fprintf (f, "%s=%s\n", KEY_FILE_KEY, key_file);
  1090. }
  1091. fprintf (f, "%s=%s\n", REMOTE_KEY, gateway);
  1092. fprintf (f, "%s=%s\n", REMOTE_USERNAME_KEY, remote_username);
  1093. fprintf (f, "%s=%s\n", REMOTE_IP_KEY, remote_ip);
  1094. fprintf (f, "%s=%s\n", LOCAL_IP_KEY, local_ip);
  1095. fprintf (f, "%s=%s\n", NETMASK_KEY, netmask);
  1096. if (ipv6) {
  1097. fprintf (f, "%s=%s\n", IP_6_KEY, YES);
  1098. fprintf (f, "%s=%s\n", REMOTE_IP_6_KEY, remote_ip_6);
  1099. fprintf (f, "%s=%s\n", LOCAL_IP_6_KEY, local_ip_6);
  1100. fprintf (f, "%s=%s\n", NETMASK_6_KEY, netmask_6);
  1101. }
  1102. fprintf (f, "%s=%s\n", PORT_KEY, port);
  1103. fprintf (f, "%s=%s\n", MTU_KEY, mtu);
  1104. fprintf (f, "%s='%s'\n", EXTRA_OPTS_KEY, extra_opts);
  1105. fprintf (f, "%s=%s\n", REMOTE_DEV_KEY, remote_dev);
  1106. /* Assign tun/tap */
  1107. fprintf (f, "%s=%s\n", DEV_TYPE_KEY, device_type);
  1108. fprintf (f, "%s=%s\n", TUNNEL_TYPE_KEY, tunnel_type);
  1109. fprintf (f, "%s=%s\n\n", NO_DEFAULT_ROUTE_KEY,
  1110. no_default_route == TRUE ? YES : NO);
  1111. /* Add a little of bash script to probe for a free tun/tap device */
  1112. fprintf (f, "for i in `seq 0 255`; do ! %s $DEV_TYPE$i >& /dev/null && LOCAL_DEV=$i && break; done", IFCONFIG);
  1113. /* The generic lines that will perform the connection */
  1114. fprintf (f, "\n");
  1115. fprintf(f, "ssh -f %s -o PreferredAuthentications=%s -o NumberOfPasswordPrompts=%d -o Tunnel=$TUNNEL_TYPE $EXTRA_OPTS -o TunnelDevice=$LOCAL_DEV:$REMOTE_DEV -o User=$REMOTE_USERNAME -o Port=$PORT -o HostName=$REMOTE $REMOTE \"%s $DEV_TYPE$REMOTE_DEV $REMOTE_IP netmask $NETMASK pointopoint $LOCAL_IP; %s\" && \\\n",
  1116. (key_file ? g_strconcat("-i ", key_file, NULL) : ""),
  1117. preferred_authentication,
  1118. password_prompt_nr,
  1119. IFCONFIG,
  1120. ifconfig_cmd_remote_6);
  1121. fprintf(f, "%s $DEV_TYPE$LOCAL_DEV $LOCAL_IP netmask $NETMASK pointopoint $REMOTE_IP; %s\n", IFCONFIG, ifconfig_cmd_local_6);
  1122. success = TRUE;
  1123. g_free(device_type);
  1124. g_free(tunnel_type);
  1125. g_free(ifconfig_cmd_local_6);
  1126. g_free(ifconfig_cmd_remote_6);
  1127. g_free(preferred_authentication);
  1128. done:
  1129. fclose (f);
  1130. return success;
  1131. }
  1132. static char *
  1133. get_suggested_name (NMVpnPluginUiInterface *iface, NMConnection *connection)
  1134. {
  1135. NMSettingConnection *s_con;
  1136. const char *id;
  1137. g_return_val_if_fail (connection != NULL, NULL);
  1138. s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
  1139. g_return_val_if_fail (s_con != NULL, NULL);
  1140. id = nm_setting_connection_get_id (s_con);
  1141. g_return_val_if_fail (id != NULL, NULL);
  1142. return g_strdup_printf ("%s (ssh).sh", id);
  1143. }
  1144. static guint32
  1145. get_capabilities (NMVpnPluginUiInterface *iface)
  1146. {
  1147. return (NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT | NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT | NM_VPN_PLUGIN_UI_CAPABILITY_IPV6);
  1148. }
  1149. static NMVpnPluginUiWidgetInterface *
  1150. ui_factory (NMVpnPluginUiInterface *iface, NMConnection *connection, GError **error)
  1151. {
  1152. return nm_vpn_plugin_ui_widget_interface_new (connection, error);
  1153. }
  1154. static void
  1155. get_property (GObject *object, guint prop_id,
  1156. GValue *value, GParamSpec *pspec)
  1157. {
  1158. switch (prop_id) {
  1159. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME:
  1160. g_value_set_string (value, SSH_PLUGIN_NAME);
  1161. break;
  1162. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC:
  1163. g_value_set_string (value, SSH_PLUGIN_DESC);
  1164. break;
  1165. case NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE:
  1166. g_value_set_string (value, SSH_PLUGIN_SERVICE);
  1167. break;
  1168. default:
  1169. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  1170. break;
  1171. }
  1172. }
  1173. static void
  1174. ssh_plugin_ui_class_init (SshPluginUiClass *req_class)
  1175. {
  1176. GObjectClass *object_class = G_OBJECT_CLASS (req_class);
  1177. object_class->get_property = get_property;
  1178. g_object_class_override_property (object_class,
  1179. NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME,
  1180. NM_VPN_PLUGIN_UI_INTERFACE_NAME);
  1181. g_object_class_override_property (object_class,
  1182. NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC,
  1183. NM_VPN_PLUGIN_UI_INTERFACE_DESC);
  1184. g_object_class_override_property (object_class,
  1185. NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE,
  1186. NM_VPN_PLUGIN_UI_INTERFACE_SERVICE);
  1187. }
  1188. static void
  1189. ssh_plugin_ui_init (SshPluginUi *plugin)
  1190. {
  1191. }
  1192. static void
  1193. ssh_plugin_ui_interface_init (NMVpnPluginUiInterface *iface_class)
  1194. {
  1195. /* interface implementation */
  1196. iface_class->ui_factory = ui_factory;
  1197. iface_class->get_capabilities = get_capabilities;
  1198. iface_class->import_from_file = import;
  1199. iface_class->export_to_file = export;
  1200. iface_class->get_suggested_name = get_suggested_name;
  1201. }
  1202. G_MODULE_EXPORT NMVpnPluginUiInterface *
  1203. nm_vpn_plugin_ui_factory (GError **error)
  1204. {
  1205. if (error)
  1206. g_return_val_if_fail (*error == NULL, NULL);
  1207. return NM_VPN_PLUGIN_UI_INTERFACE (g_object_new (SSH_TYPE_PLUGIN_UI, NULL));
  1208. }