changeset 32718:3c420b12a034

propagate from branch 'im.pidgin.pidgin' (head 78f2facd08a9b906e2ab5491bb828763c80ea46a) to branch 'im.pidgin.cpw.qulogic.gtk3' (head ec42ed4101a842f3dc81c13f1942996951d08422)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sun, 26 Feb 2012 05:51:49 +0000
parents c1daad7121f8 (diff) 194f66d5089a (current diff)
children 0a184aea3efd
files pidgin/gtkrequest.c
diffstat 11 files changed, 387 insertions(+), 249 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Feb 23 09:00:17 2012 +0000
+++ b/ChangeLog	Sun Feb 26 05:51:49 2012 +0000
@@ -24,8 +24,6 @@
 	MSN:
 	* Fix file transfer with older Mac MSN clients.
 	* Support file transfers up to ~9 EiB.
-	* Support new protocol version MSNP18. (#14753)
-	* Fix messages to offline contacts. (#14302)
 
 	MXit:
 	* Remove all reference to Hidden Number.
@@ -70,6 +68,8 @@
 	MSN:
 	* Fix possible crashes caused by not validating incoming messages as
 	  UTF-8. (Thijs Alkemade) (#14884)
+	* Support new protocol version MSNP18. (#14753)
+	* Fix messages to offline contacts. (#14302)
 
 	Windows-Specific Changes:
 	* Fix compilation of the Bonjour protocol plugin. (#14802)
--- a/ChangeLog.API	Thu Feb 23 09:00:17 2012 +0000
+++ b/ChangeLog.API	Sun Feb 26 05:51:49 2012 +0000
@@ -8,6 +8,8 @@
 		* purple_account_get_ui_data
 		* purple_account_set_ui_data
 		* purple_account_register_completed
+		* purple_certificate_get_der_data
+		* purple_certificate_get_display_string
 		* purple_conv_chat_cb_get_alias
 		* purple_conv_chat_cb_get_flags
 		* purple_conv_chat_cb_is_buddy
@@ -32,6 +34,9 @@
 		* purple_menu_action_set_data
 		* purple_menu_action_set_callback
 		* purple_menu_action_set_children
+		* purple_request_certificate
+		* purple_request_field_certificate_new
+		* purple_request_field_certificate_get_value
 		* purple_request_field_get_tooltip
 		* purple_request_field_group_get_fields_list
 		* purple_request_field_set_tooltip
@@ -159,6 +164,8 @@
 		* purple_buddy_icons_set_custom_icon
 		* purple_certificate_check_signature_chain_with_failing. Use
 		  purple_certificate_check_signature_chain, instead
+		* purple_certificate_display_x509. Use purple_request_certificate,
+		  instead
 		* purple_connection_error_reason
 		* purple_connection_new
 		* purple_connection_new_unregister
--- a/finch/gntrequest.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/finch/gntrequest.c	Sun Feb 26 05:51:49 2012 +0000
@@ -571,6 +571,22 @@
 	return combo;
 }
 
+static GntWidget*
+create_certificate_field(PurpleRequestField *field)
+{
+	GntWidget *w;
+	PurpleCertificate *cert;
+	char *str;
+
+	cert = purple_request_field_certificate_get_value(field);
+	str = purple_certificate_get_display_string(cert);
+	w = gnt_label_new(str);
+
+	g_free(str);
+
+	return w;
+}
+
 static void *
 finch_request_fields(const char *title, const char *primary,
 		const char *secondary, PurpleRequestFields *allfields,
@@ -650,6 +666,10 @@
 				accountlist = create_account_field(field);
 				purple_request_field_set_ui_data(field, accountlist);
 			}
+			else if (type == PURPLE_REQUEST_FIELD_CERTIFICATE)
+			{
+				purple_request_field_set_ui_data(field, create_certificate_field(field));
+			}
 			else
 			{
 				purple_request_field_set_ui_data(field, gnt_label_new_with_format(_("Not implemented yet."),
--- a/libpurple/certificate.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/certificate.c	Sun Feb 26 05:51:49 2012 +0000
@@ -518,6 +518,24 @@
 }
 
 gchar *
+purple_certificate_get_display_string(PurpleCertificate *crt)
+{
+	PurpleCertificateScheme *scheme;
+	gchar *str;
+
+	g_return_val_if_fail(crt, NULL);
+	g_return_val_if_fail(crt->scheme, NULL);
+
+	scheme = crt->scheme;
+
+	g_return_val_if_fail(scheme->get_display_string, NULL);
+
+	str = (scheme->get_display_string)(crt);
+
+	return str;
+}
+
+gchar *
 purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id)
 {
 	gchar *path;
@@ -662,77 +680,62 @@
 /****************************************************************************/
 
 static void
-x509_singleuse_verify_cb (PurpleCertificateVerificationRequest *vrq, gint id)
+x509_singleuse_verify_accept_cb(PurpleCertificateVerificationRequest *vrq)
 {
 	g_return_if_fail(vrq);
 
 	purple_debug_info("certificate/x509_singleuse",
-			  "VRQ on cert from %s gave %d\n",
-			  vrq->subject_name, id);
+			  "VRQ on cert from %s accepted\n",
+			  vrq->subject_name);
+
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
+}
 
-	/* Signal what happened back to the caller */
-	if (1 == id) {
-		/* Accepted! */
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_VALID);
-	} else {
-		/* Not accepted */
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_INVALID);
+static void
+x509_singleuse_verify_reject_cb(PurpleCertificateVerificationRequest *vrq)
+{
+	g_return_if_fail(vrq);
 
-	}
+	purple_debug_info("certificate/x509_singleuse",
+			  "VRQ on cert from %s rejected\n",
+			  vrq->subject_name);
+
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
 }
 
 static void
 x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq)
 {
-	gchar *sha_asc;
-	GByteArray *sha_bin;
 	gchar *cn;
 	const gchar *cn_match;
 	gchar *primary, *secondary;
-	PurpleCertificate *crt = (PurpleCertificate *) vrq->cert_chain->data;
+	PurpleCertificate *crt = (PurpleCertificate *)vrq->cert_chain->data;
 
-	/* Pull out the SHA1 checksum */
-	sha_bin = purple_certificate_get_fingerprint_sha1(crt);
-	/* Now decode it for display */
-	sha_asc = purple_base16_encode_chunked(sha_bin->data,
-					       sha_bin->len);
-
-	/* Get the cert Common Name */
 	cn = purple_certificate_get_subject_name(crt);
 
-	/* Determine whether the name matches */
 	if (purple_certificate_check_subject_name(crt, vrq->subject_name)) {
-		cn_match = "";
+		cn_match = _("(MATCH)");
 	} else {
 		cn_match = _("(DOES NOT MATCH)");
 	}
 
-	/* Make messages */
 	primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name);
-	secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc);
+	secondary = g_strdup_printf(_("Common name: %s %s"), cn, cn_match);
 
 	/* Make a semi-pretty display */
-	purple_request_accept_cancel(
+	purple_request_certificate(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
 		_("Single-use Certificate Verification"),
 		primary,
 		secondary,
-		0,            /* Accept by default */
-		NULL,         /* No account */
-		NULL,         /* No other user */
-		NULL,         /* No associated conversation */
-		vrq,
-		x509_singleuse_verify_cb,
-		x509_singleuse_verify_cb );
+		crt,
+		_("Accept"), G_CALLBACK(x509_singleuse_verify_accept_cb),
+		_("Reject"), G_CALLBACK(x509_singleuse_verify_reject_cb),
+		vrq);
 
-	/* Cleanup */
 	g_free(cn);
 	g_free(primary);
 	g_free(secondary);
-	g_free(sha_asc);
-	g_byte_array_free(sha_bin, TRUE);
 }
 
 static void
@@ -1289,102 +1292,34 @@
 static PurpleCertificateVerifier x509_tls_cached;
 
 
-/* The following is several hacks piled together and needs to be fixed.
- * It exists because show_cert (see its comments) needs the original reason
- * given to user_auth in order to rebuild the dialog.
- */
-/* TODO: This will cause a ua_ctx to become memleaked if the request(s) get
-   closed by handle or otherwise abnormally. */
-typedef struct {
-	PurpleCertificateVerificationRequest *vrq;
-	gchar *reason;
-} x509_tls_cached_ua_ctx;
-
-static x509_tls_cached_ua_ctx *
-x509_tls_cached_ua_ctx_new(PurpleCertificateVerificationRequest *vrq,
-			   const gchar *reason)
+static void
+x509_tls_cached_user_auth_accept_cb(PurpleCertificateVerificationRequest *vrq)
 {
-	x509_tls_cached_ua_ctx *c;
+	PurpleCertificatePool *tls_peers;
+	gchar *cache_id;
 
-	c = g_new0(x509_tls_cached_ua_ctx, 1);
-	c->vrq = vrq;
-	c->reason = g_strdup(reason);
+	g_return_if_fail(vrq);
+
+	tls_peers = purple_certificate_find_pool("x509", "tls_peers");
 
-	return c;
-}
-
+	cache_id = vrq->subject_name;
+	purple_debug_info("certificate/x509/tls_cached",
+				  "User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
+				  cache_id);
 
-static void
-x509_tls_cached_ua_ctx_free(x509_tls_cached_ua_ctx *c)
-{
-	g_return_if_fail(c);
-	g_free(c->reason);
-	g_free(c);
+	purple_certificate_pool_store(tls_peers, cache_id, vrq->cert_chain->data);
+
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
 }
 
 static void
-x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
-			  const gchar *reason);
-
-static void
-x509_tls_cached_show_cert(x509_tls_cached_ua_ctx *c, gint id)
+x509_tls_cached_user_auth_reject_cb(PurpleCertificateVerificationRequest *vrq)
 {
-	PurpleCertificate *disp_crt = c->vrq->cert_chain->data;
-
-	/* Since clicking a button closes the request, show it again */
-	x509_tls_cached_user_auth(c->vrq, c->reason);
-
-	/* Show the certificate AFTER re-opening the dialog so that this
-	   appears above the other */
-	purple_certificate_display_x509(disp_crt);
-
-	x509_tls_cached_ua_ctx_free(c);
-}
-
-static void
-x509_tls_cached_user_auth_cb (x509_tls_cached_ua_ctx *c, gint id)
-{
-	PurpleCertificateVerificationRequest *vrq;
-	PurpleCertificatePool *tls_peers;
-
-	g_return_if_fail(c);
-	g_return_if_fail(c->vrq);
-
-	vrq = c->vrq;
-
-	x509_tls_cached_ua_ctx_free(c);
+	g_return_if_fail(vrq);
 
-	tls_peers = purple_certificate_find_pool("x509","tls_peers");
-
-	if (2 == id) {
-		gchar *cache_id = vrq->subject_name;
-		purple_debug_info("certificate/x509/tls_cached",
-				  "User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
-				  cache_id);
-
-		purple_certificate_pool_store(tls_peers, cache_id,
-					      vrq->cert_chain->data);
+	purple_debug_warning("certificate/x509/tls_cached", "User REJECTED cert\n");
 
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_VALID);
-	} else {
-		purple_debug_warning("certificate/x509/tls_cached",
-				  "User REJECTED cert\n");
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_INVALID);
-	}
-}
-
-static void
-x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx *c, gint ignore)
-{
-	x509_tls_cached_user_auth_cb(c, 2);
-}
-
-static void
-x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx *c, gint ignore)
-{
-	x509_tls_cached_user_auth_cb(c, 1);
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
 }
 
 /** Validates a certificate by asking the user
@@ -1398,27 +1333,19 @@
 {
 	gchar *primary;
 
-	/* Make messages */
 	primary = g_strdup_printf(_("Accept certificate for %s?"),
 				  vrq->subject_name);
 
