commit af1f213aac45325f89eeabce240833d65af17ec0
Author: Lukasz Slachciak <l.slachciak@samsung.com>
Date:   Fri Oct 15 14:05:59 2010 +0200

    Implementation of peers certificate list as SoupMessage property

diff --git a/libsoup/soup-gnutls.c b/libsoup/soup-gnutls.c
index 0b57f28..35da0ec 100644
--- a/libsoup/soup-gnutls.c
+++ b/libsoup/soup-gnutls.c
@@ -3,6 +3,7 @@
  * soup-gnutls.c
  *
  * Copyright (C) 2003-2006, Novell, Inc.
+ * Copyright (C) 2010 Samsung Electronics
  */
 
 #ifdef HAVE_CONFIG_H
@@ -589,6 +590,42 @@ soup_gnutls_init (void)
 }
 
 /**
+ * soup_ssl_cert_get_peers:
+ * Gett the array of certificates ecnoded in DER format
+ *
+ * @channel gnutls channel
+ * Return value: pointer to GValueArray containing certificates
+ **/
+
+const GValueArray
+*soup_ssl_cert_get_peers(GIOChannel *channel)
+{
+	SoupGNUTLSChannel *chan = (SoupGNUTLSChannel *) channel;
+	int i;
+	const gnutls_datum* cert_list;
+	GValueArray * value_array = NULL;
+	guint cert_list_size;
+	GValue gval = {0};
+	gpointer gp = NULL;
+
+	// get the server certificates list
+	cert_list = gnutls_certificate_get_peers(chan->session, &cert_list_size);
+	value_array = g_value_array_new(cert_list_size);
+
+	g_value_init(&gval, G_TYPE_POINTER);
+	for (i=0; i<cert_list_size; i++) {
+		gp = g_memdup(&cert_list[i],sizeof(gnutls_datum));
+		if (gp) {
+			g_value_set_pointer(&gval, gp);
+			g_value_array_append(value_array,&gval);
+		}
+	}
+	g_value_unset (&gval);
+
+	return value_array;
+}
+
+/**
  * soup_ssl_get_client_credentials:
  * @ca_file: path to a file containing X509-encoded Certificate
  * Authority certificates.
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index f4f4c96..4d02d7a 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -298,6 +298,8 @@ read_metadata (SoupMessage *msg, gboolean to_blank)
 	gsize nread;
 	gboolean got_lf;
 	GError *error = NULL;
+	guint index;
+	GValue* value = NULL;
 
 	while (1) {
 		status = soup_socket_read_until (io->sock, read_buf,
@@ -336,11 +338,26 @@ read_metadata (SoupMessage *msg, gboolean to_blank)
 
 	if (soup_socket_is_ssl (io->sock)) {
 		gboolean trusted_certificate;
+		GValueArray * certificates_array = NULL;
 
 		g_object_get (io->sock,
 			      SOUP_SOCKET_TRUSTED_CERTIFICATE, &trusted_certificate,
+			      SOUP_SOCKET_PEERS_CERTIFICATE, &certificates_array,
 			      NULL);
 
+		if (msg->peers_certificate != certificates_array) {
+			if (msg->peers_certificate) {
+				for (index = 0; index < msg->peers_certificate->n_values; index++ ) {
+					// we need to release memory allocated for each certificate
+					value = g_value_array_get_nth(msg->peers_certificate, index);
+					if (value)
+						g_free(g_value_get_pointer(value));
+				}
+				g_value_array_free(msg->peers_certificate);
+			}
+			msg->peers_certificate = certificates_array;
+		}
+
 		if (trusted_certificate)
 			soup_message_set_flags (msg, priv->msg_flags | SOUP_MESSAGE_CERTIFICATE_TRUSTED);
 	}
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index cd136a3..638960c 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -124,6 +124,7 @@ enum {
 	PROP_REQUEST_HEADERS,
 	PROP_RESPONSE_BODY,
 	PROP_RESPONSE_HEADERS,
+	PROP_PEERS_CERTIFICATE,
 
 	LAST_PROP
 };
@@ -146,6 +147,7 @@ soup_message_init (SoupMessage *msg)
 	msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
 	msg->response_body = soup_message_body_new ();
 	msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+	msg->peers_certificate = NULL;
 }
 
 static void
@@ -153,6 +155,8 @@ finalize (GObject *object)
 {
 	SoupMessage *msg = SOUP_MESSAGE (object);
 	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	gint index;
+	GValue* value;
 
 	soup_message_io_cleanup (msg);
 	if (priv->chunk_allocator_dnotify)
@@ -180,6 +184,15 @@ finalize (GObject *object)
 	soup_message_body_free (msg->request_body);
 	soup_message_headers_free (msg->request_headers);
 	soup_message_body_free (msg->response_body);
+	if (msg->peers_certificate) {
+		for (index = 0; index < msg->peers_certificate->n_values; index++ ) {
+			// we need to release memory allocated for each certificate
+			value = g_value_array_get_nth(msg->peers_certificate, index);
+			if (value)
+				g_free(g_value_get_pointer(value));
+		}
+		g_value_array_free(msg->peers_certificate);
+	}
 	soup_message_headers_free (msg->response_headers);
 
 	g_free (msg->reason_phrase);
@@ -644,6 +657,14 @@ soup_message_class_init (SoupMessageClass *message_class)
 				     "The HTTP response headers",
 				    SOUP_TYPE_MESSAGE_HEADERS,
 				    G_PARAM_READABLE));
+
+	g_object_class_install_property (
+			object_class, PROP_PEERS_CERTIFICATE,
+			g_param_spec_value_array (SOUP_MESSAGE_PEERS_CERTIFICATE,
+				"Peers Certificate",
+				"Array of certificates",
+				NULL,
+				G_PARAM_READABLE));
 }
 
 static void
@@ -733,6 +754,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_RESPONSE_HEADERS:
 		g_value_set_boxed (value, msg->response_headers);
 		break;
+	case PROP_PEERS_CERTIFICATE:
+		g_value_set_boxed (value, msg->peers_certificate);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 4f2d66d..1681a20 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -34,6 +34,7 @@ struct _SoupMessage {
 
 	SoupMessageBody    *response_body;
 	SoupMessageHeaders *response_headers;
+	GValueArray  *peers_certificate;
 };
 
 typedef struct {
@@ -60,18 +61,19 @@ typedef struct {
 
 GType soup_message_get_type (void);
 
-#define SOUP_MESSAGE_METHOD           "method"
-#define SOUP_MESSAGE_URI              "uri"
-#define SOUP_MESSAGE_HTTP_VERSION     "http-version"
-#define SOUP_MESSAGE_FLAGS            "flags"
-#define SOUP_MESSAGE_SERVER_SIDE      "server-side"
-#define SOUP_MESSAGE_STATUS_CODE      "status-code"
-#define SOUP_MESSAGE_REASON_PHRASE    "reason-phrase"
-#define SOUP_MESSAGE_FIRST_PARTY      "first-party"
-#define SOUP_MESSAGE_REQUEST_BODY     "request-body"
-#define SOUP_MESSAGE_REQUEST_HEADERS  "request-headers"
-#define SOUP_MESSAGE_RESPONSE_BODY    "response-body"
-#define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
+#define SOUP_MESSAGE_METHOD            "method"
+#define SOUP_MESSAGE_URI               "uri"
+#define SOUP_MESSAGE_HTTP_VERSION      "http-version"
+#define SOUP_MESSAGE_FLAGS             "flags"
+#define SOUP_MESSAGE_SERVER_SIDE       "server-side"
+#define SOUP_MESSAGE_STATUS_CODE       "status-code"
+#define SOUP_MESSAGE_REASON_PHRASE     "reason-phrase"
+#define SOUP_MESSAGE_FIRST_PARTY       "first-party"
+#define SOUP_MESSAGE_REQUEST_BODY      "request-body"
+#define SOUP_MESSAGE_REQUEST_HEADERS   "request-headers"
+#define SOUP_MESSAGE_RESPONSE_BODY     "response-body"
+#define SOUP_MESSAGE_RESPONSE_HEADERS  "response-headers"
+#define SOUP_MESSAGE_PEERS_CERTIFICATE "peers-certificate"
 
 SoupMessage   *soup_message_new                 (const char        *method,
 						 const char        *uri_string);
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index c9c2b92..2267af5 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -3,6 +3,7 @@
  * soup-socket.c: Socket networking code.
  *
  * Copyright (C) 2000-2003, Ximian, Inc.
+ * Coypright (C) 2010, Samsung Electronics
  */
 
 #ifdef HAVE_CONFIG_H
