[network-manager-openvpn] core/ui: add HTTP Proxy support (bgo #440031)



commit 2eee51aedace28af0f39349baee130f4121428e7
Author: Dan Williams <dcbw redhat com>
Date:   Wed Aug 18 22:16:45 2010 -0500

    core/ui: add HTTP Proxy support (bgo #440031)
    
    Based off patches by:
    Tomas Kovacik <nail nodomain sk>
    Florian Klink <flokli flokli de>

 properties/auth-helpers.c          |  173 ++++++++++++++++++++++++++++--
 properties/auth-helpers.h          |    4 +
 properties/import-export.c         |   94 ++++++++++++++++
 properties/nm-openvpn-dialog.glade |  211 ++++++++++++++++++++++++++++++++++++
 properties/nm-openvpn.c            |   12 ++-
 src/nm-openvpn-service.c           |  118 +++++++++++++++------
 src/nm-openvpn-service.h           |    5 +
 7 files changed, 577 insertions(+), 40 deletions(-)
---
diff --git a/properties/auth-helpers.c b/properties/auth-helpers.c
index 33db4dc..c660ebe 100644
--- a/properties/auth-helpers.c
+++ b/properties/auth-helpers.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /***************************************************************************
  *
- * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ * Copyright (C) 2008 - 2010 Dan Williams, <dcbw redhat com>
  * Copyright (C) 2008 Tambet Ingo, <tambet gmail com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -845,6 +845,10 @@ static const char *advanced_keys[] = {
 	NM_OPENVPN_KEY_FRAGMENT_SIZE,
 	NM_OPENVPN_KEY_TAP_DEV,
 	NM_OPENVPN_KEY_PROTO_TCP,
+	NM_OPENVPN_KEY_HTTP_PROXY,
+	NM_OPENVPN_KEY_HTTP_PROXY_PORT,
+	NM_OPENVPN_KEY_HTTP_PROXY_RETRY,
+	NM_OPENVPN_KEY_HTTP_PROXY_USERNAME,
 	NM_OPENVPN_KEY_CIPHER,
 	NM_OPENVPN_KEY_AUTH,
 	NM_OPENVPN_KEY_TA_DIR,
@@ -874,11 +878,21 @@ advanced_dialog_new_hash_from_connection (NMConnection *connection,
 {
 	GHashTable *hash;
 	NMSettingVPN *s_vpn;
+	const char *secret;
 
 	hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
 	s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
 	nm_setting_vpn_foreach_data_item (s_vpn, copy_values, hash);
+
+	/* HTTP Proxy password is special */
+	secret = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+	if (secret) {
+		g_hash_table_insert (hash,
+		                     g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD),
+		                     g_strdup (secret));
+	}
+
 	return hash;
 }
 
@@ -1116,6 +1130,38 @@ tls_auth_toggled_cb (GtkWidget *widget, gpointer user_data)
 	gtk_widget_set_sensitive (widget, use_auth);
 }
 
+#define PROXY_TYPE_NONE 0
+#define PROXY_TYPE_HTTP 1
+
+static void
+proxy_type_changed (GtkComboBox *combo, gpointer user_data)
+{
+	GladeXML *xml = GLADE_XML (user_data);
+	gboolean sensitive;
+	GtkWidget *widget;
+	guint32 i = 0;
+	const char *widgets[] = {
+		"proxy_desc_label", "proxy_server_label", "proxy_server_entry",
+		"proxy_port_label", "proxy_port_spinbutton", "proxy_retry_checkbutton",
+		"proxy_username_label", "proxy_password_label", "proxy_username_entry",
+		"proxy_password_entry", NULL
+	};
+
+	sensitive = (gtk_combo_box_get_active (combo) == PROXY_TYPE_HTTP);
+	while (widgets[i]) {
+		widget = glade_xml_get_widget (xml, widgets[i++]);
+		gtk_widget_set_sensitive (widget, sensitive);
+	}
+
+	/* HTTP Proxy option requires TCP; but don't reset the TCP checkbutton
+	 * to false when the user disables HTTP proxy; leave it checked.
+	 */
+	widget = glade_xml_get_widget (xml, "tcp_checkbutton");
+	if (sensitive == TRUE)
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+	gtk_widget_set_sensitive (widget, !sensitive);
+}
+
 #define TA_DIR_COL_NAME 0
 #define TA_DIR_COL_NUM 1
 
