examples/test-lm.c
author Owen Taylor <otaylor@redhat.com>
Mon, 25 Feb 2008 23:13:58 +0100
changeset 343 35a19649d773
parent 167 7bcccfa734e2
child 482 08a9810b415b
permissions -rw-r--r--
Fixed reentrancy problem for async connections. Fixes first bit of LM-117. Patch adding reference to sockets in places where otherwise we can run into trouble where the user callback causes the socket to be freed. (Note that we actually double ref the socket in some cases ...both inside lm_socket_fail_with_error() and in the caller. This is, of course, harmless, and needed because the inner ref is necessary to handle the calls to _lm_socket_fail_with_error() from lm-proxy.c, the outer ref necessary because we reference socket after calling _lm_socket_fail_with_error()) committer: Mikael Hallendal <micke@imendio.com>

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2003-2006 Imendio 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
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include <string.h>
#include <stdlib.h>

#include <glib.h>
#include <loudmouth/loudmouth.h>

static GMainLoop *main_loop = NULL;
static gboolean   test_success = FALSE;

static gchar      expected_fingerprint[20];

static gchar     *server = NULL;
static gint       port = 5222;
static gchar     *username = NULL;
static gchar     *password = NULL;
static gchar     *resource = "test-lm";
static gchar     *fingerprint = NULL;

static GOptionEntry entries[] = 
{
  { "server", 's', 0, G_OPTION_ARG_STRING, &server, 
    "Server to connect to", NULL },
  { "port", 'P', 0, G_OPTION_ARG_INT, &port, 
    "Port to connect to [default=5222]", NULL },
  { "username", 'u', 0, G_OPTION_ARG_STRING, &username, 
    "Username to connect with (e.g. 'user' in user@server.org)", NULL },
  { "password", 'p', 0, G_OPTION_ARG_STRING, &password, 
    "Password to try", NULL },
  { "resource", 'r', 0, G_OPTION_ARG_STRING, &resource, 
    "Resource connect with [default=test-lm]", NULL },
  { "fingerprint", 'f', 0, G_OPTION_ARG_STRING, &fingerprint, 
    "SSL Fingerprint to use", NULL },
  { NULL }
};

static gchar *
get_part_name (const gchar *username)
{
	const gchar *ch;

	g_return_val_if_fail (username != NULL, NULL);

	ch = strchr (username, '@');
	if (!ch) {
		return NULL;
	}

	return g_strndup (username, ch - username);
}

static void
print_finger (const char   *fpr,
	      unsigned int  size)
{
	gint i;
	for (i = 0; i < size-1; i++) {
		g_printerr ("%02X:", fpr[i]);
	}
	
	g_printerr ("%02X", fpr[size-1]);
}

static LmSSLResponse
ssl_cb (LmSSL       *ssl, 
	LmSSLStatus  status, 
	gpointer     ud)
{
	g_print ("TestLM: SSL status:%d\n", status);

	switch (status) {
	case LM_SSL_STATUS_NO_CERT_FOUND:
		g_printerr ("TestLM: No certificate found!\n");
		break;
	case LM_SSL_STATUS_UNTRUSTED_CERT:
		g_printerr ("TestLM: Certificate is not trusted!\n"); 
		break;
	case LM_SSL_STATUS_CERT_EXPIRED:
		g_printerr ("TestLM: Certificate has expired!\n"); 
		break;
	case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
		g_printerr ("TestLM: Certificate has not been activated!\n"); 
		break;
	case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
		g_printerr ("TestLM: Certificate hostname does not match expected hostname!\n"); 
		break;
	case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
		const char *fpr = lm_ssl_get_fingerprint (ssl);
		g_printerr ("TestLM: Certificate fingerprint does not match expected fingerprint!\n"); 
		g_printerr ("TestLM: Remote fingerprint: ");
		print_finger (fpr, 16);

		g_printerr ("\n"
			    "TestLM: Expected fingerprint: ");
		print_finger (expected_fingerprint, 16);
		g_printerr ("\n");
		break;
	}
	case LM_SSL_STATUS_GENERIC_ERROR:
		g_printerr ("TestLM: Generic SSL error!\n"); 
		break;
	}

	return LM_SSL_RESPONSE_CONTINUE;
}

static void
connection_auth_cb (LmConnection *connection,
		    gboolean      success, 
		    gpointer      user_data)
{
	if (success) {
		LmMessage *m;
		
		test_success = TRUE;
		g_print ("TestLM: Authenticated successfully\n");

		m = lm_message_new_with_sub_type (NULL,
						  LM_MESSAGE_TYPE_PRESENCE,
						  LM_MESSAGE_SUB_TYPE_AVAILABLE);
		lm_connection_send (connection, m, NULL);
		g_print ("TestLM: Sent presence message:'%s'\n", 
			 lm_message_node_to_string (m->node));

		lm_message_unref (m);
	} else {
		g_printerr ("TestLM: Failed to authenticate\n");
		g_main_loop_quit (main_loop);
	}
}