@@ -60,6 +61,7 @@ enum {
 	PROP_TIMEOUT,
 	PROP_TRUSTED_CERTIFICATE,
 	PROP_CLEAN_DISPOSE,
+	PROP_PEERS_CERTIFICATE,
 
 	LAST_PROP
 };
@@ -387,6 +389,22 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 				     FALSE,
 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 	/**
+	 * SOUP_SOCKET_PEERS_CERTIFICATE:
+	 *
+	 * Alias for the #SoupSocket:peers-certificate
+	 * property. Notice that this property's value is only useful
+	 * if the socket is for an SSL connection, and only reliable
+	 * after some data has been transferred to or from it.
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_PEERS_CERTIFICATE,
+		g_param_spec_value_array (SOUP_SOCKET_PEERS_CERTIFICATE,
+				     "Peers Certificate",
+				     "Array of certificates",
+				     NULL,
+				     G_PARAM_READABLE));
+
+	/**
 	 * SOUP_SOCKET_ASYNC_CONTEXT:
 	 *
 	 * Alias for the #SoupSocket:async-context property. (The
@@ -581,6 +599,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SSL_STRICT:
 		g_value_set_boolean (value, priv->ssl_strict);
 		break;
+	case PROP_PEERS_CERTIFICATE:
+		g_value_set_boxed (value, soup_ssl_cert_get_peers(priv->iochannel));
+		break;
 	case PROP_TRUSTED_CERTIFICATE:
 		g_value_set_boolean (value, priv->trusted_certificate);
 		break;
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index 2e039d9..e9fe23f 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -47,6 +47,7 @@ typedef struct {
 #define SOUP_SOCKET_SSL_CREDENTIALS     "ssl-creds"
 #define SOUP_SOCKET_SSL_STRICT          "ssl-strict"
 #define SOUP_SOCKET_TRUSTED_CERTIFICATE "trusted-certificate"
+#define SOUP_SOCKET_PEERS_CERTIFICATE	"peers-certificate"
 #define SOUP_SOCKET_ASYNC_CONTEXT       "async-context"
 #define SOUP_SOCKET_TIMEOUT             "timeout"
 
diff --git a/libsoup/soup-ssl.h b/libsoup/soup-ssl.h
index f4e3eab..dc5d843 100644
--- a/libsoup/soup-ssl.h
+++ b/libsoup/soup-ssl.h
@@ -7,6 +7,7 @@
 #define SOUP_SSL_H 1
 
 #include <glib.h>
+#include <glib-object.h>
 
 typedef enum {
 	SOUP_SSL_TYPE_CLIENT = 0,
@@ -28,4 +29,6 @@ GIOChannel         *soup_ssl_wrap_iochannel          (GIOChannel         *sock,
 						      const char         *remote_host,
 						      SoupSSLCredentials *creds);
 
+const GValueArray *soup_ssl_cert_get_peers(GIOChannel *channel);
+
 #endif /* SOUP_SSL_H */