@@ -1125,8 +1171,11 @@ advanced_dialog_new (GHashTable *hash, const char *contype)
 	GladeXML *xml;
 	GtkWidget *dialog = NULL;
 	char *glade_file = NULL;
-	GtkWidget *widget;
-	const char *value;
+	GtkWidget *widget, *combo;
+	const char *value, *value2;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	guint32 active = 0;
 
 	g_return_val_if_fail (hash != NULL, NULL);
 
@@ -1170,6 +1219,56 @@ advanced_dialog_new (GHashTable *hash, const char *contype)
 		gtk_widget_set_sensitive (widget, FALSE);
 	}
 
+	/* Proxy support */
+	combo = glade_xml_get_widget (xml, "proxy_type_combo");
+
+	store = gtk_list_store_new (1, G_TYPE_STRING);
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, 0, _("Not required"), -1);
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, 0, _("HTTP Proxy"), -1);
+
+	value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_HTTP_PROXY);
+	value2 = g_hash_table_lookup (hash, NM_OPENVPN_KEY_HTTP_PROXY_PORT);
+	if (value && strlen (value) && value2 && strlen (value2)) {
+		long int tmp = 8080;
+
+		active = 1;
+
+		widget = glade_xml_get_widget (xml, "proxy_server_entry");
+		gtk_entry_set_text (GTK_ENTRY (widget), value);
+
+		errno = 0;
+		tmp = strtol (value2, NULL, 10);
+		if (errno != 0 || tmp < 1 || tmp > 65535)
+			tmp = 8080;
+		widget = glade_xml_get_widget (xml, "proxy_port_spinbutton");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), (gdouble) tmp);
+
+		widget = glade_xml_get_widget (xml, "proxy_retry_checkbutton");
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_HTTP_PROXY_RETRY);
+		if (value && !strcmp (value, "yes"))
+			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
+		if (value && strlen (value)) {
+			widget = glade_xml_get_widget (xml, "proxy_username_entry");
+			gtk_entry_set_text (GTK_ENTRY (widget), value);
+		}
+
+		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+		if (value && strlen (value)) {
+			widget = glade_xml_get_widget (xml, "proxy_password_entry");
+			gtk_entry_set_text (GTK_ENTRY (widget), value);
+		}
+	}
+
+	gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
+	g_object_unref (store);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), active);
+	proxy_type_changed (GTK_COMBO_BOX (combo), xml);
+	g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (proxy_type_changed), xml);
+
 	widget = glade_xml_get_widget (xml, "port_checkbutton");
 	g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (port_toggled_cb), xml);
 
@@ -1291,10 +1390,9 @@ advanced_dialog_new (GHashTable *hash, const char *contype)
 	if (   !strcmp (contype, NM_OPENVPN_CONTYPE_TLS)
 	    || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
 	    || !strcmp (contype, NM_OPENVPN_CONTYPE_PASSWORD)) {
-		GtkListStore *store;
-		GtkTreeIter iter;
-		int direction = -1, active = -1;
+		int direction = -1;
 
+		active = -1;
 		widget = glade_xml_get_widget (xml, "tls_auth_checkbutton");
 		value = g_hash_table_lookup (hash, NM_OPENVPN_KEY_TA);
 		if (value && strlen (value))
@@ -1352,7 +1450,7 @@ advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error)
 	GtkWidget *widget;
 	GladeXML *xml;
 	const char *contype = NULL;