-	/* Make a semi-pretty display */
-	purple_request_action(
+	purple_request_certificate(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
 		_("SSL Certificate Verification"),
 		primary,
 		reason,
-		0,            /* Accept by default */
-		NULL,         /* No account */
-		NULL,         /* No other user */
-		NULL,         /* No associated conversation */
-		x509_tls_cached_ua_ctx_new(vrq, reason),
-		3,            /* Number of actions */
-		_("Accept"), x509_tls_cached_user_auth_accept_cb,
-		_("Reject"),  x509_tls_cached_user_auth_reject_cb,
-		_("_View Certificate..."), x509_tls_cached_show_cert);
+		vrq->cert_chain->data,
+		_("Accept"), G_CALLBACK(x509_tls_cached_user_auth_accept_cb),
+		_("Reject"), G_CALLBACK(x509_tls_cached_user_auth_reject_cb),
+		vrq);
 
-	/* Cleanup */
 	g_free(primary);
 }
 
@@ -2165,63 +2092,6 @@
 /* Scheme-specific functions                                                */
 /****************************************************************************/
 
-void
-purple_certificate_display_x509(PurpleCertificate *crt)
-{
-	gchar *sha_asc;
-	GByteArray *sha_bin;
-	gchar *cn;
-	time_t activation, expiration;
-	gchar *activ_str, *expir_str;
-	gchar *secondary;
-
-	/* Pull out the SHA1 checksum */
-	sha_bin = purple_certificate_get_fingerprint_sha1(crt);
-	/* Now decode it for display */
-	sha_asc = purple_base16_encode_chunked(sha_bin->data,
-					       sha_bin->len);
-
-	/* Get the cert Common Name */
-	/* TODO: Will break on CA certs */
-	cn = purple_certificate_get_subject_name(crt);
-
-	/* Get the certificate times */
-	/* TODO: Check the times against localtime */
-	/* TODO: errorcheck? */
-	if (!purple_certificate_get_times(crt, &activation, &expiration)) {
-		purple_debug_error("certificate",
-				   "Failed to get certificate times!\n");
-		activation = expiration = 0;
-	}
-	activ_str = g_strdup(ctime(&activation));
-	expir_str = g_strdup(ctime(&expiration));
-
-	/* Make messages */
-	secondary = g_strdup_printf(_("Common name: %s\n\n"
-								  "Fingerprint (SHA1): %s\n\n"
-								  "Activation date: %s\n"
-								  "Expiration date: %s\n"),
-								cn ? cn : "(null)",
-								sha_asc ? sha_asc : "(null)",
-								activ_str ? activ_str : "(null)",
-								expir_str ? expir_str : "(null)");
-
-	/* Make a semi-pretty display */
-	purple_notify_info(
-		NULL,         /* TODO: Find what the handle ought to be */
-		_("Certificate Information"),
-		"",
-		secondary);
-
-	/* Cleanup */
-	g_free(cn);
-	g_free(secondary);
-	g_free(sha_asc);
-	g_free(activ_str);
-	g_free(expir_str);
-	g_byte_array_free(sha_bin, TRUE);
-}
-
 void purple_certificate_add_ca_search_path(const char *path)
 {
 	if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
--- a/libpurple/certificate.h	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/certificate.h	Sun Feb 26 05:51:49 2012 +0000
@@ -261,8 +261,16 @@
 	 */
 	GByteArray * (* get_der_data)(PurpleCertificate *crt);
 
+	/**
+	 * Retrieves a string representation of the certificate suitable for display
+	 *
+	 * @param crt   Certificate instance
+	 * @return User-displayable string representation of certificate - must be
+	 *         freed using g_free().
+	 */
+	gchar * (* get_display_string)(PurpleCertificate *crt);
+
 	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
 };
 
 /** A set of operations used to provide logic for verifying a Certificate's
@@ -577,6 +585,17 @@
 GByteArray *
 purple_certificate_get_der_data(PurpleCertificate *crt);
 
+/**
+ * Retrieves a string suitable for displaying a certificate to the user.
+ *
+ * @param crt Certificate instance
+ *
+ * @return String representing the certificate that may be displayed to the user
+ *         - must be freed using g_free().
+ */
+char *
+purple_certificate_get_display_string(PurpleCertificate *crt);
+
 /*@}*/
 
 /*****************************************************************************/
