cpw.ljfisher.ssl_client_auth: 4c8b52af: Add support for sending the complete cer...

lucas.fisher at gmail.com lucas.fisher at gmail.com
Sun Nov 20 00:40:42 EST 2011


----------------------------------------------------------------------
Revision: 4c8b52af3da1f98d92cfb7a0605058efda168d6d
Parent:   d6750cfca09a1aa5107186c7b08141aec5ad0c7a
Author:   lucas.fisher at gmail.com
Date:     11/20/11 00:36:06
Branch:   im.pidgin.cpw.ljfisher.ssl_client_auth
URL: http://d.pidgin.im/viewmtn/revision/info/4c8b52af3da1f98d92cfb7a0605058efda168d6d

Changelog: 

Add support for sending the complete certificate chain for ssl client auth.
Update pkcs12 to support importing multiple certifiates from a pkcs12 file.
Store user's certificate chain in user pool but hide non-end user certificates.
Add util function to certifictes.c to build certificte chain.
Update privatekey pool store and retrieve request to take a friendly name for the key.
Update gnutls plugin to support new pkcs12 features.
Update gnutls plugin to send certificate chain for ssl client auth.
Change jabber account options to always display the certificate's common name.
Update gtkcertmgr to use the cert unique id (subjects dn) as the pool id.
Update gtkcertmgr with separate columns for certificate common name and the pool id (subject dn).
Remove ability for the user to name certificates themselves and always use the dn.

Changes against parent d6750cfca09a1aa5107186c7b08141aec5ad0c7a

  patched  libpurple/certificate.c
  patched  libpurple/certificate.h
  patched  libpurple/pkcs12.c
  patched  libpurple/pkcs12.h
  patched  libpurple/plugins/ssl/ssl-gnutls.c
  patched  libpurple/privatekey.c
  patched  libpurple/privatekey.h
  patched  libpurple/protocols/jabber/libxmpp.c
  patched  libpurple/sslconn.c
  patched  pidgin/gtkcertmgr.c