-	const char *value = NULL;
+	const char *value;
 
 	g_return_val_if_fail (dialog != NULL, NULL);
 	if (error)
@@ -1399,6 +1497,46 @@ advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error)
 		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_PORT), g_strdup_printf ("%d", port));
 	}
 
+	/* HTTP proxy support */
+	widget = glade_xml_get_widget (xml, "proxy_type_combo");
+	if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == PROXY_TYPE_HTTP) {
+		widget = glade_xml_get_widget (xml, "proxy_server_entry");
+		value = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+
+		if (value && strlen (value)) {
+			int proxy_port;
+
+			g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_HTTP_PROXY), g_strdup (value));
+
+			widget = glade_xml_get_widget (xml, "proxy_port_spinbutton");
+			proxy_port = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+			if (!proxy_port)
+				proxy_port = 8080;
+			g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_PORT),
+			                     g_strdup_printf ("%d", proxy_port));
+
+			widget = glade_xml_get_widget (xml, "proxy_retry_checkbutton");
+			if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+				g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_RETRY), g_strdup ("yes"));
+
+			widget = glade_xml_get_widget (xml, "proxy_username_entry");
+			value = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+			if (value && strlen (value)) {
+				g_hash_table_insert (hash,
+				                     g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_USERNAME),
+				                     g_strdup (value));
+			}
+
+			widget = glade_xml_get_widget (xml, "proxy_password_entry");
+			value = (char *) gtk_entry_get_text (GTK_ENTRY (widget));
+			if (value && strlen (value)) {
+				g_hash_table_insert (hash,
+				                     g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD),
+				                     g_strdup (value));
+			}
+		}
+	}
+
 	widget = glade_xml_get_widget (xml, "lzo_checkbutton");
 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
 		g_hash_table_insert (hash, g_strdup (NM_OPENVPN_KEY_COMP_LZO), g_strdup ("yes"));
@@ -1483,3 +1621,24 @@ advanced_dialog_new_hash_from_dialog (GtkWidget *dialog, GError **error)
 	return hash;
 }
 