@@ -815,15 +834,6 @@
 
 
 /**
- * Displays a window showing X.509 certificate information
- *
- * @param crt    Certificate under an "x509" Scheme
- * @todo Will break on CA certs, as they have no Common Name
- */
-void
-purple_certificate_display_x509(PurpleCertificate *crt);
-
-/**
  * Add a search path for certificates.
  *
  * @param path   Path to search for certificates.
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Sun Feb 26 05:51:49 2012 +0000
@@ -1173,6 +1173,55 @@
 	return data;
 }
 
+static gchar *
+x509_display_string(PurpleCertificate *crt)
+{
+	gchar *sha_asc;
+	GByteArray *sha_bin;
+	gchar *cn;
+	time_t activation, expiration;
+	gchar *activ_str, *expir_str;
+	gchar *text;
+
+	/* Pull out the SHA1 checksum */
+	sha_bin = x509_sha1sum(crt);
+	sha_asc = purple_base16_encode_chunked(sha_bin->data, sha_bin->len);
+
+	/* Get the cert Common Name */
+	/* TODO: Will break on CA certs */
+	cn = x509_common_name(crt);
+
+	/* Get the certificate times */
+	/* TODO: Check the times against localtime */
+	/* TODO: errorcheck? */
+	if (!x509_times(crt, &activation, &expiration)) {
+		purple_debug_error("certificate",
+				   "Failed to get certificate times!\n");
+		activation = expiration = 0;
+	}
+	activ_str = g_strdup(ctime(&activation));
+	expir_str = g_strdup(ctime(&expiration));
+
+	/* Make messages */
+	text = g_strdup_printf(_("Common name: %s\n\n"
+	                         "Fingerprint (SHA1): %s\n\n"
+	                         "Activation date: %s\n"
+	                         "Expiration date: %s\n"),
+	                       cn ? cn : "(null)",
+	                       sha_asc ? sha_asc : "(null)",
+	                       activ_str ? activ_str : "(null)",
+	                       expir_str ? expir_str : "(null)");
+
+	/* Cleanup */
+	g_free(cn);
+	g_free(sha_asc);
+	g_free(activ_str);
+	g_free(expir_str);
+	g_byte_array_free(sha_bin, TRUE);
+
+	return text;
+}
+
 /* X.509 certificate operations provided by this plugin */
 static PurpleCertificateScheme x509_gnutls = {
 	"x509",                          /* Scheme name */
@@ -1190,8 +1239,8 @@
 	x509_times,                      /* Activation/Expiration time */
 	x509_importcerts_from_file,      /* Multiple certificates import function */
 	x509_get_der_data,               /* Binary DER data */
+	x509_display_string,             /* Display representation */
 
-	NULL,
 	NULL
 
 };
