changeset 6738:6c95f01aaf49

[gaim-migrate @ 7270] Added optional GNUTLS support, which will also be used as a fallback if Mozilla NSS is not installed. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Thu, 04 Sep 2003 06:19:25 +0000
parents 05098da416bb
children 2beae1058ca4
files configure.ac src/Makefile.am src/protocols/msn/session.c src/ssl-gnutls.c src/ssl-nss.c src/sslconn.c src/sslconn.h
diffstat 7 files changed, 604 insertions(+), 262 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Thu Sep 04 05:36:09 2003 +0000
+++ b/configure.ac	Thu Sep 04 06:19:25 2003 +0000
@@ -388,6 +388,8 @@
 		nss_manual_check="no"
 	fi
 
+	enable_nss="no"
+
 	if test "x$nss_manual_check" = "xno"; then
 		PKG_CHECK_MODULES(NSS, mozilla-nss, have_nss="yes", [])
 
@@ -399,6 +401,7 @@
 			AC_DEFINE(HAVE_SSL, 1, [Define if you have SSL])
 
 			msg_ssl="yes (Mozilla NSS)"
+			enable_nss="yes"
 		else
 			nss_manual_check="yes"
 		fi
@@ -572,6 +575,8 @@
 				else
 					msg_ssl="yes (Mozilla NSS)"
 				fi
+
+				enable_nss="yes"
 			else
 				NSS_CFLAGS=""
 				NSPR_CFLAGS=""
@@ -590,11 +595,16 @@
 	AC_SUBST(NSS_LIBS)
 fi
 
+AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes")
+
 dnl #
 dnl # Check for GNUTLS if it's specified or if NSS failed.
 dnl #
 if test "x$msg_ssl" = "xno" -o "x$enable_gnutls" != "xno"; then
+
+	enable_gnutls="no"
 	prefix=`eval echo $prefix`