+gboolean
+advanced_save_secrets (GHashTable *advanced,
+                       const char *uuid,
+                       const char *name)
+{
+	const char *secret;
+	GnomeKeyringResult result;
+	gboolean ret;
+
+	secret = g_hash_table_lookup (advanced, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+	if (secret && strlen (secret)) {
+		result = keyring_helpers_save_secret (uuid, name, NULL, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD, secret);
+		ret = result == GNOME_KEYRING_RESULT_OK;
+		if (!ret)
+			g_warning ("%s: failed to save HTTP proxy password to keyring.", __func__);
+	} else
+		ret = keyring_helpers_delete_secret (uuid, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+
+	return ret;
+}
+
diff --git a/properties/auth-helpers.h b/properties/auth-helpers.h
index d3af84b..8dcb618 100644
--- a/properties/auth-helpers.h
+++ b/properties/auth-helpers.h
@@ -63,6 +63,10 @@ gboolean auth_widget_save_secrets (GladeXML *xml,
 								   const char *uuid,
 								   const char *name);
 
+gboolean advanced_save_secrets (GHashTable *advanced,
+                                const char *uuid,
+                                const char *name);
+
 GtkFileFilter *tls_file_chooser_filter_new (gboolean pkcs_allowed);
 
 GtkFileFilter *sk_file_chooser_filter_new (void);
diff --git a/properties/import-export.c b/properties/import-export.c
index f9731d2..31186bd 100644
--- a/properties/import-export.c
+++ b/properties/import-export.c
@@ -58,6 +58,8 @@
 #define PKCS12_TAG "pkcs12 "
 #define PORT_TAG "port "
 #define PROTO_TAG "proto "
+#define PROXY_TAG "http-proxy "
+#define PROXY_RETRY_TAG "http-proxy-retry"
 #define REMOTE_TAG "remote "
 #define RENEG_SEC_TAG "reneg-sec "
 #define RPORT_TAG "rport "
@@ -197,6 +199,46 @@ parse_port (const char *str, const char *line)
 	return NULL;
 }
 
+static gboolean
+parse_http_proxy_auth (const char *str, char **out_user, char **out_pass)
+{
+	char *contents = NULL;
+	GError *error = NULL;
+	char **lines, **iter;
+
+	g_return_val_if_fail (out_user != NULL, FALSE);
+	g_return_val_if_fail (out_pass != NULL, FALSE);
+
+	if (!str || !strcmp (str, "stdin") || !strcmp (str, "auto"))
+		return TRUE;
+
+	/* Grab user/pass from authfile */
+	if (!g_file_get_contents (str, &contents, NULL, &error)) {
+		g_warning ("%s: unable to read HTTP proxy authfile '%s': (%d) %s",
+		           __func__, str, error ? error->code : -1,
+		           error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	lines = g_strsplit_set (contents, "\n\r", 0);
+	for (iter = lines; iter && *iter; iter++) {
+		if (!strlen (*iter))
+			continue;
+		if (!*out_user)
+			*out_user = g_strdup (g_strstrip (*iter));
+		else if (!*out_pass) {
+			*out_pass = g_strdup (g_strstrip (*iter));
+			break;
+		}
+	}
+	if (lines)
+		g_strfreev (lines);
+	g_free (contents);
+
+	return *out_user && *out_pass;
+}
+
 NMConnection *
 do_import (const char *path, char **lines, GError **error)
 {
@@ -347,6 +389,58 @@ do_import (const char *path, char **lines, GError **error)
 			continue;
 		}
 
+		if (!strncmp (*line, PROXY_RETRY_TAG, strlen (PROXY_RETRY_TAG))) {
+			nm_setting_vpn_add_data_item (s_vpn,
+			                              g_strdup (NM_OPENVPN_KEY_HTTP_PROXY_RETRY),
+			                              g_strdup ("yes"));
+			continue;
+		}
+
+		if (!strncmp (*line, PROXY_TAG, strlen (PROXY_TAG))) {
+			gboolean success = FALSE;
+
+			items = get_args (*line + strlen (PROXY_TAG));
+			if (!items)
+				continue;
+
+			if (g_strv_length (items) >= 2) {
+				glong port;
+				char *s_port = NULL;
+				char *user = NULL, *pass = NULL;
+
+				if (g_strv_length (items) >= 3)
+					success = parse_http_proxy_auth (items[2], &user, &pass);
+
+				if (success) {
+					success = FALSE;
+					errno = 0;
+					port = strtol (items[1], NULL, 10);
+					if ((errno == 0) && (port > 0) && (port < 65536)) {
+						s_port = g_strdup_printf ("%d", (guint32) port);
+						success = TRUE;
+					}
+				}
+
+				if (success) {
+					nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY, items[0]);
+					nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PORT, s_port);
+					if (user)
+						nm_setting_vpn_add_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME, user);
+					if (pass)
+						nm_setting_vpn_add_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD, pass);
+				}
+				g_free (s_port);
+				g_free (user);
+				g_free (pass);
+			}
+
+			if (!success)
+				g_warning ("%s: invalid http proxy port in option '%s'", __func__, *line);
+
+			g_strfreev (items);
+			continue;
+		}
+
 		if (!strncmp (*line, REMOTE_TAG, strlen (REMOTE_TAG))) {
 			items = get_args (*line + strlen (REMOTE_TAG));
 			if (!items)
diff --git a/properties/nm-openvpn-dialog.glade b/properties/nm-openvpn-dialog.glade
index 2e30529..af4c9dc 100644
--- a/properties/nm-openvpn-dialog.glade
+++ b/properties/nm-openvpn-dialog.glade
@@ -1394,6 +1394,217 @@ Example: /CN=myvpn.company.com&lt;/i&gt;</property>
                 <property name="type">tab</property>
               </packing>
             </child>
+            <child>
+              <widget class="GtkAlignment" id="alignment2">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="yalign">0</property>
+                <property name="yscale">0</property>
+                <property name="top_padding">12</property>
+                <property name="bottom_padding">12</property>
+                <property name="left_padding">12</property>
+                <property name="right_padding">12</property>
+                <child>
+                  <widget class="GtkTable" id="table1">
+                    <property name="visible">True</property>
+                    <property name="n_rows">6</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <widget class="GtkComboBox" id="proxy_type_combo">
+                        <property name="visible">True</property>
+                        <property name="button_sensitivity">on</property>
+                        <property name="items" translatable="yes">Not required</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Proxy Type:</property>
+                        <property name="justify">right</property>
+                      </widget>
+                      <packing>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="proxy_desc_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">&lt;i&gt;Select this option if your organization requires the use of a proxy server to access the Internet.&lt;/i&gt;</property>
+                        <property name="use_markup">True</property>
+                        <property name="wrap">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="proxy_server_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Server Address:</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment4">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="yalign">0</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <widget class="GtkHBox" id="hbox2">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkEntry" id="proxy_server_entry">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </widget>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="proxy_port_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">1</property>
+                                <property name="label" translatable="yes">Port:</property>
+                                <property name="justify">right</property>
+                              </widget>
+                              <packing>
+                                <property name="padding">6</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkSpinButton" id="proxy_port_spinbutton">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                                <property name="width_chars">5</property>
+                                <property name="adjustment">8080 1 65535 1 10 0</property>
+                                <property name="climb_rate">1</property>
+                                <property name="numeric">True</property>
+                              </widget>
+                              <packing>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="proxy_retry_checkbutton">
+                        <property name="label" translatable="yes">Retry indefinitely when errors occur</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="proxy_username_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Proxy Username:</property>
+                        <property name="justify">right</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="proxy_password_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Proxy Password:</property>
+                        <property name="justify">right</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="proxy_username_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="proxy_password_entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="visibility">False</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label32">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Proxies</property>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
           </widget>
           <packing>
             <property name="expand">False</property>
diff --git a/properties/nm-openvpn.c b/properties/nm-openvpn.c
index b885368..11dbe19 100644
--- a/properties/nm-openvpn.c
+++ b/properties/nm-openvpn.c
@@ -5,7 +5,7 @@
  * nm-openvpn.c : GNOME UI dialogs for configuring openvpn VPN connections
  *
  * Copyright (C) 2005 Tim Niemueller <tim niemueller de>
- * Copyright (C) 2008 Dan Williams, <dcbw redhat com>
+ * Copyright (C) 2008 - 2010 Dan Williams, <dcbw redhat com>
  * Based on work by David Zeuthen, <davidz redhat com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -395,7 +395,13 @@ hash_copy_advanced (gpointer key, gpointer data, gpointer user_data)
 	NMSettingVPN *s_vpn = NM_SETTING_VPN (user_data);
 	const char *value = (const char *) data;
 
-	nm_setting_vpn_add_data_item (s_vpn, (const char *) key, value);
+	g_return_if_fail (value && strlen (value));
+
+	/* HTTP Proxy password is a secret, not a data item */
+	if (!strcmp (value, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD))
+		nm_setting_vpn_add_secret (s_vpn, (const char *) key, value);
+	else
+		nm_setting_vpn_add_data_item (s_vpn, (const char *) key, value);
 }
 
 static const char *