--- a/libpurple/plugins/ssl/ssl-nss.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Sun Feb 26 05:51:49 2012 +0000
@@ -953,6 +953,55 @@
 	return data;
 }
 
+static gchar *
+x509_display_string(PurpleCertificate *crt)
+{
+	gchar *sha_asc;
+	GByteArray *sha_bin;
+	gchar *cn;
+	time_t activation, expiration;
+	gchar *activ_str, *expir_str;
+	gchar *text;
+
+	/* Pull out the SHA1 checksum */
+	sha_bin = x509_sha1sum(crt);
+	sha_asc = purple_base16_encode_chunked(sha_bin->data, sha_bin->len);
+
+	/* Get the cert Common Name */
+	/* TODO: Will break on CA certs */
+	cn = x509_common_name(crt);
+
+	/* Get the certificate times */
+	/* TODO: Check the times against localtime */
+	/* TODO: errorcheck? */
+	if (!x509_times(crt, &activation, &expiration)) {
+		purple_debug_error("certificate",
+				   "Failed to get certificate times!\n");
+		activation = expiration = 0;
+	}
+	activ_str = g_strdup(ctime(&activation));
+	expir_str = g_strdup(ctime(&expiration));
+
+	/* Make messages */
+	text = g_strdup_printf(_("Common name: %s\n\n"
+	                         "Fingerprint (SHA1): %s\n\n"
+	                         "Activation date: %s\n"
+	                         "Expiration date: %s\n"),
+	                       cn ? cn : "(null)",
+	                       sha_asc ? sha_asc : "(null)",
+	                       activ_str ? activ_str : "(null)",
+	                       expir_str ? expir_str : "(null)");
+
+	/* Cleanup */
+	g_free(cn);
+	g_free(sha_asc);
+	g_free(activ_str);
+	g_free(expir_str);
+	g_byte_array_free(sha_bin, TRUE);
+
+	return text;
+}
+
 static PurpleCertificateScheme x509_nss = {
 	"x509",                          /* Scheme name */
 	N_("X.509 Certificates"),        /* User-visible scheme name */
@@ -969,8 +1018,8 @@
 	x509_times,                      /* Activation/Expiration time */
 	x509_importcerts_from_file,      /* Multiple certificate import function */
 	x509_get_der_data,               /* Binary DER data */
+	x509_display_string,             /* Display representation */
 
-	NULL,
 	NULL
 };
 