-------------- next part --------------
============================================================
--- libpurple/sslconn.c	f4e0fc61c9afa41d7935be83621c49c87a92df79
+++ libpurple/sslconn.c	1e371586cee17cda73259f5c18c78bdee683315f
@@ -86,6 +86,7 @@ purple_ssl_destroy(PurpleSslConnection *
 static void
 purple_ssl_destroy(PurpleSslConnection *gsc)
 {
+	purple_debug_info("sslconn", "Destroying PurpleSslConnection %p\n", gsc);
 	purple_certificate_destroy(gsc->certificate);
 	purple_privatekey_destroy(gsc->key);
 	g_free(gsc->host);
@@ -197,6 +198,7 @@ purple_ssl_get_credentials(PurpleAccount
 	PurpleCertificatePool *crt_pool = NULL;
 	PurplePrivateKeyPool *key_pool = NULL;
 	ssl_connect_cb_data *data = NULL;
+	gchar *name = NULL;
 
 	g_return_val_if_fail(gsc, FALSE);
 	g_return_val_if_fail(gsc->certificate_id, FALSE);
@@ -231,11 +233,14 @@ purple_ssl_get_credentials(PurpleAccount
 	data->gsc = gsc;
 	data->account = account;
 
-	purple_privatekey_pool_retrieve_request(key_pool, gsc->certificate_id,
+	name = purple_certificate_get_subject_name(gsc->certificate);
+	purple_privatekey_pool_retrieve_request(key_pool, name, gsc->certificate_id,
 		ok_cb,
 		G_CALLBACK(purple_ssl_connect_cancel_cb),
 		(void*)data);
 
+	g_free(name);
+
 	return TRUE;
 }
 
@@ -279,6 +284,7 @@ purple_ssl_connect_with_ssl_cn_auth(Purp
 	}
 
 	gsc = g_new0(PurpleSslConnection, 1);
+	purple_debug_info("sslconn", "Creating new PurpleSslConnection %p\n", gsc);
 
 	gsc->fd              = -1;
 	gsc->host            = ssl_cn ? g_strdup(ssl_cn) : g_strdup(host);
@@ -415,6 +421,7 @@ purple_ssl_connect_with_host_fd_auth(Pur
 	}
 
 	gsc = g_new0(PurpleSslConnection, 1);
+	purple_debug_info("sslconn", "Creating new PurpleSslConnection %p\n", gsc);
 
 	gsc->connect_cb_data = data;
 	gsc->connect_cb      = func;
============================================================
--- libpurple/plugins/ssl/ssl-gnutls.c	973a62b7d211caecd8d885ebcd42c5dc3ce7f847
+++ libpurple/plugins/ssl/ssl-gnutls.c	1158d7567b13746d298ad56f7f70e4ea500112ac
@@ -82,6 +82,31 @@ gnutls_get_default_crypt_flags()
 #endif
 }
 
+/* Taken from gchecksum.c */
+static gchar hex_digits[] = "0123456789abcdef";
+
+static gchar *
+hex_encode(guint8 *buf, gsize buf_len)
+{
+  gint len = buf_len * 2;
+  gint i;
+  gchar *retval;
+
+  retval = g_new (gchar, len + 1);
+
+  for (i = 0; i < len; i++)
+    {
+      guint8 byte = buf[i];
+
+      retval[2 * i] = hex_digits[byte >> 4];
+      retval[2 * i + 1] = hex_digits[byte & 0xf];
+    }
+
+  retval[len] = 0;
+
+  return retval;
+}
+
 static void
 ssl_gnutls_log(int level, const char *str)
 {
@@ -250,7 +275,7 @@ ssl_gnutls_init_gnutls(void)
 	gnutls_certificate_set_x509_trust_file(xcred, "ca.pem",
 		GNUTLS_X509_FMT_PEM);
 	
-	gnutls_certificate_client_set_retrieve_function(xcred, ssl_gnutls_certificate_retrieve_function);
+/*	gnutls_certificate_client_set_retrieve_function(xcred, ssl_gnutls_certificate_retrieve_function);*/
 }
 
 static gboolean
@@ -712,7 +737,7 @@ x509_crtdata_delref(x509_crtdata_t *cd)
 }
 
 /** Helper macro to retrieve the GnuTLS crt_t from a PurpleCertificate */
-#define X509_GET_GNUTLS_DATA(pcrt) ( ((x509_crtdata_t *) (pcrt->data))->crt)
+#define X509_GET_GNUTLS_DATA(pcrt) ( ((x509_crtdata_t *) ((pcrt)->data))->crt)
 
 /** Transforms a gnutls_datum containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme.
  *
@@ -1493,6 +1518,7 @@ x509_get_unique_key_id(PurplePrivateKey 
 	int ret;
 	guchar * out_buf = NULL; /* Data to output */
 	size_t out_size = 0; /* Output size */
+	gchar* id;
 
 	g_return_val_if_fail(key, FALSE);
 	g_return_val_if_fail(key->scheme == &x509_key_gnutls, FALSE);
@@ -1516,7 +1542,9 @@ x509_get_unique_key_id(PurplePrivateKey 
 		return NULL;
 	}
 
-	return (gchar*)out_buf;
+	id = hex_encode(out_buf, out_size);
+	g_free(out_buf);
+	return id;
 }
 
 static PurplePrivateKeyScheme x509_key_gnutls = {
@@ -1550,307 +1578,320 @@ static PurplePrivateKeyScheme x509_key_g
  * SSL crypto backend supply its own keystore???
  */
 
-#define gnutls_assert() purple_debug_info("gnutls/x509", "parse_pkcs12")
-
 static int
-parse_pkcs12 (gnutls_certificate_credentials_t res,
-	      gnutls_pkcs12_t p12,
-	      const char *password,
-	      gnutls_x509_privkey_t * key,
-	      gnutls_x509_crt_t * cert, gnutls_x509_crl_t * crl)
+parse_pkcs12(gnutls_pkcs12_t p12,
+	     const char *password,
+	     GList **keys, /* gnutls_x509_privkey_t */
+	     GList **crts, /* gnutls_x509_crt_t */
+	     GList **crls) /* gnutlx_x509_crl_t */
 {
-  gnutls_pkcs12_bag_t bag = NULL;
-  int idx = 0;
-  int ret;
-  size_t cert_id_size = 0;
-  size_t key_id_size = 0;
-  unsigned char cert_id[20];
-  unsigned char key_id[20];
-  int privkey_ok = 0;
+	gnutls_pkcs12_bag_t bag = NULL;
+	int idx = 0;
+	int ret;
+/*
+	size_t cert_id_size = 0;
+	size_t key_id_size = 0;
+	unsigned char cert_id[20];
+	unsigned char key_id[20];
+*/
+	int privkey_ok = 0;
 
-  *cert = NULL;
-  *key = NULL;
-  *crl = NULL;
+	gnutls_x509_crt_t cert = NULL;
+	gnutls_x509_privkey_t key = NULL;
+	gnutls_x509_crl_t crl = NULL;
 
-  /* find the first private key */
-  for (;;)
-    {
-      int elements_in_bag;
-      int i;
+	g_return_val_if_fail(keys, -1);
 
-      ret = gnutls_pkcs12_bag_init (&bag);
-      if (ret < 0)
-	{
-	  bag = NULL;
-	  gnutls_assert ();
-	  goto done;
-	}
+	/* find the first private key */
+	for (;;) {
+		int elements_in_bag;
+		int i;
 
-      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
-      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
-	break;
-      if (ret < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
-	}
+		ret = gnutls_pkcs12_bag_init(&bag);
+		if (ret < 0) {
+			bag = NULL;
+			purple_debug_error("gnutls/pkcs12",
+				"Error initing pkcs12 bag\n");
+			goto done;
+		}
 
-      ret = gnutls_pkcs12_bag_get_type (bag, 0);
-      if (ret < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
-	}
-
-      if (ret == GNUTLS_BAG_ENCRYPTED)
-	{
-	  ret = gnutls_pkcs12_bag_decrypt (bag, password);
-	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
-	}
-
-      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
-      if (elements_in_bag < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
-	}
-
-      for (i = 0; i < elements_in_bag; i++)
-	{
-	  int type;
-	  gnutls_datum_t data;
-
-	  type = gnutls_pkcs12_bag_get_type (bag, i);
-	  if (type < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
-
-	  ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
-	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
-
-	  switch (type)
-	    {
-	    case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
-	    case GNUTLS_BAG_PKCS8_KEY:
-	      if (*key != NULL)	/* too simple to continue */
-		{
-		  gnutls_assert ();
-		  break;
+		ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+			break;
+		if (ret < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Error getting bag %d from pkcs12\n", idx);
+			goto done;
 		}
 
-	      ret = gnutls_x509_privkey_init (key);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  goto done;
+		ret = gnutls_pkcs12_bag_get_type(bag, 0);
+		if (ret < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Error getting type for bag %d\n", idx);
+			goto done;
 		}
 
-	      ret = gnutls_x509_privkey_import_pkcs8
-		(*key, &data, GNUTLS_X509_FMT_DER, password,
-		 type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  gnutls_x509_privkey_deinit (*key);
-		  goto done;
+		if (ret == GNUTLS_BAG_ENCRYPTED) {
+			ret = gnutls_pkcs12_bag_decrypt(bag, password);
+			if (ret < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Error decrypting bag %d\n", idx);
+				goto done;
+			}
 		}
 
-	      key_id_size = sizeof (key_id);
-	      ret =
-		gnutls_x509_privkey_get_key_id (*key, 0, key_id,
-						&key_id_size);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  gnutls_x509_privkey_deinit (*key);
-		  goto done;
+		elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+		if (elements_in_bag < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Error getting count for bag %d\n", idx);
+			goto done;
 		}
 
-	      privkey_ok = 1;	/* break */
-	      break;
-	    default:
-	      break;
-	    }
-	}
+		for (i = 0; i < elements_in_bag; i++) {
+			int type;
+			gnutls_datum_t data;
 
-      idx++;
-      gnutls_pkcs12_bag_deinit (bag);
+			type = gnutls_pkcs12_bag_get_type(bag, i);
+			if (type < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Error getting type for item %d in bag %d\n", i, idx);
+				goto done;
+			}
 
-      if (privkey_ok != 0)	/* private key was found */
-	break;
-    }
+			ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+			if (ret < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Error getting item %d from bag %d\n", i, idx);
+				goto done;
+			}
 
-  if (privkey_ok == 0)		/* no private key */
-    {
-      gnutls_assert ();
-      return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
-    }
+			switch (type) {
+			case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+			case GNUTLS_BAG_PKCS8_KEY:
+				if (privkey_ok == 1) { /* too simple to continue */
+					purple_debug_error("gnutls/pkcs12",
+						"Already found a key.\n");
+					break;
+				}
 
-  /* now find the corresponding certificate 
-   */
-  idx = 0;
-  bag = NULL;
-  for (;;)
-    {
-      int elements_in_bag;
-      int i;
+				ret = gnutls_x509_privkey_init(&key);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed to init x509_privkey.\n");
+					goto done;
+				}
 
-      ret = gnutls_pkcs12_bag_init (&bag);
-      if (ret < 0)
-	{
-	  bag = NULL;
-	  gnutls_assert ();
-	  goto done;
-	}
+				ret = gnutls_x509_privkey_import_pkcs8(
+					key, &data, GNUTLS_X509_FMT_DER, password,
+					type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed to import pkcs8 key from item %d in bag %d\n", i, idx);
+					gnutls_x509_privkey_deinit(key);
+					goto done;
+				}
 
-      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
-      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
-	break;
-      if (ret < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
-	}
+				purple_debug_info("gnutls/pkcs12",
+					"Found key in item %d of bag %d\n", i, idx);
+				*keys = g_list_append(*keys, key);
+#if 0
+				key_id_size = sizeof (key_id);
+				ret = gnutls_x509_privkey_get_key_id(
+					*key, 0, key_id, &key_id_size);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed to get key id from bag %d\n", idx);
+					gnutls_x509_privkey_deinit (*key);
+					goto done;
+				}
+#endif
+				privkey_ok = 1;	/* break */
+				break;
+			default:
+				break;
+			}
+		}
 
-      ret = gnutls_pkcs12_bag_get_type (bag, 0);
-      if (ret < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
-	}
+		idx++;
+		gnutls_pkcs12_bag_deinit(bag);
 
-      if (ret == GNUTLS_BAG_ENCRYPTED)
-	{
-	  ret = gnutls_pkcs12_bag_decrypt (bag, password);
-	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
+		if (privkey_ok != 0)	/* private key was found */
+			break;
 	}
 
-      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
-      if (elements_in_bag < 0)
-	{
-	  gnutls_assert ();
-	  goto done;
+	if (privkey_ok == 0) {/* no private key */
+		purple_debug_error("gnutls/pkcs12",
+			"No private key found in pkcs12 file\n");
+		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
 	}
 
-      for (i = 0; i < elements_in_bag; i++)
-	{
-	  int type;
-	  gnutls_datum_t data;
+	/* now find the corresponding certificate 
+	*/
+	idx = 0;
+	bag = NULL;
+	for (;;) {
+		int elements_in_bag;
+		int i;
 
-	  type = gnutls_pkcs12_bag_get_type (bag, i);
-	  if (type < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
+		ret = gnutls_pkcs12_bag_init(&bag);
+		if (ret < 0) {
+			bag = NULL;
+			purple_debug_error("gnutls/pkcs12",
+				"pkcs12 bag init failed\n");
+			goto done;
+		}
 
-	  ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
-	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      goto done;
-	    }
+		ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+		if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+			break;
+		if (ret < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Failed to to get bag %d\n", idx);
+			goto done;
+		}
 
-	  switch (type)
-	    {
-	    case GNUTLS_BAG_CERTIFICATE:
-	      if (*cert != NULL)	/* no need to set it again */
-		{
-		  gnutls_assert ();
-		  break;
+		ret = gnutls_pkcs12_bag_get_type(bag, 0);
+		if (ret < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Failed getting type for bag %d\n", idx);
+			goto done;
 		}
 
-	      ret = gnutls_x509_crt_init (cert);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  goto done;
+		if (ret == GNUTLS_BAG_ENCRYPTED) {
+			ret = gnutls_pkcs12_bag_decrypt(bag, password);
+			if (ret < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Failed to decrypt bag %d\n", idx);
+				goto done;
+			}
 		}
 
-	      ret =
-		gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  gnutls_x509_crt_deinit (*cert);
-		  goto done;
+		elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+		if (elements_in_bag < 0) {
+			purple_debug_error("gnutls/pkcs12",
+				"Failed getting count for bag %d\n", idx);
+			goto done;
 		}
 
-	      /* check if the key id match */
-	      cert_id_size = sizeof (cert_id);
-	      ret =
-		gnutls_x509_crt_get_key_id (*cert, 0, cert_id, &cert_id_size);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  gnutls_x509_crt_deinit (*cert);
-		  goto done;
-		}
+		for (i = 0; i < elements_in_bag; i++) {
+			int type;
+			gnutls_datum_t data;
 
-	      if (memcmp (cert_id, key_id, cert_id_size) != 0)
-		{		/* they don't match - skip the certificate */
-		  gnutls_x509_crt_deinit (*cert);
-		  *cert = NULL;
-		}
-	      break;
+			type = gnutls_pkcs12_bag_get_type(bag, i);
+			if (type < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Failed getting type for item %d in bag %d\n", i, idx);
+				goto done;
+			}
 
-	    case GNUTLS_BAG_CRL:
-	      if (*crl != NULL)
-		{
-		  gnutls_assert ();
-		  break;
-		}
+			ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+			if (ret < 0) {
+				purple_debug_error("gnutls/pkcs12",
+					"Failed getting item %d from bag %d\n", i, idx);
+				goto done;
+			}
 
-	      ret = gnutls_x509_crl_init (crl);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  goto done;
-		}
+			switch (type) {
+			case GNUTLS_BAG_CERTIFICATE:
+#if 0
+				if (*cert != NULL) {	/* no need to set it again */
+					purple_debug_error("gnutls/pkcs12","");
+					break;
+				}
+#endif
+				/* Ignore certs if we didn't provide a list */
+				if (crts == NULL)
+					break;
 
-	      ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
-	      if (ret < 0)
-		{
-		  gnutls_assert ();
-		  gnutls_x509_crl_deinit (*crl);
-		  goto done;
-		}
-	      break;
+				ret = gnutls_x509_crt_init(&cert);
+	 			if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed init x509_crt\n");
+					goto done;
+				}
 
-	    case GNUTLS_BAG_ENCRYPTED:
-	      /* XXX Bother to recurse one level down?  Unlikely to
-	         use the same password anyway. */
-	    case GNUTLS_BAG_EMPTY:
-	    default:
-	      break;
-	    }
-	}
+				ret = gnutls_x509_crt_import(
+					cert, &data, GNUTLS_X509_FMT_DER);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed importing cert from item %d in bag %d\n", i, idx);
+					gnutls_x509_crt_deinit (cert);
+					goto done;
+				}
 
-      idx++;
-      gnutls_pkcs12_bag_deinit (bag);
-    }
+				purple_debug_info("gnutls/pkcs12",
+					"Found cert in item %d of bag %d\n", i, idx);
+				*crts = g_list_append(*crts, cert);
+#if 0
+				/* check if the key id match */
+				cert_id_size = sizeof (cert_id);
+				ret = gnutls_x509_crt_get_key_id(
+					*cert, 0, cert_id, &cert_id_size);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12","");();
+					gnutls_x509_crt_deinit (*cert);
+					goto done;
+				}
 
-  ret = 0;
+				if (memcmp (cert_id, key_id, cert_id_size) != 0) {
+					/* they don't match - skip the certificate */
+					gnutls_x509_crt_deinit (*cert);
+					*cert = NULL;
+				}
+#endif
+				break;
 
+			case GNUTLS_BAG_CRL:
+#if 0
+				if (crl != NULL) {
+					purple_debug_error("gnutls/pkcs12","");();
+					break;
+				}
+#endif
+				/* Ignore crls if we didn't provide a list */
+				if (crls == NULL)
+					break;
+
+				ret = gnutls_x509_crl_init(&crl);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed init x509_crl\n");
+					goto done;
+				}
+
+				ret = gnutls_x509_crl_import(crl, &data, GNUTLS_X509_FMT_DER);
+				if (ret < 0) {
+					purple_debug_error("gnutls/pkcs12",
+						"Failed importing crl from item %d in bag %d\n", i, idx);
+					gnutls_x509_crl_deinit (crl);
+					goto done;
+				}
+
+				purple_debug_info("gnutls/pkcs12",
+					"Found crl in item %d of bag %d\n", i, idx);
+				*crls = g_list_append(*crls, crl);
+				break;
+
+			case GNUTLS_BAG_ENCRYPTED:
+				/* XXX Bother to recurse one level down?  Unlikely to
+				   use the same password anyway. */
+		 	case GNUTLS_BAG_EMPTY:
+			default:
+				break;
+		 	}
+		}
+
+		idx++;
+		gnutls_pkcs12_bag_deinit (bag);
+	 }
+
+	ret = 0;
+
 done:
-  if (bag)
-    gnutls_pkcs12_bag_deinit (bag);
+	if (bag)
+		gnutls_pkcs12_bag_deinit (bag);
 
-  return ret;
+	return ret;
 }
 
 static gboolean