@@ -479,6 +485,8 @@ save_secrets (NMVpnPluginUiWidgetInterface *iface,
 	auth_type = get_auth_type (priv->xml);
 	if (auth_type)
 		ret = auth_widget_save_secrets (priv->xml, auth_type, uuid, id);
+	if (ret)
+		ret = advanced_save_secrets (priv->advanced, uuid, id);
 
 	if (!ret)
 		g_set_error (error, OPENVPN_PLUGIN_UI_ERROR,
diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c
index b242243..2e27a82 100644
--- a/src/nm-openvpn-service.c
+++ b/src/nm-openvpn-service.c
@@ -70,6 +70,8 @@ typedef struct {
 	char *username;
 	char *password;
 	char *priv_key_pass;
+	char *proxy_username;
+	char *proxy_password;
 	GIOChannel *socket_channel;
 	guint socket_channel_eventid;
 } NMOpenvpnPluginIOData;
@@ -102,6 +104,10 @@ static ValidProperty valid_properties[] = {
 	{ NM_OPENVPN_KEY_MSSFIX,               G_TYPE_BOOLEAN, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_PROTO_TCP,            G_TYPE_BOOLEAN, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_PORT,                 G_TYPE_INT, 1, 65535, FALSE },
+	{ NM_OPENVPN_KEY_HTTP_PROXY,           G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_HTTP_PROXY_PORT,      G_TYPE_INT, 1, 65535, FALSE },
+	{ NM_OPENVPN_KEY_HTTP_PROXY_RETRY,     G_TYPE_BOOLEAN, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_HTTP_PROXY_USERNAME,  G_TYPE_STRING, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_REMOTE,               G_TYPE_STRING, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_REMOTE_IP,            G_TYPE_STRING, 0, 0, TRUE },
 	{ NM_OPENVPN_KEY_RENEG_SECONDS,        G_TYPE_INT, 0, G_MAXINT, FALSE },
@@ -120,6 +126,7 @@ static ValidProperty valid_secrets[] = {
 	{ NM_OPENVPN_KEY_PASSWORD,             G_TYPE_STRING, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_CERTPASS,             G_TYPE_STRING, 0, 0, FALSE },
 	{ NM_OPENVPN_KEY_NOSECRET,             G_TYPE_STRING, 0, 0, FALSE },
+	{ NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD,  G_TYPE_STRING, 0, 0, FALSE },
 	{ NULL,                                G_TYPE_NONE, FALSE }
 };
 
@@ -283,8 +290,20 @@ nm_openvpn_disconnect_management_socket (NMOpenvpnPlugin *plugin)
 	g_io_channel_unref (io_data->socket_channel);
 
 	g_free (io_data->username);
+	g_free (io_data->proxy_username);
+
+	if (io_data->password)
+		memset (io_data->password, 0, strlen (io_data->password));
 	g_free (io_data->password);
 
+	if (io_data->priv_key_pass)
+		memset (io_data->priv_key_pass, 0, strlen (io_data->priv_key_pass));
+	g_free (io_data->priv_key_pass);
+
+	if (io_data->proxy_password)
+		memset (io_data->proxy_password, 0, strlen (io_data->proxy_password));
+	g_free (io_data->proxy_password);
+
 	g_free (priv->io_data);
 	priv->io_data = NULL;
 }
@@ -338,6 +357,33 @@ get_detail (const char *input, const char *prefix)
 	return ret;
 }
 
+static void
+write_user_pass (GIOChannel *channel,
+                 const char *authtype,
+                 const char *user,
+                 const char *pass)
+{
+	char *quser, *qpass, *buf;
+
+	/* Quote strings passed back to openvpn */
+	quser = ovpn_quote_string (user);
+	qpass = ovpn_quote_string (pass);
+	buf = g_strdup_printf ("username \"%s\" \"%s\"\n"
+	                       "password \"%s\" \"%s\"\n",
+	                       authtype, quser,
+	                       authtype, qpass);
+	memset (qpass, 0, strlen (qpass));
+	g_free (qpass);
+	g_free (quser);
+
+	/* Will always write everything in blocking channels (on success) */
+	g_io_channel_write_chars (channel, buf, strlen (buf), NULL, NULL);
+	g_io_channel_flush (channel, NULL);
+
+	memset (buf, 0, strlen (buf));
+	g_free (buf);
+}
+
 static gboolean
 handle_management_socket (NMVPNPlugin *plugin,
                           GIOChannel *source,
@@ -347,7 +393,6 @@ handle_management_socket (NMVPNPlugin *plugin,
 	NMOpenvpnPluginIOData *io_data = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin)->io_data;
 	gboolean again = TRUE;
 	char *str = NULL, *auth = NULL, *buf;
-	gsize written;
 
 	if (!(condition & G_IO_IN))
 		return TRUE;
@@ -361,25 +406,9 @@ handle_management_socket (NMVPNPlugin *plugin,
 	auth = get_detail (str, ">PASSWORD:Need '");
 	if (auth) {
 		if (strcmp (auth, "Auth") == 0) {
-			if (io_data->username != NULL && io_data->password != NULL) {
-				char *quser, *qpass;
-
-				/* Quote strings passed back to openvpn */
-				quser = ovpn_quote_string (io_data->username);
-				qpass = ovpn_quote_string (io_data->password);
-				buf = g_strdup_printf ("username \"%s\" \"%s\"\n"
-				                       "password \"%s\" \"%s\"\n",
-				                       auth, quser,
-				                       auth, qpass);
-				memset (qpass, 0, strlen (qpass));
-				g_free (qpass);
-				g_free (quser);
-
-				/* Will always write everything in blocking channels (on success) */
-				g_io_channel_write_chars (source, buf, strlen (buf), &written, NULL);
-				g_io_channel_flush (source, NULL);
-				g_free (buf);
-			} else
+			if (io_data->username != NULL && io_data->password != NULL)
+				write_user_pass (source, auth, io_data->username, io_data->password);
+			else
 				nm_warning ("Auth requested but one of username or password is missing");
 		} else if (!strcmp (auth, "Private Key")) {
 			if (io_data->priv_key_pass) {
@@ -392,11 +421,16 @@ handle_management_socket (NMVPNPlugin *plugin,
 				g_free (qpass);
 
 				/* Will always write everything in blocking channels (on success) */
-				g_io_channel_write_chars (source, buf, strlen (buf), &written, NULL);
+				g_io_channel_write_chars (source, buf, strlen (buf), NULL, NULL);
 				g_io_channel_flush (source, NULL);
 				g_free (buf);
 			} else
 				nm_warning ("Certificate password requested but private key password == NULL");
+		} else if (strcmp (auth, "HTTP Proxy") == 0) {
+			if (io_data->proxy_username != NULL && io_data->proxy_password != NULL)
+				write_user_pass (source, auth, io_data->proxy_username, io_data->proxy_password);
+			else
+				nm_warning ("HTTP Proxy auth requested but either proxy username or password is missing");
 		} else {
 			nm_warning ("No clue what to send for username/password request for '%s'", auth);
 			if (out_failure)
@@ -686,7 +720,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                                  GError **error)
 {
 	NMOpenvpnPluginPrivate *priv = NM_OPENVPN_PLUGIN_GET_PRIVATE (plugin);
-	const char *openvpn_binary, *auth, *connection_type, *tmp;
+	const char *openvpn_binary, *auth, *connection_type, *tmp, *tmp2;
 	GPtrArray *args;
 	GSource *openvpn_watch;
 	GPid pid;
@@ -734,6 +768,19 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
 		add_openvpn_arg (args, tmp);
 	}
 
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY);
+	tmp2 = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PORT);
+	if (tmp && strlen (tmp) && tmp2 && strlen (tmp2)) {
+		add_openvpn_arg (args, "--http-proxy");
+		add_openvpn_arg (args, tmp);
+		add_openvpn_arg (args, tmp2);
+		add_openvpn_arg (args, "'auto'");  /* Automatic proxy auth method detection */
+	}
+
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_RETRY);
+	if (tmp && strlen (tmp))
+		add_openvpn_arg (args, "--http-proxy-retry");
+
 	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_COMP_LZO);
 	if (tmp && !strcmp (tmp, "yes"))
 		add_openvpn_arg (args, "--comp-lzo");
@@ -988,24 +1035,28 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
 	*/
 	if (   !strcmp (connection_type, NM_OPENVPN_CONTYPE_TLS)
 	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD)
-	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
-		NMOpenvpnPluginIOData *io_data;
+	    || !strcmp (connection_type, NM_OPENVPN_CONTYPE_PASSWORD_TLS)
+	    || nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME)) {
 
-		io_data = g_new0 (NMOpenvpnPluginIOData, 1);
+		priv->io_data = g_malloc0 (sizeof (NMOpenvpnPluginIOData));
 
 		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_USERNAME);