--- a/libpurple/request.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/request.c	Sun Feb 26 05:51:49 2012 +0000
@@ -119,6 +119,11 @@
 			gsize size;
 		} image;
 
+		struct
+		{
+			PurpleCertificate *cert;
+		} certificate;
+
 	} u;
 
 	void *ui_data;
@@ -1329,6 +1334,31 @@
 	return field->u.account.filter_func;
 }
 
+PurpleRequestField *
+purple_request_field_certificate_new(const char *id, const char *text, PurpleCertificate *cert)
+{
+	PurpleRequestField *field;
+
+	g_return_val_if_fail(id   != NULL, NULL);
+	g_return_val_if_fail(text != NULL, NULL);
+	g_return_val_if_fail(cert != NULL, NULL);
+
+	field = purple_request_field_new(id, text, PURPLE_REQUEST_FIELD_CERTIFICATE);
+
+	field->u.certificate.cert = cert;
+
+	return field;
+}
+
+PurpleCertificate *
+purple_request_field_certificate_get_value(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_CERTIFICATE, NULL);
+
+	return field->u.certificate.cert;
+}
+
 /* -- */
 
 void *
@@ -1636,6 +1666,29 @@
 	return NULL;
 }
 
+void *
+purple_request_certificate(void *handle, const char *title,
+                                  const char *primary, const char *secondary,
+                                  PurpleCertificate *cert,
+                                  const char *ok_text, GCallback ok_cb,
+                                  const char *cancel_text, GCallback cancel_cb,
+                                  void *user_data)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+	field = purple_request_field_certificate_new("certificate", "Certificate", cert);
+	purple_request_field_group_add_field(group, field);
+
+	return purple_request_fields(handle, title, primary, secondary, fields,
+	                             ok_text, ok_cb, cancel_text, cancel_cb,
+	                             NULL, NULL, NULL, user_data);
+}
+
 static void
 purple_request_close_info(PurpleRequestInfo *info)
 {
--- a/libpurple/request.h	Thu Feb 23 09:00:17 2012 +0000
+++ b/libpurple/request.h	Sun Feb 26 05:51:49 2012 +0000
@@ -76,7 +76,8 @@
 	PURPLE_REQUEST_FIELD_LIST,
 	PURPLE_REQUEST_FIELD_LABEL,
 	PURPLE_REQUEST_FIELD_IMAGE,
-	PURPLE_REQUEST_FIELD_ACCOUNT
+	PURPLE_REQUEST_FIELD_ACCOUNT,
+	PURPLE_REQUEST_FIELD_CERTIFICATE
 
 } PurpleRequestFieldType;
 