@@ -1886,6 +1927,46 @@ read_pkcs12_file(const gchar* filename, 
 }
 
 
+static void
+add_to_purple_crt_list(gpointer data, gpointer user_data)
+{
+	gnutls_x509_crt_t crt = (gnutls_x509_crt_t)data;
+	GList **pcrts = (GList**)user_data;
+	x509_crtdata_t *crtdat;
+	PurpleCertificate *pcrt;
+
+	g_return_if_fail(NULL != data);
+
+	crtdat = g_new0(x509_crtdata_t, 1);
+	crtdat->crt = crt;
+	crtdat->refcount = 0;
+
+	pcrt = g_new0(PurpleCertificate, 1);
+	pcrt->scheme = &x509_gnutls;
+	pcrt->data = x509_crtdata_addref(crtdat);
+
+	*pcrts = g_list_append(*pcrts, pcrt);
+}
+
+static PurplePrivateKey*
+create_purple_privatekey_from_privkey(gnutls_x509_privkey_t key)
+{
+	x509_keydata_t *keydat;
+	PurplePrivateKey *pkey;
+
+	g_return_val_if_fail(NULL != key, NULL);
+
+	keydat = g_new0(x509_keydata_t, 1);
+	keydat->key = key;
+	keydat->refcount = 0;
+
+	pkey = g_new0(PurplePrivateKey, 1);
+	pkey->scheme = &x509_key_gnutls;
+	pkey->data = x509_keydata_addref(keydat);
+
+	return pkey;
+}
+
 /**
  * Derived from gnutls_certificate_set_x509_simple_pkcs12_mem in
  * gnutls_x509.c. Modified to return PurpleCertificate and PurplePrivateKey
@@ -1894,16 +1975,15 @@ x509_import_pkcs12_from_file(const gchar
 static gboolean
 x509_import_pkcs12_from_file(const gchar* filename,
 			     const gchar* password,
-			     PurpleCertificate **crt,
-			     PurplePrivateKey **key)
+			     GList **pcrts, /* PurpleCertificate */
+			     PurplePrivateKey **pkey)
 {
 	gnutls_pkcs12_t p12;
-	gnutls_certificate_credentials_t res = NULL;
-	gnutls_x509_crl_t crl = NULL;
 	gnutls_datum_t dt;
 	gnutls_x509_crt_fmt_t fmt;
-	x509_crtdata_t *crtdat;
-	x509_keydata_t *keydat;
+	GList *crts = NULL;
+	GList *keys = NULL;
+	gnutls_x509_privkey_t key;
 
 	int rv;
 
@@ -1944,51 +2024,31 @@ x509_import_pkcs12_from_file(const gchar
 		}
  	}
 		
-	/* Allocate and prepare the internal key and crt data */
-	crtdat = g_new0(x509_crtdata_t, 1);
-	crtdat->crt = NULL;
-	crtdat->refcount = 0;
+	rv = parse_pkcs12(p12, password, &keys, &crts, NULL);
 
-	keydat = g_new0(x509_keydata_t, 1);
-	keydat->key = NULL;
-	keydat->refcount = 0;
-
-	rv = parse_pkcs12 (res, p12, password, &(keydat->key), &(crtdat->crt), &crl);
 	if (GNUTLS_E_SUCCESS != rv) {
 		purple_debug_error("gnutls/x509",
 			"parse_pkcs12 error: %s\n", gnutls_strerror(rv));
-		gnutls_x509_crt_deinit (crtdat->crt);
-		gnutls_x509_privkey_deinit(keydat->key);
-		gnutls_x509_crl_deinit (crl);
-		g_free(crtdat);
-		g_free(keydat);
 		return FALSE;
 	}
 
-	/* Just deinit since we aren't using and 
-	   want to avoid modifying parse_pkcs12() */
-	gnutls_x509_crl_deinit (crl);
+	purple_debug_info("gnutls/x509",
+		"Found %d keys and %d certs in pkcs12\n",
+		g_list_length(keys), g_list_length(crts));
 
-	if (NULL == keydat->key || NULL == crtdat->crt) {
+	if (g_list_length(keys) != 1) {
 		purple_debug_error("gnutls/x509",
-			"%s get a cert. %s get a key",
-			crtdat->crt ? "Did" : "Did not",
-			keydat->key ? "Did" : "Did not");
-		gnutls_x509_crt_deinit (crtdat->crt);
-		gnutls_x509_privkey_deinit(keydat->key);
-		g_free(crtdat);
-		g_free(keydat);
+			"Only support one private key in pkcs12 file. Found %d\n",
+			g_list_length(keys));
+		g_list_free_full(keys, (GDestroyNotify)gnutls_x509_privkey_deinit);
+		g_list_free_full(crts, (GDestroyNotify)gnutls_x509_crt_deinit);
 		return FALSE;
 	}
 
-	*crt = g_new0(PurpleCertificate, 1);
-	(*crt)->scheme = &x509_gnutls;
-	(*crt)->data = x509_crtdata_addref(crtdat);
+	key = (gnutls_x509_privkey_t)(g_list_first(keys)->data);
+	*pkey = create_purple_privatekey_from_privkey(key);
+	g_list_foreach(crts, add_to_purple_crt_list, pcrts);
 