-		io_data->username = tmp ? g_strdup (tmp) : NULL;
+		priv->io_data->username = tmp ? g_strdup (tmp) : NULL;
 		/* Use the default username if it wasn't overridden by the user */
-		if (!io_data->username && default_username)
-			io_data->username = g_strdup (default_username);
+		if (!priv->io_data->username && default_username)
+			priv->io_data->username = g_strdup (default_username);
 
 		tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_PASSWORD);
-		io_data->password = tmp ? g_strdup (tmp) : NULL;
+		priv->io_data->password = tmp ? g_strdup (tmp) : NULL;
 
 		tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_CERTPASS);
-		io_data->priv_key_pass = tmp ? g_strdup (tmp) : NULL;
+		priv->io_data->priv_key_pass = tmp ? g_strdup (tmp) : NULL;
 
-		priv->io_data = io_data;
+		tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
+		priv->io_data->proxy_username = tmp ? g_strdup (tmp) : NULL;
+
+		tmp = nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD);
+		priv->io_data->proxy_password = tmp ? g_strdup (tmp) : NULL;
 
 		nm_openvpn_schedule_connect_timer (plugin);
 	}
@@ -1049,6 +1100,11 @@ check_need_secrets (NMSettingVPN *s_vpn, gboolean *need_secrets)
 		/* Static key doesn't need passwords */
 	}
 