+
 	AC_ARG_WITH(gnutls-includes,
 		[  --with-gnutls-includes=PREFIX   Location of GNUTLS includes.],
 		[ with_gnutls_includes="$withval" ],
@@ -651,6 +661,8 @@
 			AC_DEFINE(HAVE_SSL)
 			msg_ssl="yes (GNUTLS)"
 			GNUTLS_LIBS="$with_gnutls_libs -lgnutls -lgcrypt"
+
+			enable_gnutls="yes"
 		else
 			GNUTLS_CFLAGS=""
 			GNUTLS_LIBS=""
@@ -667,6 +679,8 @@
 AC_SUBST(GNUTLS_CFLAGS)
 AC_SUBST(GNUTLS_LIBS)
 
+AM_CONDITIONAL(USE_GNUTLS, test "x$enable_gnutls" = "xyes")
+
 dnl Check for Tcl
 if test "$enable_tcl" = yes; then
 	AC_MSG_CHECKING([for tclConfig.sh])
--- a/src/Makefile.am	Thu Sep 04 05:36:09 2003 +0000
+++ b/src/Makefile.am	Thu Sep 04 06:19:25 2003 +0000
@@ -4,6 +4,8 @@
 		getopt1.c \
 		Makefile.mingw \
 		win_gaim.c \
+		ssl-nss.c \
+		ssl-gnutls.c \
 		win32/IdleTracker/Makefile.mingw \
 		win32/IdleTracker/idletrack.c \
 		win32/IdleTracker/idletrack.h \
@@ -43,6 +45,14 @@
 
 SUBDIRS = protocols
 
+if USE_NSS
+SSLSOURCES = ssl-nss.c
+endif
+
+if USE_GNUTLS
+SSLSOURCES = ssl-gnutls.c
+endif
+
 CORESOURCES = \
 	account.c \
 	account.h \
@@ -94,7 +104,8 @@
 	util.c \
 	util.h \
 	value.c \
-	value.h
+	value.h \
+	$(SSLSOURCES)
 
 bin_PROGRAMS = gaim gaim-remote
 gaim_SOURCES = \
@@ -165,7 +176,8 @@
 	$(SM_LIBS) \
 	$(INTLLIBS) \
 	$(GTKSPELL_LIBS) \
-	$(NSS_LIBS)
+	$(NSS_LIBS) \
+	$(GNUTLS_LIBS)
 
 gaim_remote_SOURCES = \
 	gaim-remote.c
@@ -185,4 +197,5 @@
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
-	$(NSS_CFLAGS)
+	$(NSS_CFLAGS) \
+	$(GNUTLS_CFLAGS)
--- a/src/protocols/msn/session.c	Thu Sep 04 05:36:09 2003 +0000
+++ b/src/protocols/msn/session.c	Thu Sep 04 06:19:25 2003 +0000
@@ -39,7 +39,7 @@
 	session->users  = msn_users_new();
 	session->groups = msn_groups_new();
 
-#ifdef HAVE_NSS
+#ifdef HAVE_SSL
 	session->protocol_ver = 9;
 #else
 	session->protocol_ver = 7;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl-gnutls.c	Thu Sep 04 06:19:25 2003 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file ssl-gnutls.c SSL Operations for GNUTLS
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "debug.h"
+#include "sslconn.h"
+
+#include <gnutls/gnutls.h>
+
+typedef struct
+{
+	gnutls_session session;
+	gnutls_certificate_client_credentials xcred;
+
+} GaimSslGnutlsData;
+
+#define GAIM_SSL_GNUTLS_DATA(gsc) ((GaimSslGnutlsData *)gsc->private_data)
+
+static gnutls_certificate_client_credentials xcred;
+
+static gboolean
+ssl_gnutls_init(void)
+{
+	gnutls_global_init();
+
+	gnutls_certificate_allocate_credentials(&xcred);
+	gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", GNUTLS_X509_FMT_PEM);
+
+	return TRUE;
+}
+
+static void
+ssl_gnutls_uninit(void)
+{
+	gnutls_global_deinit();
+
+	gnutls_certificate_free_credentials(xcred);
+}
+
+static void
+ssl_gnutls_connect_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	GaimSslConnection *gsc = (GaimSslConnection *)data;
+	GaimSslGnutlsData *gnutls_data;
+	int ret;
+	const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 };
+
+	gsc->fd = source;
+
+	gnutls_data = g_new0(GaimSslGnutlsData, 1);
+	gsc->private_data = gnutls_data;
+
+	gnutls_init(&gnutls_data->session, GNUTLS_CLIENT);
+	gnutls_set_default_priority(gnutls_data->session);
+
+	gnutls_certificate_type_set_priority(gnutls_data->session,
+										 cert_type_priority);
+
+	gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE,
+						   xcred);
+
+	gnutls_transport_set_ptr(gnutls_data->session, GINT_TO_POINTER(source));
+
+	gaim_debug_info("gnutls", "Handshaking\n");
+	ret = gnutls_handshake(gnutls_data->session);
+
+	if (ret < 0)
+	{
+	}
+	else
+	{
+	gaim_debug_info("gnutls", "Calling input function\n");
+	gsc->input_func(gsc->user_data, (GaimSslConnection *)gsc, cond);
+	}
+}
+
+static void
+ssl_gnutls_close(GaimSslConnection *gsc)
+{
+	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
+
+	gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR);
+
+	gnutls_deinit(gnutls_data->session);
+//	gnutls_certificate_free_credentials(gnutls_data->xcred);
+
+	g_free(gnutls_data);
+}
+
+static size_t
+ssl_gnutls_read(GaimSslConnection *gsc, void *data, size_t len)
+{
+	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
+	int s;
+
+	s = gnutls_record_recv(gnutls_data->session, data, len);
+
+	if (s < 0)
+		s = 0;
+
+	gaim_debug_misc("gnutls", "s = %d\n", s);
+
+	return s;
+}
+
+static size_t
+ssl_gnutls_write(GaimSslConnection *gsc, const void *data, size_t len)
+{
+	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
+	size_t s;
+
+	gaim_debug_misc("gnutls", "Writing: {%s}\n", data);
+
+	s = gnutls_record_send(gnutls_data->session, data, len);
+}
+
+static GaimSslOps ssl_ops =
+{
+	ssl_gnutls_init,
+	ssl_gnutls_uninit,
+	ssl_gnutls_connect_cb,
+	ssl_gnutls_close,
+	ssl_gnutls_read,
+	ssl_gnutls_write
+};
+
+GaimSslOps *
+gaim_ssl_gnutls_get_ops()
+{
+	return &ssl_ops;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl-nss.c	Thu Sep 04 06:19:25 2003 +0000
@@ -0,0 +1,273 @@
+/**
+ * @file ssl-nss.c SSL Operations for Mozilla NSS
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "debug.h"
+#include "sslconn.h"
+
+#include <nspr.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <prio.h>
+#include <secerr.h>
+#include <secmod.h>
+#include <ssl.h>
+#include <sslerr.h>
+#include <sslproto.h>
+
+typedef struct
+{
+	PRFileDesc *fd;
+	PRFileDesc *in;
+
+} GaimSslNssData;
+
+#define GAIM_SSL_NSS_DATA(gsc) ((GaimSslNssData *)gsc->private_data)
+
+static const PRIOMethods *_nss_methods = NULL;
+static PRDescIdentity _identity;
+
+static SECStatus
+ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig,
+			  PRBool is_server)
+{
+	return SECSuccess;
+
+#if 0
+	CERTCertificate *cert;
+	void *pinArg;
+	SECStatus status;
+
+	cert = SSL_PeerCertificate(socket);
+	pinArg = SSL_RevealPinArg(socket);
+
+	status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
+								certUsageSSLClient, pinArg);
+
+	if (status != SECSuccess) {
+		gaim_debug_error("nss", "CERT_VerifyCertNow failed\n");
+		CERT_DestroyCertificate(cert);
+		return status;
+	}
+
+	CERT_DestroyCertificate(cert);
+	return SECSuccess;
+#endif
+}
+
+SECStatus
+ssl_bad_cert(void *arg, PRFileDesc *socket)
+{
+	SECStatus status = SECFailure;
+	PRErrorCode err;
+
+	if (arg == NULL)
+		return status;
+
+	*(PRErrorCode *)arg = err = PORT_GetError();
+
+	switch (err)
+	{
+		case SEC_ERROR_INVALID_AVA:
+		case SEC_ERROR_INVALID_TIME:
+		case SEC_ERROR_BAD_SIGNATURE:
+		case SEC_ERROR_EXPIRED_CERTIFICATE:
+		case SEC_ERROR_UNKNOWN_ISSUER:
+		case SEC_ERROR_UNTRUSTED_CERT:
+		case SEC_ERROR_CERT_VALID:
+		case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+		case SEC_ERROR_CRL_EXPIRED:
+		case SEC_ERROR_CRL_BAD_SIGNATURE:
+		case SEC_ERROR_EXTENSION_VALUE_INVALID:
+		case SEC_ERROR_CA_CERT_INVALID:
+		case SEC_ERROR_CERT_USAGES_INVALID:
+		case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+			status = SECSuccess;
+			break;
+
+		default:
+			status = SECFailure;
+			break;
+	}
+
+	gaim_debug_error("nss", "Bad certificate: %d\n");
+
+	return status;
+}
+
+static void
+input_func(gpointer data, gint source, GaimInputCondition cond)
+{
+	GaimSslConnection *gsc = (GaimSslConnection *)data;
+	GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc);
+	char *cp, *ip, *sp;
+	int op, kp0, kp1;
+	int result;
+
+	result = SSL_SecurityStatus(nss_data->in, &op, &cp, &kp0,
+								&kp1, &ip, &sp);
+
+	gaim_debug_misc("nss",
+		"bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
+		"subject DN: %s\n"
+		"issuer  DN: %s\n",
+		cp, kp1, kp0, op, sp, ip);
+
+	PR_Free(cp);
+	PR_Free(ip);
+	PR_Free(sp);
+
+	gsc->input_func(gsc->user_data, gsc, cond);
+}
+
+static gboolean
+ssl_nss_init(void)
+{
+	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+	NSS_NoDB_Init(NULL);
+
+	/* TODO: Fix this so autoconf does the work trying to find this lib. */
+	SECMOD_AddNewModule("Builtins", LIBDIR "/libnssckbi.so", 0, 0);
+	NSS_SetDomesticPolicy();
+
+	_identity = PR_GetUniqueIdentity("Gaim");
+	_nss_methods = PR_GetDefaultIOMethods();
+
+	return TRUE;
+}
+
+static void
+ssl_nss_uninit(void)
+{
+	PR_Cleanup();
+
+	_nss_methods = NULL;
+}
+
+static void
+ssl_nss_connect_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	GaimSslConnection *gsc = (GaimSslConnection *)data;
+	GaimSslNssData *nss_data = g_new0(GaimSslNssData, 1);
+	PRSocketOptionData socket_opt;
+
+	gsc->private_data = nss_data;
+
+	gsc->fd = source;
+
+	nss_data->fd = PR_ImportTCPSocket(gsc->fd);
+
+	if (nss_data->fd == NULL)
+	{
+		gaim_debug_error("nss", "nss_data->fd == NULL!\n");
+
+		gaim_ssl_close((GaimSslConnection *)gsc);
+
+		return;
+	}
+
+	socket_opt.option = PR_SockOpt_Nonblocking;
+	socket_opt.value.non_blocking = PR_FALSE;
+
+	PR_SetSocketOption(nss_data->fd, &socket_opt);
+
+	nss_data->in = SSL_ImportFD(NULL, nss_data->fd);
+
+	if (nss_data->in == NULL)
+	{
+		gaim_debug_error("nss", "nss_data->in == NUL!\n");
+
+		gaim_ssl_close((GaimSslConnection *)gsc);
+
+		return;
+	}
+
+	SSL_OptionSet(nss_data->in, SSL_SECURITY,            PR_TRUE);
+	SSL_OptionSet(nss_data->in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+
+	SSL_AuthCertificateHook(nss_data->in,
+							(SSLAuthCertificate)ssl_auth_cert,
+							(void *)CERT_GetDefaultCertDB());
+	SSL_BadCertHook(nss_data->in, (SSLBadCertHandler)ssl_bad_cert, NULL);
+
+	SSL_SetURL(nss_data->in, gsc->host);
+
+	SSL_ResetHandshake(nss_data->in, PR_FALSE);
+
+	if (SSL_ForceHandshake(nss_data->in))
+	{
+		gaim_debug_error("nss", "Handshake failed\n");
+
+		gaim_ssl_close((GaimSslConnection *)gsc);
+
+		return;
+	}
+
+#if 0
+	gsc->input_func(gsc->user_data, (GaimSslConnection *)gsc,
+						 cond);
+#endif
+
+	input_func(gsc, source, cond);
+}
+
+static void
+ssl_nss_close(GaimSslConnection *gsc)
+{
+	GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc);
+
+	if (nss_data->in) PR_Close(nss_data->in);
+	if (nss_data->fd) PR_Close(nss_data->fd);
+
+	g_free(nss_data);
+}
+
+static size_t
+ssl_nss_read(GaimSslConnection *gsc, void *data, size_t len)
+{
+	GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc);
+
+	return PR_Read(nss_data->in, data, len);
+}
+
+static size_t
+ssl_nss_write(GaimSslConnection *gsc, const void *data, size_t len)
+{
+	GaimSslNssData *nss_data = GAIM_SSL_NSS_DATA(gsc);
+
+	return PR_Write(nss_data->in, data, len);
+}
+
+static GaimSslOps ssl_ops =
+{
+	ssl_nss_init,
+	ssl_nss_uninit,
+	ssl_nss_connect_cb,
+	ssl_nss_close,
+	ssl_nss_read,
+	ssl_nss_write
+};
+
+GaimSslOps *
+gaim_ssl_nss_get_ops()
+{
+	return &ssl_ops;
+}
--- a/src/sslconn.c	Thu Sep 04 05:36:09 2003 +0000
+++ b/src/sslconn.c	Thu Sep 04 06:19:25 2003 +0000
@@ -25,235 +25,41 @@
 #include "debug.h"
 #include "sslconn.h"
 