static void
connection_open_cb (LmConnection *connection, 
		    gboolean      success,
		    gpointer      user_data)
{
	if (success) {
		gchar *user;

		user = get_part_name (username);
		lm_connection_authenticate (connection, user, 
					    password, resource,
					    connection_auth_cb, 
					    NULL, FALSE,  NULL);
		g_free (user);
		
		g_print ("TestLM: Sent authentication message\n");
	} else {
		g_printerr ("TestLM: Failed to connect\n");
		g_main_loop_quit (main_loop);
	}
}

static void
connection_close_cb (LmConnection       *connection, 
		     LmDisconnectReason  reason,
		     gpointer            user_data)
{
	const char *str;
	
	switch (reason) {
	case LM_DISCONNECT_REASON_OK:
		str = "LM_DISCONNECT_REASON_OK";
		break;
	case LM_DISCONNECT_REASON_PING_TIME_OUT:
		str = "LM_DISCONNECT_REASON_PING_TIME_OUT";
		break;
	case LM_DISCONNECT_REASON_HUP:
		str = "LM_DISCONNECT_REASON_HUP";
		break;
	case LM_DISCONNECT_REASON_ERROR:
		str = "LM_DISCONNECT_REASON_ERROR";
		break;
	case LM_DISCONNECT_REASON_UNKNOWN:
	default:
		str = "LM_DISCONNECT_REASON_UNKNOWN";
		break;
	}

	g_print ("TestLM: Disconnected, reason:%d->'%s'\n", reason, str);
}

static LmHandlerResult
handle_messages (LmMessageHandler *handler,
		 LmConnection     *connection,
		 LmMessage        *m,
		 gpointer          user_data)
{
	g_print ("TestLM: Incoming message from: %s\n",
		 lm_message_node_get_attribute (m->node, "from"));

	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}

int 
main (int argc, char **argv)
{
	GOptionContext   *context;
	LmConnection     *connection;
	LmMessageHandler *handler;
	gboolean          result;
	GError           *error = NULL;
	
	context = g_option_context_new ("- test Loudmouth");
	g_option_context_add_main_entries (context, entries, NULL);
	g_option_context_parse (context, &argc, &argv, NULL);
	g_option_context_free (context);
	
	if (!server || !username || !password) {
		g_printerr ("For usage, try %s --help\n", argv[0]);
		return EXIT_FAILURE;
	}

	if (fingerprint && !lm_ssl_is_supported ()) {
		g_printerr ("TestLM: SSL is not supported in this build\n");
		return EXIT_FAILURE;
	}

	if (username && strchr (username, '@') == NULL) {
		g_printerr ("TestLM: Username must have an '@' included\n");
		return EXIT_FAILURE;
	}

        connection = lm_connection_new (server);
	lm_connection_set_port (connection, port);
	lm_connection_set_jid (connection, username);

	handler = lm_message_handler_new (handle_messages, NULL, NULL);
	lm_connection_register_message_handler (connection, handler, 
						LM_MESSAGE_TYPE_MESSAGE, 
						LM_HANDLER_PRIORITY_NORMAL);
	
	lm_message_handler_unref (handler);
	
	lm_connection_set_disconnect_function (connection,
					       connection_close_cb,
					       NULL, NULL);

	if (fingerprint) {
		LmSSL *ssl;
		char  *p;
		int    i;
		
		if (port == LM_CONNECTION_DEFAULT_PORT) {
			lm_connection_set_port (connection,
						LM_CONNECTION_DEFAULT_PORT_SSL);
		}

		for (i = 0, p = fingerprint; *p && *(p+1); i++, p += 3) {
			expected_fingerprint[i] = (unsigned char) g_ascii_strtoull (p, NULL, 16);
		}
	
		ssl = lm_ssl_new (expected_fingerprint,
				  (LmSSLFunction) ssl_cb,
				  NULL, NULL);
	
		lm_connection_set_ssl (connection, ssl);
		lm_ssl_unref (ssl);
	}

	result = lm_connection_open (connection,
				     (LmResultFunction) connection_open_cb,
				     NULL, NULL, &error);

	if (!result) {
		g_printerr ("TestLM: Opening connection failed, error:%d->'%s'\n", 
			 error->code, error->message);
		g_free (error);
		return EXIT_FAILURE;
	}
	
	main_loop = g_main_loop_new (NULL, FALSE);
	g_main_loop_run (main_loop);

	return (test_success ? EXIT_SUCCESS : EXIT_FAILURE);
}