+	/* HTTP Proxy might require a password; assume so if there's an HTTP proxy username */
+	tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_USERNAME);
+	if (tmp && !nm_setting_vpn_get_secret (s_vpn, NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD))
+		*need_secrets = TRUE;
+
 	return ctype;
 }
 
diff --git a/src/nm-openvpn-service.h b/src/nm-openvpn-service.h
index f1e3f0d..c8a7619 100644
--- a/src/nm-openvpn-service.h
+++ b/src/nm-openvpn-service.h
@@ -50,6 +50,10 @@
 #define NM_OPENVPN_KEY_MSSFIX "mssfix"
 #define NM_OPENVPN_KEY_PORT "port"
 #define NM_OPENVPN_KEY_PROTO_TCP "proto-tcp"
+#define NM_OPENVPN_KEY_HTTP_PROXY "http-proxy"
+#define NM_OPENVPN_KEY_HTTP_PROXY_PORT "http-proxy-port"
+#define NM_OPENVPN_KEY_HTTP_PROXY_RETRY "http-proxy-retry"
+#define NM_OPENVPN_KEY_HTTP_PROXY_USERNAME "http-proxy-username"
 #define NM_OPENVPN_KEY_REMOTE "remote"
 #define NM_OPENVPN_KEY_REMOTE_IP "remote-ip"
 #define NM_OPENVPN_KEY_STATIC_KEY "static-key"
@@ -63,6 +67,7 @@
 
 #define NM_OPENVPN_KEY_PASSWORD "password"
 #define NM_OPENVPN_KEY_CERTPASS "cert-pass"
+#define NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD "http-proxy-password"
 /* Internal auth-dialog -> service token indicating that no secrets are
  * required for the connection.
  */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]