Index: ChangeLog
===================================================================
--- ChangeLog	(revision 924)
+++ ChangeLog	(working copy)
@@ -1,5 +1,24 @@
 2007-04-16  Dan Winship  <danw@novell.com>
 
+	* libsoup/soup-session.c (soup_session_class_init): add
+	certificate_requested and sign_data signals, for use by
+	evolution-exchange to implement smart-card-based logins. #334021
+	(These signals are not intended for use by anything except
+	Evolution. Once GnuTLS supports PKCS#11, they will go away.)
+	(soup_session_host_new): if we have GnuTLS with set_sign_function,
+	set up the callbacks and attach them to the new signals.
+
+	* libsoup/soup-ssl.h: 
+	* libsoup/soup-gnutls.c: if we have GnuTLS with set_sign_function,
+	add some new methods to set and use it
+
+	* configure.in: Check for
+	gnutls_certificate_client_set_sign_function.
+
+	Based on a patch from Jacob Berkman.
+
+2007-04-16  Dan Winship  <danw@novell.com>
+
 	* libsoup/soup-ssl.h: Make a real SoupSSLCredentials type rather
 	than just using gpointer
 
Index: configure.in
===================================================================
--- configure.in	(revision 924)
+++ configure.in	(working copy)
@@ -140,6 +140,11 @@
 	if test "$have_ssl" = "yes"; then
 		AC_DEFINE(HAVE_SSL, 1, [Defined if you have SSL support])
 		SSL_REQUIREMENT="gnutls"
+
+		old_LDFLAGS="$LDFLAGS"
+		LDFLAGS="$LDFLAGS $LIBGNUTLS_LIBS $LIBGCRYPT_LIBS"
+		AC_CHECK_FUNCS(gnutls_certificate_client_set_sign_function)
+		LDFLAGS="$old_LDFLAGS"
 	else
 		if test "$enable_ssl" = "auto"; then
 			AC_MSG_WARN(Disabling SSL support);
Index: libsoup/soup-ssl.h
===================================================================
--- libsoup/soup-ssl.h	(revision 924)
+++ libsoup/soup-ssl.h	(working copy)
@@ -44,4 +44,24 @@
 	SOUP_SSL_ERROR_CERTIFICATE,
 } SoupSocketError;
 
+#ifdef HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION
+#include <gnutls/gnutls.h>
+
+typedef int (*SoupGnuTLSCertificateRequestFunc) (const gnutls_datum_t *req_ca_rdn,
+						 int nreqs,
+						 gnutls_datum_t **cert_ret,
+						 int *cert_ret_len,
+						 gpointer user_data);
+
+typedef int (*SoupGnuTLSSignDataFunc) (gnutls_datum_t *cert_der,
+				       const gnutls_datum_t *hash_data,
+				       gnutls_datum_t *sign_data,
+				       gpointer user_data);
+
+void soup_gnutls_set_callbacks (SoupSSLCredentials *creds,
+				SoupGnuTLSCertificateRequestFunc cert_func,
+				SoupGnuTLSSignDataFunc sign_func,
+				gpointer user_data);
+#endif /* HAVE_GNUTLS... */
+
 #endif /* SOUP_SSL_H */
Index: libsoup/soup-gnutls.c
===================================================================
--- libsoup/soup-gnutls.c	(revision 924)
+++ libsoup/soup-gnutls.c	(working copy)
@@ -33,6 +33,12 @@
 struct SoupSSLCredentials {
 	gnutls_certificate_credentials creds;
 	gboolean have_ca_file;
+
+#ifdef HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION
+	SoupGnuTLSCertificateRequestFunc cert_func;
+	SoupGnuTLSSignDataFunc sign_func;
+	gpointer auth_data;
+#endif
 };
 
 typedef struct {
@@ -411,6 +417,7 @@
 		gnutls_dh_set_prime_bits (session, DH_BITS);
 
 	gnutls_transport_set_ptr (session, GINT_TO_POINTER (sockfd));
+	gnutls_session_set_ptr (session, creds);
 
 	chan = g_new0 (SoupGNUTLSChannel, 1);
 	chan->fd = sockfd;
@@ -451,6 +458,111 @@
 	soup_gnutls_inited = TRUE;
 }
 