-	*key = g_new0(PurplePrivateKey, 1);
-	(*key)->scheme = &x509_key_gnutls;
-	(*key)->data = x509_keydata_addref(keydat);
-
 	/* check if the key and certificate found match */
 #if 0 /* TODO ljf */
 	if (key && (ret = _gnutls_check_key_cert_match (res)) < 0) {
@@ -2004,53 +2064,56 @@ static gboolean
  */
 /* Derived from generate_pkcs12() in certtool.c in the gnutls source. */
 static gboolean
-x509_export_pkcs12_to_filename(const gchar* filename, const gchar* password, PurpleCertificate *purple_crt, PurplePrivateKey *purple_key)
+x509_export_pkcs12_to_filename(const gchar* filename, const gchar* password,
+			       GList *pcrts, PurplePrivateKey *pkey)
 {
 	gnutls_pkcs12_t pkcs12 = NULL;
-	gnutls_x509_crt_t crts[1];
+	gnutls_x509_crt_t crt = NULL;
 	gnutls_x509_privkey_t key = NULL;
 	int result;
 	size_t size;
 	gnutls_datum_t data;
 	const char *name;
-	unsigned int flags, i;
+	unsigned int flags;
 	gnutls_datum_t key_id;
 	unsigned char _key_id[20];
 	int indx;
-	size_t ncrts;
 	gboolean success = FALSE;
 	gnutls_pkcs12_bag_t kbag = NULL;
 	gnutls_pkcs12_bag_t bag = NULL;
 	char *key_buf = NULL;
 	char *out_buf = NULL;
+	GList *item = NULL;
 
-	crts[0] = X509_GET_GNUTLS_DATA(purple_crt);
-	key = X509_GET_GNUTLS_KEYDATA(purple_key);
-	ncrts = 1;
-
-	name = x509_common_name(purple_crt);
-	if (NULL == name) {
-		purple_debug_error("gnutls/pkcs12", "export: can't get common name for cert\n");
+	result = gnutls_pkcs12_init (&pkcs12);
+	if (result < 0) {
+		purple_debug_error("gnutls/pkcs12",
+			"export: pkcs12_init: %s\n", gnutls_strerror (result));
 		goto done;
 	}
 
-	result = gnutls_pkcs12_init (&pkcs12);
+	result = gnutls_pkcs12_bag_init (&bag);
 	if (result < 0) {
-		purple_debug_error("gnutls/pkcs12", "export: pkcs12_init: %s\n", gnutls_strerror (result));
+		purple_debug_error("gnutls/pkcs12",
+			"export: bag_init: %s\n", gnutls_strerror (result));
 		goto done;
 	}
 
-	for (i = 0; i < ncrts; i++)
-	{
-		result = gnutls_pkcs12_bag_init (&bag);
-		if (result < 0) {
-			purple_debug_error("gnutls/pkcs12", "export: bag_init: %s\n", gnutls_strerror (result));
+	for (item = g_list_first(pcrts); NULL != item; item = g_list_next(item)) {
+		PurpleCertificate *pcrt = (PurpleCertificate*)item->data;
+
+		crt = X509_GET_GNUTLS_DATA(pcrt);
+
+		name = x509_common_name(pcrt);
+		if (NULL == name) {
+			purple_debug_error("gnutls/pkcs12",
+				"export: can't get common name for cert\n");
 			goto done;
 		}
 
-		result = gnutls_pkcs12_bag_set_crt (bag, crts[i]);
+		result = gnutls_pkcs12_bag_set_crt (bag, crt);
 		if (result < 0) {
-			purple_debug_error ("gnutls/pkcs12", "export: set_crt[%d]: %s\n", i,
+			purple_debug_error ("gnutls/pkcs12", "export: set_crt: %s\n",
 				 gnutls_strerror (result));
 			goto done;
 		}
@@ -2065,9 +2128,9 @@ x509_export_pkcs12_to_filename(const gch
 		}
 
 		size = sizeof (_key_id);
-		result = gnutls_x509_crt_get_key_id (crts[i], 0, _key_id, &size);
+		result = gnutls_x509_crt_get_key_id (crt, 0, _key_id, &size);
 		if (result < 0) {
-			purple_debug_error("gnutls/pkcs12", "key_id[%d]: %s\n", i,
+			purple_debug_error("gnutls/pkcs12", "key_id: %s\n", 
 				 gnutls_strerror(result));
 			goto done;
 		}
@@ -2081,32 +2144,36 @@ x509_export_pkcs12_to_filename(const gch
 				 gnutls_strerror(result));
 			goto done;
 		}
+	}
 
-		flags = gnutls_get_default_crypt_flags();
+#if 0
+	flags = gnutls_get_default_crypt_flags();
+	/* Should we be encrypting the certs?? Don't see why we should. */
+	result = gnutls_pkcs12_bag_encrypt (bag, password, flags);
+	if (result < 0) {
+		purple_debug_error("gnutls/pkcs12", "bag_encrypt: %s\n", gnutls_strerror (result));
+		goto done;
+	}
+#endif
+	result = gnutls_pkcs12_set_bag (pkcs12, bag);
+	if (result < 0) {
+		purple_debug_error("gnutls/pkcs12", "set_bag: %s\n", gnutls_strerror (result));
+		goto done;
+	}
 
-		result = gnutls_pkcs12_bag_encrypt (bag, password, flags);
-		if (result < 0) {
-			purple_debug_error("gnutls/pkcs12", "bag_encrypt: %s\n", gnutls_strerror (result));
-			goto done;
-		}
+	gnutls_pkcs12_bag_deinit(bag);
+	bag = NULL;
 
-		result = gnutls_pkcs12_set_bag (pkcs12, bag);
-		if (result < 0) {
-			purple_debug_error("gnutls/pkcs12", "set_bag: %s\n", gnutls_strerror (result));
-			goto done;
-		}
+	/* XXX Assume one key */
 
-		gnutls_pkcs12_bag_deinit(bag);
-		bag = NULL;
-	}
-
-
 	result = gnutls_pkcs12_bag_init (&kbag);
 	if (result < 0) {
 		purple_debug_error("gnutls/pkcs12", "bag_init: %s\n", gnutls_strerror (result));
 		goto done;
 	}
 
+	key = X509_GET_GNUTLS_KEYDATA(pkey);
+
 	flags = gnutls_get_default_crypt_flags();
 	size = 0;
 	result = gnutls_x509_privkey_export_pkcs8 (key, GNUTLS_X509_FMT_DER,
@@ -2119,6 +2186,7 @@ x509_export_pkcs12_to_filename(const gch
 
 	purple_debug_info("gnutls/pkcs12", "Got pkcs8 export memory size = %zd\n", size);
 
+	size = 2 * size;
 	key_buf = g_new0(char, size);
 
 	result = gnutls_x509_privkey_export_pkcs8 (key, GNUTLS_X509_FMT_DER,
@@ -2131,9 +2199,8 @@ x509_export_pkcs12_to_filename(const gch
 
 	data.data = (unsigned char*)key_buf;
 	data.size = size;
-	result =
-		gnutls_pkcs12_bag_set_data (kbag,
-				GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data);
+	result = gnutls_pkcs12_bag_set_data (kbag,
+			GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data);
 	if (result < 0) {
 		purple_debug_error("gnutls/pkcs12", "bag_set_data: %s\n", gnutls_strerror (result));
 		goto done;
@@ -2141,6 +2208,13 @@ x509_export_pkcs12_to_filename(const gch
 
 	indx = result;
 
+	name = x509_common_name((PurpleCertificate*)(g_list_first(pcrts)->data));
+	if (NULL == name) {
+		purple_debug_error("gnutls/pkcs12",
+			"export: can't get common name for key's cert\n");
+		goto done;
+	}
+
 	result = gnutls_pkcs12_bag_set_friendly_name (kbag, indx, name);
 	if (result < 0) {
 		purple_debug_error("gnutls/pkcs12", "bag_set_friendly_name: %s\n",
@@ -2208,30 +2282,36 @@ pkcs12_import(const gchar *filename, con
 
 static gboolean 
 pkcs12_import(const gchar *filename, const gchar *password,
-	      PurpleCertificate **crt, PurplePrivateKey **key)
+	      GList **crts, PurplePrivateKey **key)
 {
 	g_return_val_if_fail(filename, FALSE);
 	g_return_val_if_fail(password, FALSE);
-	g_return_val_if_fail(crt, FALSE);
+	g_return_val_if_fail(crts, FALSE);
 	g_return_val_if_fail(key, FALSE);
 
 
-	return x509_import_pkcs12_from_file(filename, password, crt, key);
+	return x509_import_pkcs12_from_file(filename, password, crts, key);
 }
 
 static gboolean 
 pkcs12_export(const gchar *filename, const gchar *password,
-	      PurpleCertificate *crt, PurplePrivateKey *key)
+	      GList *crts, PurplePrivateKey *key)
 {
+	GList *i = NULL;
+
 	g_return_val_if_fail(filename, FALSE);
 	g_return_val_if_fail(password, FALSE);
-	g_return_val_if_fail(crt, FALSE);
-	g_return_val_if_fail(key, FALSE);
 
+	g_return_val_if_fail(NULL != crts, FALSE);
+	g_return_val_if_fail(NULL != key, FALSE);
 	g_return_val_if_fail(key->scheme == &x509_key_gnutls, FALSE);
-	g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
 
-	return x509_export_pkcs12_to_filename(filename, password, crt, key);
+	for (i = g_list_first(crts); NULL != i; i = g_list_next(i)) {
+		PurpleCertificate *crt = (PurpleCertificate*)i->data;
+		g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
+	}
+
+	return x509_export_pkcs12_to_filename(filename, password, crts, key);
 }
 
 
@@ -2250,34 +2330,58 @@ static PurplePkcs12Scheme pkcs12_gnutls 
  * Setting the Purple Certificate and Private Key for authentication  *
  **********************************************************************/
 
+/**
+ * This attempts to add all certs in pcrt's cert chain as the certs
+ * are available. It will look in all our cert pools for issuers and
+ * add them to the creds.
+ */
 static gboolean
 ssl_gnutls_set_client_auth(gnutls_certificate_client_credentials cred,
 		PurpleCertificate * pcrt, PurplePrivateKey * pkey)
 {
-	gnutls_x509_crt_t cert_list[1];
+	gnutls_x509_crt_t *cert_list;
 	int rv;
+	PurpleCertificatePool *user_pool = NULL;
+	GList *crts = NULL;
+	int numcrts = 0;
+	GList *item = NULL;
+	int idx = 0;
 
 	g_return_val_if_fail(pcrt, FALSE);
 	g_return_val_if_fail(pkey, FALSE);
 	g_return_val_if_fail(pcrt->scheme == &x509_gnutls, FALSE);
 	g_return_val_if_fail(pkey->scheme == &x509_key_gnutls, FALSE);
 
+	user_pool = purple_certificate_find_pool("x509", "user");
+	g_return_val_if_fail(user_pool, FALSE);
+
 	if (NULL != xcred) {
+#if 0
 		/* Set global state for creds to return when server 
 		 * requests a client certificate */
 		client_auth_certs[0] = X509_GET_GNUTLS_DATA(pcrt);
 		client_auth_key = X509_GET_GNUTLS_KEYDATA(pkey);
+#endif
 
-		cert_list[0] = X509_GET_GNUTLS_DATA(pcrt);
-#if 0
-		rv = gnutls_certificate_set_x509_key(cred, cert_list, 1, X509_GET_GNUTLS_KEYDATA(pkey));
+		crts = purple_certificate_build_chain(user_pool, pcrt, NULL);
+
+		numcrts = g_list_length(crts);
+		cert_list = g_new0(gnutls_x509_crt_t, numcrts);
+		for (idx=0, item=g_list_first(crts); NULL != item; item = g_list_next(item),idx+=1) {
+			cert_list[idx] = X509_GET_GNUTLS_DATA((PurpleCertificate*)(item->data));
+		}
+
+		purple_debug_info("gnutls/ssl", "Added %d crts and 1 key to creds\n", numcrts);
+
+		rv = gnutls_certificate_set_x509_key(cred, cert_list, numcrts, X509_GET_GNUTLS_KEYDATA(pkey));
 		if (GNUTLS_E_SUCCESS != rv) {
 			purple_debug_error("gnutls/ssl",
 					  "Failed to set add certs to credentials: %s\n",
 					  gnutls_strerror(rv));
+			g_free(cert_list);
 			return FALSE;
 		}
-#endif
+		g_free(cert_list);
 		return TRUE;
 	}
 
============================================================
--- libpurple/protocols/jabber/libxmpp.c	bf737d7692b0c536ab219551fc66256e0046ac68
+++ libpurple/protocols/jabber/libxmpp.c	bb0a6be59335e919d7ae26797830a81a1e9d09b8
@@ -265,6 +265,7 @@ GList* jabber_get_account_options()
 	GList *options = NULL;
 	GList *encryption_values = NULL;
 	PurpleCertificatePool *cert_pool = NULL;
+	PurplePrivateKeyPool *key_pool = NULL;
 	GList *certificates = NULL;
 	
 	/* Destroy the current option list so we can recreated it. 
@@ -294,19 +295,21 @@ GList* jabber_get_account_options()
 	
 	ADD_VALUE(certificates, _(""), "");
 	cert_pool = purple_certificate_find_pool("x509", "user");
-	if (cert_pool) {
+	key_pool = purple_privatekey_find_pool("x509", "user");
+	if (cert_pool && key_pool) {
 		GList *id_list = NULL;
 		GList *item = NULL;
 		PurpleCertificate *cert = NULL;
 
-		id_list = purple_certificate_pool_get_idlist(cert_pool);
+		/* Only display certificates that have a private key */
+		id_list = purple_privatekey_pool_get_idlist(key_pool);
 		for (item = id_list; item != NULL; item = item->next) {
 			gchar* id = item->data;
 			cert = purple_certificate_pool_retrieve(cert_pool, id);
 			if (cert) {
 				PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
-				kvp->key = g_strdup(id);
-				kvp->value = purple_certificate_get_subject_name(cert);
+				kvp->key = purple_certificate_get_subject_name(cert);
+				kvp->value = g_strdup(id);
 				certificates = g_list_append(certificates, kvp);
 				purple_debug_info("xmpp/accountopt", "added cert %s to acct opt list\n", id);
 			}
@@ -314,7 +317,7 @@ GList* jabber_get_account_options()
 				purple_debug_warning("xmpp/accountopt", "Failed to find cert for id %s\n", id);
 			}
 		}
-		purple_certificate_pool_destroy_idlist(id_list);
+		purple_privatekey_pool_destroy_idlist(id_list);
 	}
 
 	option = purple_account_option_list_new(_("Login certificate"), "certificate_id", certificates);
============================================================
--- libpurple/certificate.c	42bd9d99c05d03dc1b950f0a2d6f85dd818c04e1
+++ libpurple/certificate.c	a403631b58a637e98dd9ba217836ce9f2a638bef
@@ -638,7 +638,52 @@ purple_certificate_pool_destroy_idlist(G
 	g_list_free(idlist);
 }
 
+GList*
+purple_certificate_build_chain(PurpleCertificatePool *pool, PurpleCertificate *crt, gboolean *complete)
+{
+	PurpleCertificate *issuer = NULL;
+	gchar *issuer_id = NULL;
+	gchar *crt_id = NULL;
+	GList *chain = NULL;
 
+	g_return_val_if_fail(NULL != pool, NULL);
+	g_return_val_if_fail(NULL != crt, NULL);
+
+	if (NULL != complete)
+		*complete = FALSE;
+
+	chain = g_list_append(chain, crt);
+
+	crt_id = purple_certificate_get_unique_id(crt);
+	for (;;) {
+		issuer_id = purple_certificate_get_issuer_unique_id(crt);
+		if (NULL != crt_id && NULL != issuer_id) {
+			if (0 == g_strcmp0(crt_id, issuer_id)) {
+				/* found the last cert */
+				if (NULL != complete)
+					*complete = TRUE;
+				break;
+			}
+			issuer = purple_certificate_pool_retrieve(pool, issuer_id);
+			if (NULL != issuer) {
+				purple_debug_info("cert", "add %s to cert chain\n", issuer_id);
+				chain = g_list_append(chain, issuer);
+				crt = issuer;
+				crt_id = issuer_id;
+			}
+			else {
+				purple_debug_info("cert",
+					"issuer %s not found\n", issuer_id);
+				break;
+			}
+		}
+		else
+			break;
+	}
+
+	return chain;
+}
+
 /****************************************************************************/
 /* Builtin Verifiers, Pools, etc.                                           */
 /****************************************************************************/
@@ -1365,7 +1410,7 @@ x509_user_delete_cert(const gchar *id)
 
 	/* Is the id even in the pool? */
 	if (!x509_user_cert_in_pool(id)) {
-		purple_debug_warning("certificate/tls_peers",
+		purple_debug_warning("certificate/user",
 				     "Id %s wasn't in the pool\n",
 				     id);
 		return FALSE;
@@ -1374,7 +1419,7 @@ x509_user_delete_cert(const gchar *id)
 	/* OK, so work out the keypath and delete the thing */
 	keypath = purple_certificate_pool_mkpath(&x509_user, id);
 	if ( unlink(keypath) != 0 ) {
-		purple_debug_error("certificate/tls_peers",
+		purple_debug_error("certificate/user",
 				   "Unlink of %s failed!\n",
 				   keypath);
 		ret = FALSE;
============================================================
--- libpurple/certificate.h	dbabc811a5eebadde1308f263fbdaa6855920c58
+++ libpurple/certificate.h	66e876816e6e2706f19d083fb59c7fc1e42e7e2c
@@ -213,7 +213,8 @@ struct _PurpleCertificateScheme
 	 *
 	 * @param crt   Certificate instance
 	 * @return Newly allocated string that can be used to uniquely
-	 *         identify the certificate.
+	 *         identify the certificate. The character set must be
+	 *         valid as a filename, so ASCII is safest.
 	 */
 	gchar * (* get_unique_id)(PurpleCertificate *crt);
 
@@ -561,6 +562,20 @@ purple_certificate_get_times(PurpleCerti
 gboolean
 purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration);
 
+/**
+ * Helper to build the certificate chain. Assumes the given pool stores certificates
+ * using purple_certificate_get_unique_id() as the id.
+ *
+ * @param pool Pool to search for issuer certificates
+ * @param crt End user certificate. 
+ * @param complete TRUE if the complete certificate chain is returned.
+ * @returns The certificate chain. The first certificate will be crt followed by
+ *          its issuer's certificate, and so on until the root CA is found or
+ *          no more issuer's certificate can be found. The chain is NOT
+ *          guaranteed to be complete.
+ */
+GList*
+purple_certificate_build_chain(PurpleCertificatePool *pool, PurpleCertificate *crt, gboolean *complete);
 /*@}*/
 
 /*****************************************************************************/
============================================================
--- pidgin/gtkcertmgr.c	a69a0ed1e497cdbb8068f11ffd0339a970185485
+++ pidgin/gtkcertmgr.c	a7d390346287ecda2e250e4c763db492c6a2b022
@@ -563,6 +563,7 @@ enum
 enum
 {
 	UM_NAME_COLUMN,
+	UM_ID_COLUMN,
 	UM_N_COLUMNS
 };
 
@@ -598,24 +599,29 @@ user_mgmt_repopulate_list(void)
 	g_return_if_fail(user_crts);
 	g_return_if_fail(user_keys);
 
-	/* Grab the loaded certificates */
-	idlist = purple_certificate_pool_get_idlist(user_crts);
+	/* Only show certs that have a corresponding private key. */
+	/* ids for the cert and key are the same */
+	idlist = purple_privatekey_pool_get_idlist(user_keys);
 
 	/* Populate the listview */
 	for (l = idlist; l; l = l->next) {
 		GtkTreeIter iter;
 
-		if ( ! purple_privatekey_pool_contains(user_keys, l->data)) {
-			purple_debug_warning("gtkcertmgr/user_mgmt",
-					     "User cert %s is missing it's private key.\n",
-					     (gchar*)l->data);
-		}
+		PurpleCertificate *crt;
+		crt = purple_certificate_pool_retrieve(user_crts, l->data);
+		if (NULL != crt) {
+			gchar *name = purple_certificate_get_subject_name(crt);
+			gtk_list_store_append(store, &iter);
 
-		gtk_list_store_append(store, &iter);
-
-		gtk_list_store_set(GTK_LIST_STORE(store), &iter,
-				   UM_NAME_COLUMN, l->data,
-				   -1);
+			gtk_list_store_set(GTK_LIST_STORE(store), &iter,
+					   UM_NAME_COLUMN, name,
+					   UM_ID_COLUMN, l->data,
+					   -1);
+		}
+		else {
+			purple_debug_error("gtkcertmgr/user_mgmt",
+				"Failed to find cert %s in pool\n", (gchar*)l->data);
+		}
 	}
 	purple_certificate_pool_destroy_idlist(idlist);
 }
@@ -655,7 +661,7 @@ typedef struct {
  */
 
 typedef struct {
-	PurpleCertificate *crt;
+	GList *crts;
 	PurplePrivateKey *key;
 	char *name;
 } pkcs12_import_data;
@@ -663,7 +669,7 @@ pkcs12_import_data_free(pkcs12_import_da
 static void
 pkcs12_import_data_free(pkcs12_import_data *data)
 {
-	purple_certificate_destroy(data->crt);
+	purple_certificate_destroy_list(data->crts);
 	purple_privatekey_destroy(data->key);
 	g_free(data->name);
 	g_free(data);
@@ -672,9 +678,17 @@ pkcs12_import_key_password_ok_cb(gboolea
 static void
 pkcs12_import_key_password_ok_cb(gboolean result, pkcs12_import_data *data)
 {
-	if (!purple_certificate_pool_store(um_dat->user_crts, data->name, data->crt)) {
-		purple_notify_error(um_dat, NULL, _("Failed to save imported certificate."), NULL);
-		/* TODO: deleted corresponding key stored in privatekey pool */
+	GList *i = NULL;
+	gchar *id = NULL;
+	PurpleCertificate *crt;
+
+	for(i = g_list_first(data->crts); NULL != i; i = g_list_next(i)) {
+		crt = (PurpleCertificate*)i->data;
+		id = purple_certificate_get_unique_id(crt);
+		if (!purple_certificate_pool_store(um_dat->user_crts, id, crt)) {
+			purple_notify_error(um_dat, NULL, _("Failed to save imported certificate."), NULL);
+			/* TODO: deleted corresponding key stored in privatekey pool */
+		}
 	}
 
 	pkcs12_import_data_free(data);
@@ -696,9 +710,10 @@ pkcs12_import_name_ok_cb(pkcs12_import_d
 	purple_privatekey_pool_store_request(
 		um_dat->user_keys,
 		data->name,
+		data->name,
 		data->key,
-		pkcs12_import_key_password_ok_cb,
-		pkcs12_import_key_password_cancel_cb,
+		G_CALLBACK(pkcs12_import_key_password_ok_cb),
+		G_CALLBACK(pkcs12_import_key_password_cancel_cb),
 		data);
 }
 
@@ -713,7 +728,7 @@ user_mgmt_import_pkcs12(const gchar* fil
 {
 	PurpleCertificateScheme *x509_crts;
 	PurplePrivateKeyScheme *x509_keys;
-	PurpleCertificate *crt = NULL;
+	GList *crts = NULL;
 	PurplePrivateKey *key = NULL;
 	pkcs12_import_data *data;
 	gboolean result;
@@ -727,47 +742,35 @@ user_mgmt_import_pkcs12(const gchar* fil
 	g_return_if_fail(x509_keys);
 
 	/* Now load the certificate/keys from disk */
-	result = purple_pkcs12_import(um_dat->pkcs12, filename, password, &crt, &key);
+	result = purple_pkcs12_import(um_dat->pkcs12, filename, password, &crts, &key);
 
 	/* Did it work? */
 	if (result) {
-		gchar *default_name;
+		/* key id must be the same as the corresponding cert */
+		/* We will only add all the certs to the pool if the key add is ok */
+		PurpleCertificate *crt = (PurpleCertificate*)(g_list_first(crts)->data);
+		gchar *id = purple_certificate_get_unique_id(crt);
+		gchar *name = purple_certificate_get_subject_name(crt);
 
-		/* Get name to add to pool as */
-		/* Make a guess about what the hostname should be */
-		 default_name = purple_certificate_get_subject_name(crt);
-		/* TODO: Find a way to make sure that crt & key gets destroyed
-		   if the window gets closed unusually, such as by handle
-		   deletion */
-		/* TODO: Display some more information on the certificate? */
-		
 		data = g_new0(pkcs12_import_data, 1);
-		data->crt = crt;
+		data->crts = crts;
 		data->key = key;
-		data->name = default_name;	
 
-		/* TODO: Enable custom cert name dialog  */
-		purple_request_input(um_dat,
-				     _("PKCS12 Import"),
-				     _("Specify a name"),
-				     _("Type the name for the imported certificate and key."),
-				     default_name,
-				     FALSE, /* Not multiline */
-				     FALSE, /* Not masked? */
-				     NULL,  /* No hints? */
-				     _("OK"),
-				     G_CALLBACK(pkcs12_import_name_ok_cb),
-				     _("Cancel"),
-				     G_CALLBACK(pkcs12_import_name_cancel_cb),
-				     NULL, NULL, NULL, /* No account/who/conv*/
-				     data);
+		purple_privatekey_pool_store_request(
+			um_dat->user_keys,
+			name,
+			id,
+			key,
+			G_CALLBACK(pkcs12_import_key_password_ok_cb),
+			G_CALLBACK(pkcs12_import_key_password_cancel_cb),
+			data);
 	} else {
 		/* Errors! Oh no! */
 		/* TODO: Perhaps find a way to be specific about what just
 		   went wrong? */
 		gchar * secondary;
 
-		purple_certificate_destroy(crt);
+		purple_certificate_destroy_list(crts);
 		purple_privatekey_destroy(key);
 
 		secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable, in PKCS12 format and you used the correct password.\n"), filename);
@@ -842,7 +845,7 @@ typedef struct {
  */
 
 typedef struct {
-	PurpleCertificate *crt;
+	GList *crts;
 	PurplePrivateKey *key;
 	char* filename;
 	char* id;
@@ -853,7 +856,7 @@ pkcs12_export_data_free(pkcs12_export_da
 {
 
 	g_return_if_fail(data);
-	purple_certificate_destroy(data->crt);
+	purple_certificate_destroy_list(data->crts);
 	purple_privatekey_destroy(data->key);
 	g_free(data->filename);
 	g_free(data->id);
@@ -886,7 +889,7 @@ pkcs12_export_password_ok_cb(pkcs12_expo
 
 	/* Finally create the pkcs12 file */
 
-	if (!purple_pkcs12_export(um_dat->pkcs12, data->filename, entry, data->crt, data->key)) {
+	if (!purple_pkcs12_export(um_dat->pkcs12, data->filename, entry, data->crts, data->key)) {
 		/* Errors! Oh no! */
 		/* TODO: Perhaps find a way to be specific about what just
 		   went wrong? */
@@ -986,10 +989,10 @@ user_mgmt_export_cb(GtkWidget *button, v
 	GtkTreeIter iter;
 	GtkTreeModel *model = NULL;
 	gchar *id = NULL;
+	gchar *name = NULL;
+	GList *chain = NULL;
 	pkcs12_export_data *data = NULL;
 
-	purple_debug_info("gtkcertmgr/user_mgmt", "1111111111111111\n");
-
 	/* See if things are selected */
 	if (!gtk_tree_selection_get_selected(select, &model, &iter)) {
 		purple_debug_warning("gtkcertmgr/user_mgmt",
@@ -998,7 +1001,7 @@ user_mgmt_export_cb(GtkWidget *button, v
 	}
 
 	/* Retrieve the selected name */
-	gtk_tree_model_get(model, &iter, UM_NAME_COLUMN, &id, -1);
+	gtk_tree_model_get(model, &iter, UM_ID_COLUMN, &id, -1);
 
 	/* Extract the certificate & keys from the pools now to make sure it doesn't
 	   get deleted out from under us */
@@ -1006,23 +1009,30 @@ user_mgmt_export_cb(GtkWidget *button, v
 
 	if (NULL == crt) {
 		purple_debug_error("gtkcertmgr/user_mgmt",
-	 				   "Id %s was not in the user cert pool?!\n",
-					   id);
+			"Id %s was not in the user cert pool?!\n", id);
 		g_free(id);
 		return;
 	}
 
+	chain = purple_certificate_build_chain(um_dat->user_crts, crt, NULL);
+
+	purple_debug_info("gtkcertmgr/user_mgmt",
+		"Got chain of %d certs\n", g_list_length(chain));
+
 	/* stuff we will need in our callbacks */
 	data = g_new0(pkcs12_export_data, 1);
-	data->crt = crt;
+	data->crts = chain;
 	data->id = id;
 
+	name = purple_certificate_get_subject_name(crt);
 	purple_privatekey_pool_retrieve_request(
 			um_dat->user_keys,
+			name,
 			id,
 			G_CALLBACK(pkcs12_get_key_password_ok_cb),
 			G_CALLBACK(pkcs12_get_key_password_cancel_cb),
 			data);
+	g_free(name);
 }
 
 /**********************************************************
@@ -1046,7 +1056,7 @@ user_mgmt_info_cb(GtkWidget *button, gpo
 	}
 
 	/* Retrieve the selected name */
-	gtk_tree_model_get(model, &iter, UM_NAME_COLUMN, &id, -1);
+	gtk_tree_model_get(model, &iter, UM_ID_COLUMN, &id, -1);
 
 	/* Now retrieve the certificate */
 	crt = purple_certificate_pool_retrieve(um_dat->user_crts, id);
@@ -1098,8 +1108,10 @@ user_mgmt_delete_cb(GtkWidget *button, g
 		gchar *primary;
 
 		/* Retrieve the selected hostname */
-		gtk_tree_model_get(model, &iter, UM_NAME_COLUMN, &id, -1);
+		gtk_tree_model_get(model, &iter, UM_ID_COLUMN, &id, -1);
 
+		/* TODO: What to do about issuer certs still there? */
+
 		/* Prompt to confirm deletion */
 		primary = g_strdup_printf(
 			_("Really delete certificate for %s?"), id );
@@ -1168,7 +1180,9 @@ user_mgmt_build(void)
 	gtk_widget_show(GTK_WIDGET(sw));
 
 	/* List view */
-	store = gtk_list_store_new(UM_N_COLUMNS, G_TYPE_STRING);
+	store = gtk_list_store_new(UM_N_COLUMNS,
+					G_TYPE_STRING,   /* Name */
+					G_TYPE_STRING);  /* Id */
 
 	um_dat->listview = listview =
 		GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
@@ -1189,6 +1203,13 @@ user_mgmt_build(void)
 
 		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
 				UM_NAME_COLUMN, GTK_SORT_ASCENDING);
+
+		column = gtk_tree_view_column_new_with_attributes(
+				_("Subject"),
+				renderer,
+				"text", UM_ID_COLUMN,
+				NULL);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
 	}
 
 	/* Get the treeview selector into the struct */
============================================================
--- libpurple/pkcs12.c	3973cbae39543e5119e29f597db46f71f18aa17e
+++ libpurple/pkcs12.c	defe4a56403ae5e5e52c39ff98f290652dad4f15
@@ -38,41 +38,73 @@ gboolean
 static GList *pkcs12_schemes = NULL;
 
 gboolean
-purple_pkcs12_import(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-					 PurpleCertificate **crt, PurplePrivateKey **key)
+purple_pkcs12_import(PurplePkcs12Scheme *scheme,
+		     const gchar *filename, const gchar *password,
+		     GList **crts, PurplePrivateKey **key)
 {
 	g_return_val_if_fail(scheme, FALSE);
 	g_return_val_if_fail(filename, FALSE);
 	g_return_val_if_fail(password, FALSE);
-	g_return_val_if_fail(crt, FALSE);
+	g_return_val_if_fail(crts, FALSE);
 	g_return_val_if_fail(key, FALSE);
 
-	return (scheme->import_pkcs12)(filename, password, crt, key);
+	return (scheme->import_pkcs12)(filename, password, crts, key);
 }
 
 gboolean
-purple_pkcs12_export(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-					 PurpleCertificate *crt, PurplePrivateKey *key)
+purple_pkcs12_export(PurplePkcs12Scheme *scheme,
+		     const gchar *filename, const gchar *password,
+		     GList *crts, PurplePrivateKey *key)
 {
 	g_return_val_if_fail(scheme, FALSE);
 	g_return_val_if_fail(filename, FALSE);
 	g_return_val_if_fail(password, FALSE);
-	g_return_val_if_fail(crt, FALSE);
-	g_return_val_if_fail(key, FALSE);
 
-	return (scheme->export_pkcs12)(filename, password, crt, key);
+	return (scheme->export_pkcs12)(filename, password, crts, key);
 }
 
 gboolean
 purple_pkcs12_import_to_pool(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-							 PurpleCertificatePool *certpool, PurplePrivateKeyPool *keypool)
+							 PurpleCertificatePool *crtpool, PurplePrivateKeyPool *keypool)
 {
+	GList *crts;
+	PurplePrivateKey *key;
+	GList *i;
+	gchar *id;
+
 	g_return_val_if_fail(scheme, FALSE);
 	g_return_val_if_fail(filename, FALSE);
 	g_return_val_if_fail(password, FALSE);
-	g_return_val_if_fail(certpool, FALSE);
+	g_return_val_if_fail(crtpool, FALSE);
 	g_return_val_if_fail(keypool, FALSE);
 
+
+	if (!purple_pkcs12_import(scheme, filename, password, &crts, &key))
+		return FALSE;
+
+	for (i = g_list_first(crts); NULL != g_list_next(i); i = g_list_next(i)) {
+		PurpleCertificate *crt = (PurpleCertificate*)i->data;
+
+		id = purple_certificate_get_unique_id(crt);
+		if (NULL == id)
+			goto error;
+
+		if (!purple_certificate_pool_store(crtpool, id, crt))
+			goto error;
+	}
+
+	id = purple_privatekey_get_unique_id(key);
+	if (NULL == id)
+		goto error;
+
+	if (!purple_privatekey_pool_store(keypool, id, key, password))
+		goto error;
+
+	return TRUE;
+
+error:
+	purple_certificate_destroy_list(crts);
+	purple_privatekey_destroy(key);
 	return FALSE;
 }
 
============================================================
--- libpurple/pkcs12.h	05d4a10d6c746924f33988bcc583483496e0cc8b
+++ libpurple/pkcs12.h	ee8c1699b2607827b15c6e779940cfd117e95372
@@ -67,28 +67,32 @@ struct _PurplePkcs12Scheme
 	gchar * fullname;
 
 	/**
-	* Imports PurpleCertificates and PurplePrivateKeys from a PKCS12 file
-	*
-	* @param filename    File path to import from
-	* @param password    Password protecting the PKCS12 file
-	* @param crt         Certificate in the PKCS12 file. Must be free'd by caller.
-	* @param key         Private key in the PKCS12 file. Must be free'd by caller.
-	* @return TRUE if at least one certificate and key were imported, and FALSE on failure
-	*/
+	 * Imports PurpleCertificates and PurplePrivateKeys from a PKCS12 file
+	 *
+	 * @param filename    File path to import from
+	 * @param password    Password protecting the PKCS12 file
+	 * @param crts        List of ptrs to PurpleCertificates from the PKCS12 file.
+	 *                    Must be free'd by caller.
+	 * @param key         PurplePrivateKey from the PKCS12 file.
+	 *                    Must be free'd by caller.
+	 *                    We only support one private key per PKCS12 file since we
+	 *                    are otherwise unable to match the key with its certificate.
+	 * @return TRUE if at least one certificate and key were imported, and FALSE on failure
+	 */
 	gboolean (*import_pkcs12)(const gchar *filename, const gchar *password,
-				  PurpleCertificate **crt, PurplePrivateKey **key);
+				  GList **crts, PurplePrivateKey **key);
 
 	/**
-	* Exports PurpleCertificates and PurplePrivateKey to a file
-	*
-	* @param filename    File to export the key to
-	* @param password    Password to protect the PKCS12 file
-	* @param crt         Certificate to export
-	* @param key         Key to export
-	* @return TRUE if the export succeeded, otherwise FALSE
-	*/
+	 * Exports PurpleCertificates and PurplePrivateKey to a file
+	 *
+	 * @param filename    File to export the key to
+	 * @param password    Password to protect the PKCS12 file
+	 * @param crts        List of ptrs to PurpleCertificates to export
+	 * @param key         PurplePrivateKey to export
+	 * @return TRUE if the export succeeded, otherwise FALSE
+	 */
 	gboolean (*export_pkcs12)(const gchar *filename, const gchar *password,
-				  PurpleCertificate *crt, PurplePrivateKey *key);
+				  GList *crts, PurplePrivateKey *key);
 
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
@@ -108,26 +112,35 @@ struct _PurplePkcs12Scheme
  * @param scheme      Scheme to import under
  * @param filename    File path to import from
  * @param password    Password protecting the PKCS12 file
- * @param crt         Certificate in the PKCS12 file. Must be free'd by caller.
- * @param key         Private key in the PKCS12 file. Must be free'd by caller.
+ * @param crts        Certificate chain from the PKCS12 file in the form of a list
+ *                    of ptrs to PurpleCertificates. The chain must be in order.
+ *                    The first certificate must be the certificate corresponding to
+ *                    key. Each certificate should be followed by the issuer's
+ *                    certificate and end at the root CA certificate. The whole chain
+ *                    need not be present.
+ *                    Must be free'd by caller.
+ * @param key         PurplePrivateKey from the PKCS12 file.
+ *                    Must be free'd by caller.
+ *                    We only support one private key per PKCS12 file since we are
+ *                    otherwise unable to match up the key with its certificate.
  * @return TRUE if at least one certificate and key were imported, and FALSE on failure
  */
 gboolean
 purple_pkcs12_import(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-					 PurpleCertificate **crt, PurplePrivateKey **key);
+		     GList **crts, PurplePrivateKey **key);
 
 /**
  * Exports PurpleCertificates and PurplePrivateKey to a file
  *
  * @param filename    File to export the key to
  * @param password    Password to protect the PKCS12 file
- * @param crt         Certificate to export
- * @param key         Key to export
+ * @param crts        List of ptrs to PurpleCertificates to export
+ * @param key         PurplePrivateKey to export
  * @return TRUE if the export succeeded, otherwise FALSE
  */
 gboolean
 purple_pkcs12_export(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-					 PurpleCertificate *crt, PurplePrivateKey *key);
+		     GList *crts, PurplePrivateKey *key);
 
 /**
  * Imports certificates and key into given certificate and private key pools.
@@ -141,7 +154,7 @@ purple_pkcs12_import_to_pool(PurplePkcs1
  */
 gboolean
 purple_pkcs12_import_to_pool(PurplePkcs12Scheme *scheme, const gchar *filename, const gchar *password,
-							 PurpleCertificatePool *certpool, PurplePrivateKeyPool *keypool);
+							 PurpleCertificatePool *crtpool, PurplePrivateKeyPool *keypool);
 
 /**
  * Request the password used to encrypt the pkcs12 file.
============================================================
--- libpurple/privatekey.c	036edfa2ab7aef714315d27bdbef35a019365127
+++ libpurple/privatekey.c	10e64ba504f487f60ae93642b85cf0ceb6b65a81
@@ -259,7 +259,8 @@ void purple_privatekey_pool_retrieve_req
 }
 
 void purple_privatekey_pool_retrieve_request(
-	PurplePrivateKeyPool *pool, const gchar *id,
+	PurplePrivateKeyPool *pool,
+	const gchar *friendly_name, const gchar *id,
 	GCallback ok_cb,
 	GCallback cancel_cb,
 	void* user_data)
@@ -283,7 +284,7 @@ void purple_privatekey_pool_retrieve_req
 	data->cancel_cb = G_CALLBACK(cancel_cb);
 	data->user_data = user_data;
 
-	msg = g_strdup_printf(_("Enter the password protecting the key named \"%s\""), id);
+	msg = g_strdup_printf(_("Enter the password protecting the key named \"%s\""), friendly_name);
 	purple_privatekey_pool_request_password(
 		msg,
 		pool,
@@ -323,7 +324,9 @@ purple_privatekey_pool_store_request(
 
 void 
 purple_privatekey_pool_store_request(
-	PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key,
+	PurplePrivateKeyPool *pool,
+	const gchar *friendly_name, const gchar *id,
+	PurplePrivateKey *key,
 	GCallback ok_cb,
 	GCallback cancel_cb,
 	void* user_data)
@@ -346,7 +349,7 @@ purple_privatekey_pool_store_request(
 	data->cancel_cb = G_CALLBACK(cancel_cb);
 	data->user_data = user_data;
 
-	msg = g_strdup_printf(_("Enter a password to protect the key named \"%s\""), id);
+	msg = g_strdup_printf(_("Enter a password to protect the key named \"%s\""), friendly_name);
 	purple_privatekey_pool_request_password(
 		msg,
 		pool,
============================================================
--- libpurple/privatekey.h	1af336d71d294c9a3d2bf3504408958a23eb1d4d
+++ libpurple/privatekey.h	912c0eaa58d077a2d793ab0eaebd69d0d96801b9
@@ -196,7 +196,8 @@ struct _PurplePrivateKeyScheme
 	 *
 	 * @param key PrivateKey instance
 	 * @return Newly allocated string that can be used to uniquely
-	 *         identify the key.
+	 *         identify the key. The chacter set must be valid in
+	 *         a filename so ASCII is safest.
 	 */
 	gchar* (* get_unique_id)(PurplePrivateKey *key);
 
@@ -326,6 +327,7 @@ typedef void (*PurplePrivateKeyPoolStore
  * Retrieve a key from a pool and prompt user for the password protecting the key.
  *
  * @param pool      Pool to get key from
+ * @param friendly_name Name of key to show in request message.
  * @param id        ID of key to retrieve
  * @param ok_cb     Called if the user clicks ok in the password prompt.
  *                  The key parameter to the callback is non-null if the key was successfully
@@ -335,7 +337,8 @@ purple_privatekey_pool_retrieve_request(
  */
 void 
 purple_privatekey_pool_retrieve_request(
-	PurplePrivateKeyPool *pool, const gchar *id,
+	PurplePrivateKeyPool *pool,
+	const gchar *friendly_name, const gchar *id,
 	GCallback ok_cb,
 	GCallback cancel_cb,
 	void* user_data);
@@ -344,6 +347,7 @@ purple_privatekey_pool_retrieve_request(
  * Store a key in the given pool and prompt user for a password to protect the key.
  *
  * @param pool      Pool to store key in
+ * @param friendly_name Name of key to show in request message.
  * @param id        ID of key to store
  * @param key       Key to store.
  * @param ok_cb     Called if the user clicks ok in the password prompt.
@@ -354,7 +358,9 @@ purple_privatekey_pool_store_request(
  */
 void 
 purple_privatekey_pool_store_request(
-	PurplePrivateKeyPool *pool, const gchar *id, PurplePrivateKey *key,
+	PurplePrivateKeyPool *pool,
+	const gchar* friendly_name, const gchar *id,
+	PurplePrivateKey *key,
 	GCallback ok_cb,
 	GCallback cancel_cb,
 	void* user_data);


More information about the Commits mailing list