@@ -1170,6 +1171,36 @@
 /*@}*/
 
 /**************************************************************************/
+/** @name Certificate Field API                                           */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates a certificate field.
+ *
+ * @param id   The field ID.
+ * @param text The label of the field.
+ * @param cert The certificate of the field.
+ *
+ * @return The new field.
+ */
+PurpleRequestField *purple_request_field_certificate_new(const char *id,
+														 const char *text,
+														 PurpleCertificate *cert);
+
+/**
+ * Returns the certificate in a certificate field.
+ *
+ * @param field The field.
+ *
+ * @return The certificate.
+ */
+PurpleCertificate *purple_request_field_certificate_get_value(
+		const PurpleRequestField *field);
+
+/*@}*/
+
+/**************************************************************************/
 /** @name Request API                                                     */
 /**************************************************************************/
 /*@{*/
@@ -1500,6 +1531,37 @@
 	PurpleAccount *account, const char *who, PurpleConversation *conv,
 	void *user_data);
 
+/**
+ * Prompts the user for action over a certificate.
+ *
+ * This is often represented as a dialog with a button for each action.
+ *
+ * @param handle        The plugin or connection handle.  For some things this
+ *                      is <em>extremely</em> important.  See the comments on
+ *                      purple_request_input().
+ * @param title         The title of the message, or @c NULL if it should have
+ *                      no title.
+ * @param primary       The main point of the message, or @c NULL if you're
+ *                      feeling enigmatic.
+ * @param secondary     Secondary information, or @c NULL if there is none.
+ * @param cert          The #PurpleCertificate associated with this request.
+ * @param ok_text       The text for the @c OK button, which may not be @c NULL.
+ * @param ok_cb         The callback for the @c OK button, which may not be
+ *                      @c NULL.
+ * @param cancel_text   The text for the @c Cancel button, which may not be
+ *                      @c NULL.
+ * @param cancel_cb     The callback for the @c Cancel button, which may be
+ *                      @c NULL.
+ * @param user_data     The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ */
+void *purple_request_certificate(void *handle, const char *title,
+	const char *primary, const char *secondary, PurpleCertificate *cert,
+	const char *ok_text, GCallback ok_cb,
+	const char *cancel_text, GCallback cancel_cb,
+	void *user_data);
+
 /*@}*/
 
 /**************************************************************************/