+#ifdef HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION
+static int
+soup_ssl_retrieve_cert (gnutls_session_t session,
+			const gnutls_datum_t *req_ca_rdn, int nreqs,
+			const gnutls_pk_algorithm_t *sign_algos,
+			int sign_algos_length, gnutls_retr_st * st)
+{
+	SoupSSLCredentials *creds;
+	int cert_ret_len = 0;
+	gnutls_datum_t *cert_ret;
+	gnutls_x509_crt_t *gnutls_cert;
+	int i;
+	int ret;
+
+	creds = gnutls_session_get_ptr (session);
+	if (!creds)
+		return -1;
+
+	g_return_val_if_fail (creds->cert_func != NULL, -1);
+
+	if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+		return -1;
+
+	ret = creds->cert_func (req_ca_rdn, nreqs,
+				&cert_ret, &cert_ret_len,
+				creds->auth_data);
+
+	if (ret != 0)
+		return -1;
+
+	if (cert_ret_len == 0)
+		return 0;
+
+	gnutls_cert = gnutls_malloc (sizeof (gnutls_x509_crt_t) * cert_ret_len);
+	for (i = 0; i < cert_ret_len; i++) {
+		if (ret == 0) {
+			gnutls_x509_crt_init (&gnutls_cert[i]);
+			ret = gnutls_x509_crt_import (gnutls_cert[i],
+						      &cert_ret[i],
+						      GNUTLS_X509_FMT_DER);
+			if (ret != 0) {
+				int j;
+				for (j = 0; i < i; j++)
+					gnutls_x509_crt_deinit (gnutls_cert[j]);
+			}
+		}
+		gnutls_free (cert_ret[i].data);
+	}
+	gnutls_free (cert_ret);
+
+	if (ret != 0) {
+		gnutls_free (gnutls_cert);
+		return -1;
+	}
+
+	st->type = GNUTLS_CRT_X509;
+	st->cert.x509 = gnutls_cert;
+	st->ncerts = cert_ret_len;
+	st->key.x509 = NULL;
+	st->deinit_all = 1;
+
+	return 0;
+}
+
+static int
+soup_ssl_sign_data (gnutls_session_t session,
+		    gnutls_datum_t * cert,
+		    gnutls_certificate_type_t cert_type,
+		    const gnutls_datum_t *hash_concat,
+		    gnutls_datum_t * signature)
+{
+	SoupSSLCredentials *creds;
+	int ret;
+
+	creds = gnutls_session_get_ptr (session);
+	if (!creds)
+		return -1;
+
+	g_return_val_if_fail (creds->sign_func != NULL, -1);
+	g_return_val_if_fail (cert_type == GNUTLS_CRT_X509, -1);
+
+	ret = creds->sign_func (cert, hash_concat, signature,
+				creds->auth_data);
+
+	return ret;
+}
+
+void
+soup_gnutls_set_callbacks (SoupSSLCredentials *creds,
+			   SoupGnuTLSCertificateRequestFunc cert_func,
+			   SoupGnuTLSSignDataFunc sign_func,
+			   gpointer user_data)
+{
+	creds->cert_func = cert_func;
+	creds->sign_func = sign_func;
+	creds->auth_data = user_data;
+
+	if (cert_func)
+		gnutls_certificate_client_set_retrieve_function (creds->creds, soup_ssl_retrieve_cert);
+
+	if (sign_func)
+		gnutls_certificate_client_set_sign_function (creds->creds, soup_ssl_sign_data);
+}
+#endif /* HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION */
+
 /**
  * soup_ssl_get_client_credentials:
  * @ca_file: path to a file containing X509-encoded Certificate
Index: libsoup/soup-session.c
===================================================================
--- libsoup/soup-session.c	(revision 924)
+++ libsoup/soup-session.c	(working copy)
@@ -89,6 +89,8 @@
 enum {
 	AUTHENTICATE,
 	REAUTHENTICATE,
+	CERTIFICATE_REQUESTED,
+	SIGN_DATA,
 	LAST_SIGNAL
 };
 
@@ -292,6 +294,35 @@
 			      G_TYPE_POINTER,
 			      G_TYPE_POINTER);
 
+	/* This signal is not part of SoupSession's public API, and
+	 * *will* disappear without warning in the future.
+	 */
+	signals[CERTIFICATE_REQUESTED] =
+		g_signal_new ("certificate-requested",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      soup_marshal_INT__POINTER_INT_POINTER_POINTER,
+			      G_TYPE_INT, 4,
+			      G_TYPE_POINTER,
+			      G_TYPE_INT,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER);
+
+	/* This signal is not part of SoupSession's public API, and
+	 * *will* disappear without warning in the future.
+	 */
+	signals[SIGN_DATA] =
+		g_signal_new ("sign-data",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      soup_marshal_INT__POINTER_POINTER_POINTER,
+			      G_TYPE_INT, 3,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER,
+			      G_TYPE_POINTER);
+
 	/* properties */
 	g_object_class_install_property (
 		object_class, PROP_PROXY_URI,
@@ -550,6 +581,46 @@
 	return strcmp (one->host, two->host) == 0;
 }
 