+/* Pre-installed SSL op functions. */
 #ifdef HAVE_NSS
-# include <nspr.h>
-# include <nss.h>
-# include <pk11func.h>
-# include <prio.h>
-# include <secerr.h>
-# include <secmod.h>
-# include <ssl.h>
-# include <sslerr.h>
-# include <sslproto.h>
-
-typedef struct
-{
-	char *host;
-	int port;
-	void *user_data;
-	GaimSslInputFunction input_func;
-
-	int fd;
-	int inpa;
-
-	PRFileDesc *nss_fd;
-	PRFileDesc *nss_in;
-
-} GaimSslData;
-
-static gboolean _nss_initialized = FALSE;
-static const PRIOMethods *_nss_methods = NULL;
-static PRDescIdentity _identity;
-
-static void
-destroy_ssl_data(GaimSslData *data)
-{
-	if (data->inpa)   gaim_input_remove(data->inpa);
-	if (data->nss_in) PR_Close(data->nss_in);
-	if (data->nss_fd) PR_Close(data->nss_fd);
-	if (data->fd)     close(data->fd);
-
-	if (data->host != NULL)
-		g_free(data->host);
-
-	g_free(data);
-}
-
-static void
-init_nss(void)
-{
-	if (_nss_initialized)
-		return;
-
-	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-	NSS_NoDB_Init(NULL);
-
-	/* TODO: Fix this so autoconf does the work trying to find this lib. */
-	SECMOD_AddNewModule("Builtins", LIBDIR "/libnssckbi.so", 0, 0);
-	NSS_SetDomesticPolicy();
-
-	_identity = PR_GetUniqueIdentity("Gaim");
-	_nss_methods = PR_GetDefaultIOMethods();
-
-	_nss_initialized = TRUE;
-}
-
-static SECStatus
-ssl_auth_cert(void *arg, PRFileDesc *socket, PRBool checksig, PRBool is_server)
-{
-	return SECSuccess;
-
-#if 0
-	CERTCertificate *cert;
-	void *pinArg;
-	SECStatus status;
-
-	cert = SSL_PeerCertificate(socket);
-	pinArg = SSL_RevealPinArg(socket);
-
-	status = CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
-								certUsageSSLClient, pinArg);
-
-	if (status != SECSuccess) {
-		gaim_debug(GAIM_DEBUG_ERROR, "msn", "CERT_VerifyCertNow failed\n");
-		CERT_DestroyCertificate(cert);
-		return status;
-	}
-
-	CERT_DestroyCertificate(cert);
-	return SECSuccess;
+GaimSslOps *gaim_ssl_nss_get_ops();
 #endif
