2003-12-15 Mikael Hallendal <micke@imendio.com>
authorhallski <hallski>
Mon, 15 Dec 2003 16:21:28 +0000
changeset 57 6b168a8917f7
parent 56 3bf928955fc5
child 58 fd8010f00952
2003-12-15 Mikael Hallendal <micke@imendio.com> * loudmouth/lm-connection.c: - Fixed issue with the SSL parts. Patch from Colin Walters
ChangeLog
docs/reference/tmpl/lm-connection.sgml
docs/reference/tmpl/loudmouth-unused.sgml
loudmouth/lm-connection.c
loudmouth/lm-connection.h
loudmouth/lm-debug.c
loudmouth/lm-debug.h
loudmouth/lm-error.c
loudmouth/lm-error.h
loudmouth/lm-internals.h
loudmouth/lm-message-handler.c
loudmouth/lm-message-handler.h
loudmouth/lm-message-node.c
loudmouth/lm-message-node.h
loudmouth/lm-message.c
loudmouth/lm-message.h
loudmouth/lm-parser.c
loudmouth/lm-parser.h
loudmouth/lm-sha.h
loudmouth/lm-utils.c
loudmouth/lm-utils.h
loudmouth/loudmouth.h
loudmouth/test-lm.c
--- a/ChangeLog	Thu Nov 27 21:12:51 2003 +0000
+++ b/ChangeLog	Mon Dec 15 16:21:28 2003 +0000
@@ -1,3 +1,8 @@
+2003-12-15  Mikael Hallendal  <micke@imendio.com>
+
+	* loudmouth/lm-connection.c:
+	- Fixed issue with the SSL parts. Patch from Colin Walters
+
 2003-11-20  Ross Burton  <ross@burtonini.com>
 
 	* loudmouth/lm-message-node.h:
--- a/docs/reference/tmpl/lm-connection.sgml	Thu Nov 27 21:12:51 2003 +0000
+++ b/docs/reference/tmpl/lm-connection.sgml	Mon Dec 15 16:21:28 2003 +0000
@@ -232,15 +232,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION lm_connection_set_use_ssl ##### -->
-<para>
-
-</para>
-
-@connection: 
-@use_ssl: 
-
-
 <!-- ##### FUNCTION lm_connection_send ##### -->
 <para>
 
--- a/docs/reference/tmpl/loudmouth-unused.sgml	Thu Nov 27 21:12:51 2003 +0000
+++ b/docs/reference/tmpl/loudmouth-unused.sgml	Mon Dec 15 16:21:28 2003 +0000
@@ -27,6 +27,14 @@
 @value: 
 @user_data: 
 
+<!-- ##### FUNCTION lm_connection_set_use_ssl ##### -->
+<para>
+
+</para>
+
+@connection: 
+@use_ssl: 
+
 <!-- ##### FUNCTION lm_message_node_add_child_message_node ##### -->
 <para>
 