+#ifdef HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION
+static int
+soup_session_ssl_certificate_requested (const gnutls_datum_t *req_ca_rdn,
+					int nreqs,
+					gnutls_datum_t **cert_ret,
+					int *cert_ret_len,
+					gpointer user_data)
+{
+	SoupSession *session = user_data;
+	int ret = 0;
+
+	g_return_val_if_fail (SOUP_IS_SESSION (user_data), -1);
+
+	g_signal_emit (session, signals[CERTIFICATE_REQUESTED], 0,
+		       req_ca_rdn, nreqs,
+		       cert_ret, cert_ret_len,
+		       &ret);
+
+	return ret;
+}
+
+static int
+soup_session_ssl_sign_data (gnutls_datum_t *cert_der,
+			    const gnutls_datum_t *hash_data,
+			    gnutls_datum_t *sign_data,
+			    gpointer user_data)
+{
+	SoupSession *session = user_data;
+	int ret = 0;
+
+	g_return_val_if_fail (SOUP_IS_SESSION (user_data), -1);
+
+	g_signal_emit (session, signals[SIGN_DATA], 0,
+		       cert_der, hash_data, sign_data,
+		       &ret);
+
+	return ret;
+}
+#endif
+
 static SoupSessionHost *
 soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
 {
@@ -563,6 +634,12 @@
 	    !priv->ssl_creds) {
 		priv->ssl_creds =
 			soup_ssl_get_client_credentials (priv->ssl_ca_file);
+#ifdef HAVE_GNUTLS_CERTIFICATE_CLIENT_SET_SIGN_FUNCTION
+		soup_gnutls_set_callbacks (priv->ssl_creds,
+					   soup_session_ssl_certificate_requested,
+					   soup_session_ssl_sign_data,
+					   session);
+#endif
 	}
 
 	return host;
Index: libsoup/soup-marshal.list
===================================================================
--- libsoup/soup-marshal.list	(revision 924)
+++ libsoup/soup-marshal.list	(working copy)
@@ -2,3 +2,5 @@
 NONE:INT
 NONE:OBJECT
 NONE:OBJECT,STRING,STRING,POINTER,POINTER
+INT:POINTER,INT,POINTER,POINTER
+INT:POINTER,POINTER,POINTER