--- a/pidgin/gtkcertmgr.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/pidgin/gtkcertmgr.c	Sun Feb 26 05:51:49 2012 +0000
@@ -40,12 +40,6 @@
 
 #include "gtkcertmgr.h"
 
-#ifdef ENABLE_GCR
-#define GCR_API_SUBJECT_TO_CHANGE
-#include <gcr/gcr.h>
-#include <gcr/gcr-simple-certificate.h>
-#endif
-
 /*****************************************************************************
  * X.509 tls_peers management interface                                      *
  *****************************************************************************/
@@ -316,13 +310,7 @@
 	GtkTreeModel *model;
 	gchar *id;
 	PurpleCertificate *crt;
-#ifdef ENABLE_GCR
-	GByteArray *der;
-	GcrCertificate *gcrt;
 	char *title;
-	GtkWidget *dialog;
-	GcrCertificateBasicsWidget *cert_widget;
-#endif
 
 	/* See if things are selected */
 	if (!gtk_tree_selection_get_selected(select, &model, &iter)) {
@@ -338,38 +326,15 @@
 	crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id);
 	g_return_if_fail(crt);
 
-#ifdef ENABLE_GCR
-	der = purple_certificate_get_der_data(crt);
-	g_return_if_fail(der);
-
-	gcrt = gcr_simple_certificate_new(der->data, der->len);
-	g_return_if_fail(gcrt);
-
 	/* Fire the notification */
 	title = g_strdup_printf(_("Certificate Information for %s"), id);
