/*
*  dns_lookup.c
*
*  deals with the dns lookup of servers stuff
*
*
*  (c) 2001 Tim-Philipp Muller <t.i.m@orange.net>
*/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <gnet.h>

#include "global.h"

#include "core-conn.h"
#include "misc.h"
#include "status_page.h"
#include "statusbar.h"
#include "options.h"


#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include <gtk/gtkmain.h>

/* local variables */

#define ONE_LOOKUP_DONE          1
#define ONE_LOOKUP_DONE_FAILURE  2
static gint          dns_lookup_single_lookup_status = -1;


/* is TRUE while we do lookup */
static gboolean      dns_lookup_in_progress = FALSE;


/* contains pointers to GURL* structures */
static GPtrArray    *dns_lookup_list = NULL;


/* contains pointers to GInetAddr* after lookup */
static GPtrArray    *dns_server_list = NULL; 



/* functions */

static void          dns_lookup_free_lookuplist (void);

static void          dns_lookup_free_serverlist (void);

static void          dns_lookup_callback ( GInetAddr *inetaddr,
                                           gpointer data );


// misc_gnet_inetaddr_get_ip
//
// Gets the IP Address [XXX - in which order] from a GInetAddr

static guint32
misc_gnet_inetaddr_get_ip (const GInetAddr *ia)
{
	gchar	*name;
	guint32	n1,n2,n3,n4;
	g_return_val_if_fail (ia!=NULL,0);
	name = gnet_inetaddr_get_canonical_name (ia);
	g_return_val_if_fail (name!=NULL,0);
	g_return_val_if_fail (sscanf(name, "%u.%u.%u.%u",&n1,&n2,&n3,&n4)==4,0);
	g_return_val_if_fail ((n1<256 && n2<256 && n3<256 && n4<256), 0);
	// XXX - shifts are byte order independent, aren't they?
	if (G_BYTE_ORDER!=G_LITTLE_ENDIAN)
		g_print ("CHECK ME: %s\n", __FUNCTION__);

	g_free(name);
	return (n1 | (n2<<8) | (n3<<16) | (n4<<24));
}


/******************************************************************************
 *
 *  dns_lookup_free_lookuplist
 *
 *  Frees all the memory taken up by the dns_lookup_servers
 *   GPtrArray and its records
 *
 ***/

static void
dns_lookup_free_lookuplist (void)
{
	guint i;

	if (!dns_lookup_list)
		return;

	for (i=0; i<dns_lookup_list->len; i++)
	{
		GURI *url = (GURI*) g_ptr_array_index (dns_lookup_list, i);

		if (url)
			gnet_uri_delete (url);
	}

	g_ptr_array_free (dns_lookup_list, TRUE);

	dns_lookup_list = NULL;
}



/******************************************************************************
 *
 *  dns_lookup_free_serverlist
 *
 *  frees all memory and structures of the dns_lookup_server_list GPtrArray
 *
 ***/

static void
dns_lookup_free_serverlist (void)
{
	guint i;

	if (!dns_server_list)
		return;

	for (i=0; i<dns_server_list->len; i++)
	{
		GInetAddr *ia = (GInetAddr*) g_ptr_array_index (dns_server_list, i);

		if (ia)
			gnet_inetaddr_delete (ia);
	}

	g_ptr_array_free (dns_server_list, TRUE);

	dns_server_list = NULL;
}


/******************************************************************************
 *
 *  dns_lookup_read_lookuplist
 *
 *  read the gui_lookuplist file and put the servers in the
 *    GPtrArray dns_lookup_list
 *
 ***/

void
dns_lookup_read_lookuplist (void)
{
	const gchar  *filename;
	gchar       **line, **linearray;

	dns_lookup_free_lookuplist ();

	dns_lookup_list = g_ptr_array_new();

	filename = opt_get_opt_filename_without_instance("gui_lookuplist");

	linearray = misc_get_array_of_lines_from_textfile (filename, FALSE, FALSE);

	if (!linearray)
		return;

	line = linearray;
	while (*line)
	{
		gchar *thisline;
		gchar *urlstr;
		gchar *proto;
		GURI  *url;

		thisline = g_strchug(*line);	/* remove leading whitespaces */

		if ( strlen(thisline) <=5 || *thisline == '#' || *thisline == 0x00 )
		{
			line++;
			continue;
		}

		proto = "normal"; /* XXX - we'll abuse that for 'priority' */

		if (*thisline == '+')
		{
			proto = "high";
			thisline++;
		}

		urlstr = g_strdup_printf ("%s://%s", proto, thisline);

		url = gnet_uri_new (urlstr);

		if (url)
		{
			/* set to default port if no port is specified */
			if (!url->port)
				gnet_uri_set_port (url, 4661);

			g_ptr_array_add (dns_lookup_list, url);
		}

		G_FREE (urlstr);

		line++;
	}

	g_strfreev(linearray);
}



/******************************************************************************
 *
 *   dns_lookup_remove_dns_servers
 *
 *   Removes the server IPs/ports added via DNS lookup
 *    from core's serverlist
 *
 ***/