-}
-
-SECStatus
-ssl_bad_cert(void *arg, PRFileDesc *socket)
-{
-	SECStatus status = SECFailure;
-	PRErrorCode err;
-
-	if (arg == NULL)
-		return status;
-
-	*(PRErrorCode *)arg = err = PORT_GetError();
 
-	switch (err)
-	{
-		case SEC_ERROR_INVALID_AVA:
-		case SEC_ERROR_INVALID_TIME:
-		case SEC_ERROR_BAD_SIGNATURE:
-		case SEC_ERROR_EXPIRED_CERTIFICATE:
-		case SEC_ERROR_UNKNOWN_ISSUER:
-		case SEC_ERROR_UNTRUSTED_CERT:
-		case SEC_ERROR_CERT_VALID:
-		case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-		case SEC_ERROR_CRL_EXPIRED:
-		case SEC_ERROR_CRL_BAD_SIGNATURE:
-		case SEC_ERROR_EXTENSION_VALUE_INVALID:
-		case SEC_ERROR_CA_CERT_INVALID:
-		case SEC_ERROR_CERT_USAGES_INVALID:
-		case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
-			status = SECSuccess;
-			break;
-
-		default:
-			status = SECFailure;
-			break;
-	}
-
-	gaim_debug(GAIM_DEBUG_ERROR, "msn",
-			   "Bad certificate: %d\n");
-
-	return status;
-}
-
-static void
-input_func(gpointer data, gint source, GaimInputCondition cond)
-{
-	GaimSslData *ssl_data = (GaimSslData *)data;
-	char *cp, *ip, *sp;
-	int op, kp0, kp1;
-	int result;
-
-	result = SSL_SecurityStatus(ssl_data->nss_in, &op, &cp, &kp0,
-								&kp1, &ip, &sp);
-
-	gaim_debug(GAIM_DEBUG_MISC, "msn",
-		"bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
-		"subject DN: %s\n"
-		"issuer  DN: %s\n",
-		cp, kp1, kp0, op, sp, ip);
-
-	PR_Free(cp);
-	PR_Free(ip);
-	PR_Free(sp);
-
-	ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
-						 cond);
-}
-
-static void
-ssl_connect_cb(gpointer data, gint source, GaimInputCondition cond)
-{
-	PRSocketOptionData socket_opt;
-	GaimSslData *ssl_data = (GaimSslData *)data;
-
-	if (!_nss_initialized)
-		init_nss();
-
-	ssl_data->fd = source;
-
-	ssl_data->nss_fd = PR_ImportTCPSocket(ssl_data->fd);
-
-	if (ssl_data->nss_fd == NULL)
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_fd == NULL!\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-	socket_opt.option = PR_SockOpt_Nonblocking;
-	socket_opt.value.non_blocking = PR_FALSE;
-
-	PR_SetSocketOption(ssl_data->nss_fd, &socket_opt);
-
-	ssl_data->nss_in = SSL_ImportFD(NULL, ssl_data->nss_fd);
-
-	if (ssl_data->nss_in == NULL)
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "nss_in == NUL!\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-	SSL_OptionSet(ssl_data->nss_in, SSL_SECURITY,            PR_TRUE);
-	SSL_OptionSet(ssl_data->nss_in, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
-
-	SSL_AuthCertificateHook(ssl_data->nss_in,
-							(SSLAuthCertificate)ssl_auth_cert,
-							(void *)CERT_GetDefaultCertDB());
-	SSL_BadCertHook(ssl_data->nss_in, (SSLBadCertHandler)ssl_bad_cert, NULL);
-
-	SSL_SetURL(ssl_data->nss_in, ssl_data->host);
-
-	SSL_ResetHandshake(ssl_data->nss_in, PR_FALSE);
-
-	if (SSL_ForceHandshake(ssl_data->nss_in))
-	{
-		gaim_debug(GAIM_DEBUG_ERROR, "ssl", "Handshake failed\n");
-
-		destroy_ssl_data(ssl_data);
-
-		return;
-	}
-
-#if 0
-	ssl_data->input_func(ssl_data->user_data, (GaimSslConnection *)ssl_data,
-						 cond);
+#ifdef HAVE_GNUTLS
+GaimSslOps *gaim_ssl_gnutls_get_ops();
 #endif
 
-	input_func(ssl_data, source, cond);
+
+static gboolean _ssl_initialized = FALSE;
+static GaimSslOps *_ssl_ops = NULL;
+
+static gboolean
+ssl_init(void)
+{
+	GaimSslOps *ops = gaim_ssl_get_ops();
+	gboolean success = FALSE;
+
+	if (_ssl_initialized)
+		return FALSE;
+
+	if (ops != NULL && ops->init != NULL)
+		success = ops->init();
+
+	_ssl_initialized = success;
+
+	return success;
 }
-#endif /* HAVE_NSS */
 
 gboolean
 gaim_ssl_is_supported(void)
 {
-#ifdef HAVE_NSS
-	return TRUE;
+#ifdef HAVE_SSL
+	return (gaim_ssl_get_ops() != NULL);
 #else
 	return FALSE;
 #endif
@@ -263,95 +69,138 @@
 gaim_ssl_connect(GaimAccount *account, const char *host, int port,
 				 GaimSslInputFunction func, void *data)
 {
-#ifdef HAVE_NSS
+	GaimSslConnection *gsc;
+	GaimSslOps *ops;
 	int i;
-	GaimSslData *ssl_data;
 
 	g_return_val_if_fail(host != NULL,            NULL);
 	g_return_val_if_fail(port != 0 && port != -1, NULL);
 	g_return_val_if_fail(func != NULL,            NULL);
 	g_return_val_if_fail(gaim_ssl_is_supported(), NULL);
 
-	ssl_data = g_new0(GaimSslData, 1);
+	ops = gaim_ssl_get_ops();
+
+	g_return_val_if_fail(ops != NULL, NULL);
+	g_return_val_if_fail(ops->connect_cb != NULL, NULL);
 
-	ssl_data->host       = g_strdup(host);
-	ssl_data->port       = port;
-	ssl_data->user_data  = data;
-	ssl_data->input_func = func;
+	if (!_ssl_initialized)
+	{
+		if (!ssl_init())
+			return NULL;
+	}
 
-	i = gaim_proxy_connect(account, host, port, ssl_connect_cb, ssl_data);
+	gsc = g_new0(GaimSslConnection, 1);
+
+	gsc->host       = g_strdup(host);
+	gsc->port       = port;
+	gsc->user_data  = data;
+	gsc->input_func = func;
+
+	i = gaim_proxy_connect(account, host, port, ops->connect_cb, gsc);
 
 	if (i < 0)
 	{
-		g_free(ssl_data->host);
-		g_free(ssl_data);
+		g_free(gsc->host);
+		g_free(gsc);
 
 		return NULL;
 	}
 
-	return (GaimSslConnection)ssl_data;
-#else
-	return GINT_TO_POINTER(-1);
-#endif
+	return (GaimSslConnection *)gsc;
 }
 
 void
 gaim_ssl_close(GaimSslConnection *gsc)
 {
+	GaimSslOps *ops;
+
 	g_return_if_fail(gsc != NULL);
 
-#ifdef HAVE_NSS
-	destroy_ssl_data((GaimSslData *)gsc);
-#endif
+	ops = gaim_ssl_get_ops();
+
+	if (gsc->inpa)
+		gaim_input_remove(gsc->inpa);
+
+	if (ops != NULL && ops->close != NULL)
+		ops->close(gsc);
+
+	if (gsc->fd)
+		close(gsc->fd);
+
+	if (gsc->host != NULL)
+		g_free(gsc->host);
+
+	g_free(gsc);
 }
 
 size_t
 gaim_ssl_read(GaimSslConnection *gsc, void *data, size_t len)
 {
-#ifdef HAVE_NSS
-	GaimSslData *ssl_data = (GaimSslData *)gsc;
+	GaimSslOps *ops;
 
 	g_return_val_if_fail(gsc  != NULL, 0);
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(len  >  0,    0);
 
-	return PR_Read(ssl_data->nss_in, data, len);
-#else
+	ops = gaim_ssl_get_ops();
+
+	if (ops != NULL && ops->read != NULL)
+		return ops->read(gsc, data, len);
+
 	return 0;
-#endif
 }
 
 size_t
 gaim_ssl_write(GaimSslConnection *gsc, const void *data, size_t len)
 {
-#ifdef HAVE_NSS
-	GaimSslData *ssl_data = (GaimSslData *)gsc;
+	GaimSslOps *ops;
 
 	g_return_val_if_fail(gsc  != NULL, 0);
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(len  >  0,    0);
 
-	return PR_Write(ssl_data->nss_in, data, len);
-#else
+	ops = gaim_ssl_get_ops();
+
+	if (ops != NULL && ops->write != NULL)
+		return ops->write(gsc, data, len);
+
 	return 0;
-#endif
+}
+
+void
+gaim_ssl_set_ops(GaimSslOps *ops)
+{
+	_ssl_ops = ops;
+}
+
+GaimSslOps *
+gaim_ssl_get_ops(void)
+{
+	return _ssl_ops;
 }
 
 void
 gaim_ssl_init(void)
 {
+#if defined(HAVE_NSS)
+	gaim_ssl_set_ops(gaim_ssl_nss_get_ops());
+#elif defined(HAVE_GNUTLS)
+	gaim_ssl_set_ops(gaim_ssl_gnutls_get_ops());
+#endif
 }
 
 void
 gaim_ssl_uninit(void)
 {
-#ifdef HAVE_NSS
-	if (!_nss_initialized)
+	GaimSslOps *ops;
+
+	if (!_ssl_initialized)
 		return;
 
-	PR_Cleanup();
+	ops = gaim_ssl_get_ops();
 
-	_nss_initialized = FALSE;
-	_nss_methods     = NULL;
-#endif
+	if (ops != NULL && ops->uninit != NULL)
+		ops->uninit();
+
+	_ssl_initialized = FALSE;
 }
--- a/src/sslconn.h	Thu Sep 04 05:36:09 2003 +0000
+++ b/src/sslconn.h	Thu Sep 04 06:19:25 2003 +0000
@@ -25,16 +25,46 @@
 
 #include "proxy.h"
 
+#define GAIM_SSL_DEFAULT_PORT 443
+
+typedef struct _GaimSslConnection GaimSslConnection;
+
+typedef void (*GaimSslInputFunction)(gpointer, GaimSslConnection *,
+									 GaimInputCondition);
+
+struct _GaimSslConnection
+{
+	char *host;
+	int port;
+	void *user_data;
+	GaimSslInputFunction input_func;
+
+	int fd;
+	int inpa;
+
+	void *private_data;
+};
+
+/**
+ * SSL implementation operations structure.
+ *
+ * Every SSL implementation must provide one of these and register it.
+ */
+typedef struct
+{
+	gboolean (*init)(void);
+	void (*uninit)(void);
+	GaimInputFunction connect_cb;
+	void (*close)(GaimSslConnection *gsc);
+	size_t (*read)(GaimSslConnection *gsc, void *data, size_t len);
+	size_t (*write)(GaimSslConnection *gsc, const void *data, size_t len);
+
+} GaimSslOps;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#define GAIM_SSL_DEFAULT_PORT 443
-
-typedef void *GaimSslConnection;
-typedef void (*GaimSslInputFunction)(gpointer, GaimSslConnection *,
-									 GaimInputCondition);
-
 /**************************************************************************/
 /** @name SSL API                                                         */
 /**************************************************************************/
@@ -99,6 +129,20 @@
 /*@{*/
 
 /**
+ * Sets the current SSL operations structure.
+ *
+ * @param ops The SSL operations structure to assign.
+ */
+void gaim_ssl_set_ops(GaimSslOps *ops);
+
+/**
+ * Returns the current SSL operations structure.
+ *
+ * @return The SSL operations structure.
+ */
+GaimSslOps *gaim_ssl_get_ops(void);
+
+/**
  * Initializes the SSL subsystem.
  */
 void gaim_ssl_init(void);