-	dialog = gtk_dialog_new_with_buttons(title,
-	                                     NULL,
-	                                     0,
-	                                     GTK_STOCK_OK,
-	                                     GTK_RESPONSE_ACCEPT,
-	                                     NULL);
-	cert_widget = gcr_certificate_basics_widget_new(gcrt);
-	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
-	                   GTK_WIDGET(cert_widget), TRUE, TRUE, 0);
-	g_signal_connect_swapped(dialog, "response",
-	                         G_CALLBACK(gtk_widget_destroy),
-	                         dialog);
-	gtk_widget_show_all(dialog);
-
-	g_byte_array_free(der, TRUE);
-	g_object_unref(G_OBJECT(gcrt));
-#else
-	/* Fire the notification */
-	purple_certificate_display_x509(crt);
+	purple_request_certificate(tpm_dat, title, NULL, NULL, crt,
+	                           _("OK"), G_CALLBACK(purple_certificate_destroy),
+	                           _("Cancel"), G_CALLBACK(purple_certificate_destroy),
+	                           crt);
 
 	g_free(id);
-	purple_certificate_destroy(crt);
-#endif
+	g_free(title);
 }
 
 static void
--- a/pidgin/gtkrequest.c	Thu Feb 23 09:00:17 2012 +0000
+++ b/pidgin/gtkrequest.c	Sun Feb 26 05:51:49 2012 +0000
@@ -39,6 +39,12 @@
 
 #include <gdk/gdkkeysyms.h>
 
+#ifdef ENABLE_GCR
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+#include <gcr/gcr-simple-certificate.h>
+#endif
+
 #if !GTK_CHECK_VERSION(2,18,0)
 #define gtk_widget_set_can_default(x,y) do {\
 	if (y) \
@@ -1222,6 +1228,51 @@
 	return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1);
 }
 
+static GtkWidget *
+create_certificate_field(PurpleRequestField *field)
+{
+	PurpleCertificate *cert;
+#ifdef ENABLE_GCR
+	GcrCertificateBasicsWidget *cert_widget;
+	GByteArray *der;
+	GcrCertificate *gcrt;
+#else
+	GtkWidget *cert_label;
+	char *str;
+	char *escaped;
+#endif
+
+	cert = purple_request_field_certificate_get_value(field);
+
+#ifdef ENABLE_GCR
+	der = purple_certificate_get_der_data(cert);
+	g_return_val_if_fail(der, NULL);
+
+	gcrt = gcr_simple_certificate_new(der->data, der->len);
+	g_return_val_if_fail(gcrt, NULL);
+
+	cert_widget = gcr_certificate_basics_widget_new(gcrt);
+
+	g_byte_array_free(der, TRUE);
+	g_object_unref(G_OBJECT(gcrt));
+
+	return GTK_WIDGET(cert_widget);
+#else
+	str = purple_certificate_get_display_string(cert);
+	escaped = g_markup_escape_text(str, -1);
+
+	cert_label = gtk_label_new(NULL);
+	gtk_label_set_markup(GTK_LABEL(cert_label), escaped);
+	gtk_label_set_line_wrap(GTK_LABEL(cert_label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(cert_label), 0, 0);
+
+	g_free(str);
+	g_free(escaped);
+
+	return cert_label;
+#endif
+}
+
 static void *
 pidgin_request_fields(const char *title, const char *primary,
 						const char *secondary, PurpleRequestFields *fields,
@@ -1509,6 +1560,8 @@
 						widget = create_image_field(field);
 					else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 						widget = create_account_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_CERTIFICATE)
+						widget = create_certificate_field(field);
 					else
 						continue;
 				}