--- a/loudmouth/lm-connection.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-connection.c	Mon Dec 15 16:21:28 2003 +0000
@@ -2,7 +2,6 @@
 /*
  * Copyright (C) 2003 Imendio HB
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -63,6 +62,7 @@
 	gchar          *server;
 	guint           port;
 	gboolean        use_ssl;
+	char	        fingerprint[20];
 
 #ifdef HAVE_GNUTLS
 	gnutls_session  gnutls_session;
@@ -112,10 +112,13 @@
 static void     connection_new_message_cb    (LmParser             *parser,
 					      LmMessage            *message,
 					      LmConnection         *connection);
-static gboolean connection_do_open         (LmConnection     *connection,
-					      GError          **error);
+static gboolean connection_do_open           (LmConnection         *connection,
+					      const gchar          *fingerprint,
+					      LmSSLFunction         ssl_func,
+					      gpointer              user_data,
+					      GError              **error);
 
-static void     connection_do_close           (LmConnection          *connection);
+static void     connection_do_close           (LmConnection        *connection);
 static gboolean connection_in_event          (GIOChannel   *source,
 					      GIOCondition  condition,
 					      LmConnection *connection);
@@ -254,8 +257,94 @@
 	lm_queue_push_tail (connection->incoming_messages, m);
 }
 
+#ifdef HAVE_GNUTLS
 static gboolean
-connection_do_open (LmConnection *connection, GError **error)
+connection_verify_certificate (LmConnection  *connection,
+			       const gchar   *expected_fingerprint,
+			       LmSSLFunction  ssl_function,
+			       gpointer       user_data)
+{
+	int status;
+
+	/* This verification function uses the trusted CAs in the credentials
+	 * structure. So you must have installed one or more CA certificates.
+	 */
+	status = gnutls_certificate_verify_peers (connection->gnutls_session);
+
+	if (status == GNUTLS_E_NO_CERTIFICATE_FOUND)
+		if (ssl_function (connection,
+				   LM_SSL_STATUS_NO_CERT_FOUND,
+				   user_data) != LM_SSL_RESPONSE_CONTINUE)
+			return FALSE;
+
+	if (status & GNUTLS_CERT_INVALID
+	    || status & GNUTLS_CERT_NOT_TRUSTED
+	    || status & GNUTLS_CERT_CORRUPTED
+	    || status & GNUTLS_CERT_REVOKED)
+		if (ssl_function (connection,
+				   LM_SSL_STATUS_UNTRUSTED_CERT,
+				   user_data) != LM_SSL_RESPONSE_CONTINUE)
+			return FALSE;
+
+	if (gnutls_certificate_expiration_time_peers (connection->gnutls_session) < time (0)) {
+		if (ssl_function (connection,
+				   LM_SSL_STATUS_CERT_EXPIRED,
+				   user_data) != LM_SSL_RESPONSE_CONTINUE)
+			return FALSE;
+	}
+	
+	if (gnutls_certificate_activation_time_peers (connection->gnutls_session) > time (0)) {
+		if (ssl_function (connection,
+				   LM_SSL_STATUS_CERT_NOT_ACTIVATED,
+				   user_data) != LM_SSL_RESPONSE_CONTINUE)
+			return FALSE;
+	}
+	
+	if (gnutls_certificate_type_get (connection->gnutls_session) == GNUTLS_CRT_X509) {
+		const gnutls_datum* cert_list;
+		int cert_list_size;
+		int digest_size;
+		
+		cert_list = gnutls_certificate_get_peers (connection->gnutls_session, &cert_list_size);
+		if (cert_list == NULL) {
+			if (ssl_function (connection,
+					   LM_SSL_STATUS_NO_CERT_FOUND,
+					   user_data) != LM_SSL_RESPONSE_CONTINUE)
+				return FALSE;
+		}
+		if (!gnutls_x509_check_certificates_hostname (&cert_list[0],
+							      connection->server)) {
+			if (ssl_function (connection,
+					   LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
+					   user_data) != LM_SSL_RESPONSE_CONTINUE)
+				return FALSE;
+		}
+		if (gnutls_x509_fingerprint (GNUTLS_DIG_MD5, &cert_list[0],
+					     connection->fingerprint,
+					     &digest_size) >= 0) {
+			if (expected_fingerprint &&
+			    memcmp (expected_fingerprint, connection->fingerprint,
+				    digest_size) &&
+			    ssl_function (connection,
+					   LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
+					   user_data) != LM_SSL_RESPONSE_CONTINUE)
+				return FALSE;
+		} else if (ssl_function (connection,
+					  LM_SSL_STATUS_GENERIC_ERROR,
+					  user_data) != LM_SSL_RESPONSE_CONTINUE)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+#endif
+
+static gboolean
+connection_do_open (LmConnection    *connection,
+		    const gchar     *fingerprint,
+		    LmSSLFunction    ssl_function, 
+		    gpointer         user_data,
+		    GError         **error)
 {
 	gint             fd = -1;
 	int              err = -1;
@@ -333,6 +422,7 @@
 #ifdef HAVE_GNUTLS
 	if (connection->use_ssl) {
 		int ret;
+		gboolean auth_ok = TRUE;
 		const int cert_type_priority[2] =
 		{ GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP };
 
@@ -348,14 +438,32 @@
 					  (gnutls_transport_ptr) fd);
 
 		ret = gnutls_handshake (connection->gnutls_session);
+
+		if (ret >= 0) {
+			auth_ok = connection_verify_certificate (connection,
+								 fingerprint,
+								 ssl_function,
+								 user_data);
+		}
 		
-		if (ret < 0) {
+		if (ret < 0 || !auth_ok) {
+			char *errmsg;
+			
 			gnutls_perror (ret);
 			shutdown (fd, SHUT_RDWR);
 			close (fd);
 			connection_do_close (connection);
-			g_set_error (error, LM_ERROR, LM_ERROR_CONNECTION_OPEN,
-				     "*** GNUTLS handshake failed");
+			
+			if (!auth_ok) {
+				errmsg = "*** GNUTLS authentication error";
+			} else {
+				errmsg = "*** GNUTLS handshake failed";
+			}
+			
+			g_set_error (error, 
+				     LM_ERROR, LM_ERROR_CONNECTION_OPEN,
+				     errmsg);			
+			
 			return FALSE;
 		}
 	}
@@ -878,6 +986,7 @@
 	
 	connection->port              = LM_CONNECTION_DEFAULT_PORT;
 	connection->use_ssl           = FALSE;
+	connection->fingerprint[0]    = '\0';
 	connection->disconnect_cb     = NULL;
 	connection->incoming_messages = lm_queue_new ();
 	
@@ -900,8 +1009,10 @@
 }
 
 /**
- * lm_connection_open:
- * @connection: #LmConnection to open
+ * lm_connection_open_ssl:
+ * @connection: #LmConnection to open, using SSL
+ * @fingerprint: the expected fingerprint of the remote cert, or %NULL 
+ * @ssl_function: Callback function used when an authentication error occurs.
  * @function: Callback function that will be called when the connection is open.
  * @user_data: User data that will be passed to @function.
  * @notify: Function for freeing that user_data, can be NULL.
@@ -912,16 +1023,20 @@
  * Return value: #TRUE if everything went fine, otherwise #FALSE.
  **/
 gboolean
-lm_connection_open (LmConnection      *connection, 
-		    LmResultFunction   function,
-		    gpointer           user_data,
-		    GDestroyNotify     notify,
-		    GError           **error)
+lm_connection_open_ssl (LmConnection      *connection, 
+			const gchar	  *fingerprint,
+			LmSSLFunction ssl_function,
+			LmResultFunction   function,
+			gpointer           user_data,
+			GDestroyNotify     notify,
+			GError           **error)
 {
 	LmMessage *m;
 	gboolean   result;
 	
 	g_return_val_if_fail (connection != NULL, FALSE);
+
+	connection->use_ssl = ssl_function != NULL;
 	
 	if (lm_connection_is_open (connection)) {
 		g_set_error (error,
@@ -946,7 +1061,7 @@
 	lm_verbose ("Connecting to: %s:%d\n", 
 		    connection->server, connection->port);
 	
-	if (!connection_do_open (connection, error)) {
+	if (!connection_do_open (connection, fingerprint, ssl_function, user_data, error)) {
 		return FALSE;
 	}
 	
@@ -965,8 +1080,34 @@
 }
 
 /**
- * lm_connection_open_and_block:
- * @connection: an #LmConnection
+ * lm_connection_open:
+ * @connection: #LmConnection to open
+ * @function: Callback function that will be called when the connection is open.
+ * @user_data: User data that will be passed to @function.
+ * @notify: Function for freeing that user_data, can be NULL.
+ * @error: location to store error, or %NULL
+ * 
+ * An async call to open @connection. When the connection is open @function will be called.
+ * 
+ * Return value: #TRUE if everything went fine, otherwise #FALSE.
+ **/
+gboolean
+lm_connection_open (LmConnection      *connection, 
+		    LmResultFunction   function,
+		    gpointer           user_data,
+		    GDestroyNotify     notify,
+		    GError           **error)
+{
+	return lm_connection_open_ssl (connection, NULL, NULL,
+				       function, user_data, notify, error);
+}
+
+/**
+ * lm_connection_open_and_block_ssl:
+ * @connection: an #LmConnection to open using SSL
+ * @fingerprint: the expected fingerprint of the remote cert, or %NULL
+ * @ssl_function: Callback function used when a SSL error occurs.
+ * @user_data: User data that will be passed to @function.
  * @error: location to store error, or %NULL
  * 
  * Opens @connection and waits until the stream is setup. 
@@ -974,7 +1115,11 @@
  * Return value: #TRUE if no errors where encountered during opening and stream setup successfully, #FALSE otherwise.
  **/
 gboolean
-lm_connection_open_and_block (LmConnection *connection, GError **error)
+lm_connection_open_and_block_ssl (LmConnection *connection,
+				  const gchar *fingerprint,
+				  LmSSLFunction ssl_function,
+				  gpointer user_data,
+				  GError **error)
 {
 	LmMessage *m;
 	gboolean   result;
@@ -982,6 +1127,8 @@
 	gboolean   ret_val = FALSE;
 
 	g_return_val_if_fail (connection != NULL, FALSE);
+
+	connection->use_ssl = ssl_function != NULL;
 	
 	if (lm_connection_is_open (connection)) {
 		g_set_error (error,
@@ -1001,7 +1148,8 @@
 	lm_verbose ("(Block)Connecting to: %s:%d\n", 
 		    connection->server, connection->port);
 	
-	if (!connection_do_open (connection, error)) {
+	if (!connection_do_open (connection, fingerprint, ssl_function,
+				 user_data, error)) {
 		return FALSE;
 	}
 	
@@ -1051,6 +1199,22 @@
 }
 
 /**
+ * lm_connection_open_and_block:
+ * @connection: an #LmConnection to open
+ * @error: location to store error, or %NULL
+ * 
+ * Opens @connection and waits until the stream is setup. 
+ * 
+ * Return value: #TRUE if no errors where encountered during opening and stream setup successfully, #FALSE otherwise.
+ **/
+gboolean
+lm_connection_open_and_block (LmConnection *connection,
+			      GError **error)
+{
+	return lm_connection_open_and_block_ssl (connection, NULL, NULL, NULL, error);
+}
+
+/**
  * lm_connection_close:
  * @connection: #LmConnection to close 
  * @error: location to store error, or %NULL
@@ -1360,21 +1524,17 @@
 }
 
 /**
- * lm_connection_set_use_ssl:
+ * lm_connection_get_fingerprint: 
  * @connection: an #LmConnection
- * @use_ssl: whether to use SSL or not.
+ *
+ * Returns the MD5 fingerprint of the remote server's certificate.
  * 
- * Sets whether @connection should use SSL for encrypting traffic to/from the server.
+ * Return value: A 16-byte array representing the fingerprint or %NULL if unknown.
  **/
-void
-lm_connection_set_use_ssl (LmConnection *connection, gboolean use_ssl)
+const unsigned char *
+lm_connection_get_fingerprint (LmConnection *connection)
 {
-	if (lm_connection_is_open (connection)) {
-		g_warning ("use_ssl can't be changed while connected");
-		return;
-	}
-
-	connection->use_ssl = use_ssl;
+	return (unsigned char*) connection->fingerprint;
 }
 
 /**
--- a/loudmouth/lm-connection.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-connection.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -56,13 +55,38 @@
 	LM_DISCONNECT_REASON_UNKNOWN
 } LmDisconnectReason;
 
-typedef void (* LmResultFunction)     (LmConnection *connection,
-				       gboolean      success,
-				       gpointer      user_data);
+typedef enum {
+	LM_CERT_INVALID,
+	LM_CERT_ISSUER_NOT_FOUND,
+	LM_CERT_REVOKED,
+} LmCertificateStatus;
+
+typedef enum {
+	LM_SSL_STATUS_NO_CERT_FOUND,	
+	LM_SSL_STATUS_UNTRUSTED_CERT,
+	LM_SSL_STATUS_CERT_EXPIRED,
+	LM_SSL_STATUS_CERT_NOT_ACTIVATED,
+	LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,			
+	LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,			
+	LM_SSL_STATUS_GENERIC_ERROR,	
+} LmSSLStatus;
 
-typedef void (* LmDisconnectFunction) (LmConnection *connection,
-				       LmDisconnectReason reason,
-				       gpointer           user_data);
+typedef enum {
+	LM_SSL_RESPONSE_CONTINUE,
+	LM_SSL_RESPONSE_STOP,
+} LmSSLResponse;
+
+typedef void          (* LmResultFunction)     (LmConnection       *connection,
+						gboolean            success,
+						gpointer            user_data);
+
+typedef void          (* LmDisconnectFunction) (LmConnection       *connection,
+						LmDisconnectReason  reason,
+						gpointer            user_data);
+
+typedef LmSSLResponse (* LmSSLFunction)        (LmConnection *connection,
+						LmSSLStatus   status,
+						gpointer      user_data);
 
 
 LmConnection *lm_connection_new               (const gchar        *server);
@@ -71,9 +95,23 @@
 					       gpointer            user_data,
 					       GDestroyNotify      notify,
 					       GError            **error);
+gboolean      lm_connection_open_ssl          (LmConnection       *connection,
+					       const gchar        *fingerprint,
+					       LmSSLFunction       ssl_function,
+					       LmResultFunction    function,
+					       gpointer            user_data,
+					       GDestroyNotify      notify,
+					       GError            **error);
+
 gboolean      lm_connection_open_and_block    (LmConnection       *connection,
 					       GError            **error);
 
+gboolean      lm_connection_open_and_block_ssl (LmConnection      *connection,
+						const gchar       *fingerprint,
+						LmSSLFunction      ssl_function,
+						gpointer           user_data,
+						GError           **error);
+
 gboolean      lm_connection_close             (LmConnection       *connection,
 					       GError            **error);
 gboolean      lm_connection_authenticate      (LmConnection       *connection,
@@ -102,8 +140,9 @@
 					       guint               port);
 gboolean      lm_connection_supports_ssl      (void);
 gboolean      lm_connection_get_use_ssl       (LmConnection       *connection);
-void          lm_connection_set_use_ssl       (LmConnection       *connection,
-					       gboolean            use_ssl);
+
+const unsigned char * 
+lm_connection_get_fingerprint                 (LmConnection       *connection);
 					       
 gboolean      lm_connection_send              (LmConnection       *connection,
 					       LmMessage          *message,
--- a/loudmouth/lm-debug.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-debug.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-debug.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-debug.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-error.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-error.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-error.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-error.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-internals.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-internals.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message-handler.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message-handler.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message-handler.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message-handler.h	Mon Dec 15 16:21:28 2003 +0000
@@ -2,7 +2,6 @@
 /*
  * Copyright (C) 2003 Imendio HB
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message-node.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message-node.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message-node.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message-node.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-message.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-message.h	Mon Dec 15 16:21:28 2003 +0000
@@ -2,7 +2,6 @@
 /*
  * Copyright (C) 2003 Imendio HB
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-parser.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-parser.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-parser.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-parser.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-sha.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-sha.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-utils.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-utils.c	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/lm-utils.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/lm-utils.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/loudmouth.h	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/loudmouth.h	Mon Dec 15 16:21:28 2003 +0000
@@ -1,7 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
--- a/loudmouth/test-lm.c	Thu Nov 27 21:12:51 2003 +0000
+++ b/loudmouth/test-lm.c	Mon Dec 15 16:21:28 2003 +0000
@@ -2,7 +2,6 @@
 /*
  * Copyright (C) 2003 Imendio HB
  * Copyright (C) 2003 Mikael Hallendal <micke@imendio.com>
- * Copyright (C) 2003 CodeFactory AB. 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -24,19 +23,67 @@
 
 #include <glib.h>
 #include <string.h>
+#include <stdlib.h>
 #include <loudmouth/loudmouth.h>
 #ifdef __WIN32__
 #include <winsock2.h>
 #endif
 #include "lm-internals.h"
 
-#define USAGE "Usage: test-lm <server> <username> <password>\n"
-
 typedef struct {
 	gchar *name;
 	gchar *passwd;
 } UserInfo;
 
+static unsigned char expected_fingerprint[20];
+
+static void
+print_finger (const unsigned char *fpr, unsigned int size)
+{
+	gint i;
+	for (i = 0; i < size-1; i++)
+		g_print ("%02X:", (unsigned char) fpr[i]);
+	g_print ("%02X", (unsigned char) fpr[size-1]);
+}
+
+static LmSSLResponse
+ssl_cb (LmConnection *connection, LmSSLStatus status, gpointer ud)
+{
+	g_print ("SSL status: %d\n", status);
+	switch (status) {
+	case LM_SSL_STATUS_NO_CERT_FOUND:
+		g_print ("No certificate found!\n");
+		break;
+	case LM_SSL_STATUS_UNTRUSTED_CERT:
+		g_print ("Certificate is not trusted!\n"); 
+		break;
+	case LM_SSL_STATUS_CERT_EXPIRED:
+		g_print ("Certificate has expired!\n"); 
+		break;
+	case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
+		g_print ("Certificate has not been activated!\n"); 
+		break;
+	case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
+		g_print ("Certificate hostname does not match expected hostname!\n"); 
+		break;
+	case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
+		const unsigned char *fpr = lm_connection_get_fingerprint (connection);
+		g_print ("Certificate fingerprint does not match expected fingerprint!\n"); 
+		g_print ("Remote fingerprint: ");
+		print_finger (fpr, 16);
+		g_print ("\nExpected fingerprint: ");
+		print_finger (expected_fingerprint, 16);
+		g_print ("\n");
+		break;
+	}
+	case LM_SSL_STATUS_GENERIC_ERROR:
+		g_print ("Generic SSL error!\n"); 
+		break;
+	}
+
+	return LM_SSL_RESPONSE_CONTINUE;
+}
+
 static void
 authentication_cb (LmConnection *connection, gboolean result, gpointer ud)
 {
@@ -92,7 +139,8 @@
 #endif
 	
 	if (argc < 4) {
-		g_print (USAGE);
+		g_print ("Usage: test-lm <server> <username> <password>\n"
+			 "       test-lm <server> <username> <password> <fingerprint>\n");
 		return 1;
 	}
 
@@ -124,12 +172,11 @@
 
         connection = lm_connection_new (argv[1]);
 
-	/*
-	if (lm_connection_supports_ssl ()) {
-		lm_connection_set_port (connection, 5223);
-		lm_connection_set_use_ssl (connection, TRUE);
+	if (argc > 4 && !lm_connection_supports_ssl ()) {
+		g_error ("No SSL support!");
+		exit (1);
 	}
-	*/
+
 
 	handler = lm_message_handler_new (handle_messages, NULL, NULL);
 	lm_connection_register_message_handler (connection, handler, 
@@ -142,9 +189,25 @@
 	info->name = g_strdup (argv[2]);
 	info->passwd = g_strdup (argv[3]);
 	
-	result = lm_connection_open (connection,
-				     (LmResultFunction) connection_open_cb,
-				     info, NULL, NULL);
+	if (argc > 4) {
+		int i;
+		char *p;
+		lm_connection_set_port (connection,
+					LM_CONNECTION_DEFAULT_PORT_SSL);
+		
+		for (i = 0, p = argv[4]; *p && *(p+1); i++, p += 3)
+			expected_fingerprint[i] = (unsigned char) g_ascii_strtoull (p, NULL, 16);
+		
+		result = lm_connection_open_ssl (connection,
+						 expected_fingerprint,
+						 (LmSSLFunction) ssl_cb,
+						 (LmResultFunction) connection_open_cb,
+						 info, NULL, NULL);
+	} else {
+		result = lm_connection_open (connection,
+					     (LmResultFunction) connection_open_cb,
+					     info, NULL, NULL);
+	}
 
 	if (!result) {
 		g_print ("Opening connection failed: %d\n", result);