diff --git a/tests/get.c b/tests/get.c
index 79d6e80..c63f0fc 100644
--- a/tests/get.c
+++ b/tests/get.c
@@ -1,6 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2001-2003, Ximian, Inc.
+ * Copyright (C) 20010, Samsung Electronics
  */
 
 #ifdef HAVE_CONFIG_H
@@ -16,6 +17,9 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
 #ifdef HAVE_GNOME
 #include <libsoup/soup-gnome.h>
 #else
@@ -27,6 +31,46 @@ static GMainLoop *loop;
 static gboolean debug = FALSE, quiet = FALSE;
 static const char *method;
 
+
+static void
+print_certificates (GValueArray* certificates_array, const char* uri)
+{
+
+	guint index;
+	GValue *value = NULL;
+	gpointer certificate = NULL;
+
+	if (certificates_array) {
+		printf("\nCertificates for %s\n", uri);
+		for (index=0; index<certificates_array->n_values; index++) {
+			value = g_value_array_get_nth(certificates_array, index);
+			if (value) {
+				certificate = g_value_get_pointer(value);
+				if (certificate) {
+					gnutls_x509_crt_t cert;
+					if (gnutls_x509_crt_init (&cert)>= 0)
+						if (gnutls_x509_crt_import(cert, certificate, GNUTLS_X509_FMT_DER) >= 0)
+						{
+							size_t len =0;
+							char *buf;
+							gnutls_x509_crt_get_dn(cert, NULL, &len);
+							buf = malloc(len + 1);
+							if (buf) {
+								buf[0] = buf[len] = '\0';
+								gnutls_x509_crt_get_dn(cert, buf, &len);
+							}
+							printf("%s\n", buf);
+						}
+				}
+				else
+					g_warning("gvalue is not containing certificate!");
+			}
+			else g_warning("certificate is empty!");
+
+		}
+	}
+}
+
 static void
 get_url (const char *url)
 {
@@ -83,6 +127,15 @@ get_url (const char *url)
 	} else if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
 		fwrite (msg->response_body->data, 1,
 			msg->response_body->length, stdout);
+
+		if (debug) {
+			GValueArray * certificates_array = NULL;
+			g_object_get (msg,
+				SOUP_MESSAGE_PEERS_CERTIFICATE, &certificates_array,
+				NULL);
+			print_certificates(certificates_array, soup_uri_to_string(soup_message_get_uri (msg), FALSE));
+
+		}
 	}
 }
 