void
dns_lookup_remove_dns_servers (void)
{
	guint i;

	if (!dns_server_list)
		return;

	if ( dns_server_list->len == 0 )
		return;

	statusbar_msg ("%s", _(" Removing servers on dynamic ip..."));

	for (i=0; i < dns_server_list->len; i++)
	{
		GInetAddr *ia = (GInetAddr*) g_ptr_array_index (dns_server_list, i);

		if (ia)
			gui_core_conn_send_remove_server(core, misc_gnet_inetaddr_get_ip(ia), gnet_inetaddr_get_port(ia));

		while (gtk_events_pending())
			gtk_main_iteration();

        statusbar_msg ("%s", _(" Done removing DNS servers. (core shutdown may take a few seconds...)"));
	}

	dns_lookup_free_serverlist();
}


/******************************************************************************
 *
 *  dns_lookup_callback
 *
 *  The callback that is called when the lookup succeeded or failed
 *   data is: 0 for normal priority, 1 for high priority
 *
 ***/

static void
dns_lookup_callback (GInetAddr *inetaddr, gpointer data)
{
	guint priority = GPOINTER_TO_UINT(data);

	if ( inetaddr != NULL )
	{
		guint32 ip   = misc_gnet_inetaddr_get_ip (inetaddr);
		guint16 port = gnet_inetaddr_get_port (inetaddr);

		if ( ip > 0 )
		{
			gui_core_conn_send_add_server(core, ip, port);

			/* set server to high priority? */
			if ( priority == 1 )
				gui_core_conn_send_set_server_pref(core, ip, port, 1);
		}

		if ( !dns_server_list )
			dns_server_list = g_ptr_array_new();

		g_ptr_array_add (dns_server_list, inetaddr);
	}

	dns_lookup_single_lookup_status = (inetaddr != NULL ) ? ONE_LOOKUP_DONE : ONE_LOOKUP_DONE_FAILURE;
}



/******************************************************************************
 *
 *  dns_lookup_do_lookup
 *
 *  Start the DNS lookup procedure
 *
 ***/

void
dns_lookup_do_lookup (void)
{
	guint i;

	if (dns_lookup_in_progress)
		return;

	/* XXX - Do NOT check whether core is alive. We call this function when we just
	 *       got connected to the core. At that point the core is technically not
	 *       'alive' yet (because status is not _COMMUNICATING yet).
	 */
/*
	if (!gui_core_conn_is_alive(core))
		return;
*/

	if (!dns_lookup_list)
		return;

	if ( dns_lookup_list->len == 0 )
		return;

	dns_lookup_free_serverlist();

	dns_lookup_in_progress = TRUE;

	for ( i = 0;  i < dns_lookup_list->len; i++ )
	{
		guint        priority = 0;
		GInetAddr   *inetaddr;
		GURI        *url;

		url = g_ptr_array_index (dns_lookup_list, i);

		if (!url)
			continue;

		if ( g_ascii_strncasecmp(url->scheme,"high",4) == 0 )
			priority = 1; /* high */

		dns_lookup_single_lookup_status = -1;

#warning ******************************* FIX ASYNC DNS LOOKUP ******************
#if 0 /* HOW DO WE GET THE CONTEXT OF THE GTK MAIN LOOP? */

		/* check number of CPUs at runtime, so we only call the slow DNS lookup code that
		 * will freeze the GUI if we are on multi-processor systems
		 */
		if (misc_get_ncpus()==1)
		{

		/*  DO ASYNCHRONEOUS LOOKUP. DOESN'T WORK ON SMP MACHINES DUE TO A BUG IN GLIB OR GNET.
			BUT AS WE WAIT FOR THE RESULT RIGHT AWAY ANYWAY (AND BLOCK THE DISPLAY), WE CAN
			JUST AS WELL DO A SYNCHRONEOUS LOOKUP... (thanks to Roman Hodek for fixing this problem)
		*/
			if (0 < gnet_inetaddr_new_async (url->hostname, url->port, dns_lookup_callback, GUINT_TO_POINTER(priority)))
			{
				while (dns_lookup_single_lookup_status == -1)
				{
					/*
					 * XXX - for some reasons (I suppose in connection with threads)
					 * we can NOT do a gtk_main_iteration() here without screwing up
					 * the GUI (everlasting freeze) (????)
					 */
/*					if (gtk_events_pending())
						gtk_main_iteration();
*/
					g_main_iteration(FALSE);
				}
			}

		}
		else
		{
#endif
			/* we are on a SMP-machine. This code is bad, but at least it doesn't crash */
			inetaddr = gnet_inetaddr_new (url->hostname, url->port);

			if (gtk_events_pending())
				gtk_main_iteration();

			dns_lookup_single_lookup_status = (inetaddr!=NULL) ? ONE_LOOKUP_DONE : ONE_LOOKUP_DONE_FAILURE;

			/* calling the old async callback is dirty, but works for now */
			dns_lookup_callback(inetaddr, GUINT_TO_POINTER(priority));
#if 0
		}
#endif

		gnet_uri_set_path (url, (dns_lookup_single_lookup_status==ONE_LOOKUP_DONE) ? "/OK" : "/FAIL");
		status_msg (_("GUI: DNS lookup of %s:%u %s\n"), url->hostname, url->port, url->path);
	}

	dns_lookup_in_progress = FALSE;
}


