/*
 * This is the main source file for GtkICQ.
 * Written by:
 *  Jeremy Wise <jwise@pathwaynet.com>
 *  Ryan Azcueta Krenzischek <rkrenzis@ureach.com>
 *
 * Original content provided by:
 *  Matt Smith
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gtkicq.h"
#include "datatype.h"
#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>

#include <fcntl.h>
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <sys/wait.h>
#include <signal.h>

#include <gtk/gtk.h>
#include "gtkfunc.h"

#include "tcp.h"

#include "loadpixmap.h"

#include "sendmsg.h"
#include "msg_queue.h"

BOOL Int_End;
BOOL Russian = FALSE;
BOOL Logging = TRUE;
BOOL Quit = FALSE;
BOOL Verbose = FALSE; /* this is needed because I leeched so code 
                  from another project I'm working on. */
BOOL serv_mess[ 1024 ];
WORD last_cmd[ 1024 ]; /* command issued for the first 1024 SEQ #'s */
/******************** if we have more than 1024 SEQ this will need some hacking */
WORD seq_num = 1;  /* current sequence number */
DWORD our_ip = 0x0100007f; /* localhost for some reason */
DWORD our_port = 0; /* the port to make tcp connections on */
DWORD our_sok; /* The TCP socket */
/************ We don't make tcp connections yet though :( */
DWORD UIN; /* current User Id Number */
char nickname[30];
BOOL Contact_List = FALSE;
Contact_Member Contacts[ 200 ]; /* no more than 100 contacts max */
int Num_Contacts=0;
DWORD Current_Status=STATUS_OFFLINE;
DWORD last_recv_uin=0;
char passwd[100];
char server[100];
DWORD set_status;
DWORD remote_port;
BOOL Done_Login=FALSE;
char Away_Message[256] = "User is currently away.";
BOOL search_in_progress = FALSE;

BYTE chat_fg_red = 0, chat_fg_green = 0, chat_fg_blue = 0;
BYTE chat_bg_red = 255, chat_bg_green = 255, chat_bg_blue = 255;

GtkWidget *log_list;
GtkWidget *log_window;

GdkPixmap *gtkicq_logo_pixmap;
GdkPixmap *icon_blank_pixmap;
GdkPixmap *icon_message_pixmap;
GdkPixmap *icon_url_pixmap;
GdkPixmap *icon_auth_pixmap;
GdkPixmap *icon_away_pixmap;
GdkPixmap *icon_na_pixmap;
GdkPixmap *icon_occ_pixmap;
GdkPixmap *icon_dnd_pixmap;
GdkPixmap *icon_ffc_pixmap;
GdkPixmap *icon_inv_pixmap;
GdkPixmap *icon_online_pixmap;
GdkPixmap *icon_offline_pixmap;
GdkPixmap *icon_chat_pixmap;
GdkPixmap *icon_chat2_pixmap;
GdkPixmap *nomess_pixmap;

GdkBitmap *gtkicq_logo_bitmap;
GdkBitmap *icon_blank_bitmap;
GdkBitmap *icon_message_bitmap;
GdkBitmap *icon_url_bitmap;
GdkBitmap *icon_auth_bitmap;
GdkBitmap *icon_away_bitmap;
GdkBitmap *icon_na_bitmap;
GdkBitmap *icon_occ_bitmap;
GdkBitmap *icon_dnd_bitmap;
GdkBitmap *icon_ffc_bitmap;
GdkBitmap *icon_inv_bitmap;
GdkBitmap *icon_online_bitmap;
GdkBitmap *icon_offline_bitmap;
GdkBitmap *icon_chat_bitmap;
GdkBitmap *icon_chat2_bitmap;
GdkBitmap *nomess_bitmap;

char configfilename[256] = "";

#if 0
WORD system_messages;
BYTE **system_message;
#endif

int sound_toggle = FALSE;
int beep_toggle = FALSE;
int force_toggle = FALSE;
int webpresence_toggle = TRUE;

int Connected = FALSE;

/* Sound variables added by Paul Laufer [PEL] */
int UserOnline = FALSE;
char UserOnlineSound[256] = {};
int UserOffline = FALSE;
char UserOfflineSound[256] = {};
int RecvMessage = FALSE;
char RecvMessageSound[256] = {};
int RecvChat = FALSE;
char RecvChatSound[256] = {};
/* end sound vars */

int packet_toggle = FALSE;

int is_new_user = FALSE;

int udp_gdk_input = 0;
int tcp_gdk_input = 0;

GtkWidget *statusbar;
GtkTooltips *status_tooltip;

int connecting;

int applet_toggle = TRUE;

USER_INFO_STRUCT our_user_info;

char WindowTitle[256];
int WindowWidth = 175, WindowHeight = 310;

unsigned int next_resend;

struct sokandlb *MainData;

GdkFont *ChatFont;
char ChatFontString[256];

void Server_Response( struct sokandlb *data, srv_net_icq_pak pak, DWORD len, WORD cmd, WORD ver, WORD seq, WORD uin );
void add_from_search( GtkWidget *widget, struct sokandlb *data );
void handle_signal( int signum );
int hide_ch_window( GtkWidget *widget, GdkEvent *event, GtkWidget *window );
void Check_Endian( void );

void ready_set( void )
{
	char *sts = NULL;

#ifdef TRACE_FUNCTION
	printf( "ready_set\n" );
#endif

	if ( Current_Status == STATUS_OFFLINE )
	{
		sts = " Ready (Offline)";
	}
	else
	{
		switch ( Current_Status & 0xffff )
		{
			case STATUS_ONLINE:
				sts = " Ready (Online)";
				break;
			case STATUS_DND:
				sts = " Ready (Do Not Disturb)";
				break;
			case STATUS_AWAY:
				sts = " Ready (Away)";
				break;
			case STATUS_OCCUPIED:
				sts = " Ready (Occupied)";
				break;
			case STATUS_NA:
				sts = " Ready (Not Available)";
				break;
			case STATUS_INVISIBLE:
				sts = " Ready (Invisible)";
				break;
			case STATUS_FREE_CHAT:
				sts = " Ready (Free for Chat)";
				break;
			default:
				sts = " Ready";
				break;
		}
	}
	gtk_statusbar_pop( GTK_STATUSBAR( statusbar ), 1 );
	gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 1, sts );
}

void add_from_search( GtkWidget *widget, struct sokandlb *data )
{
	int new_uin;
	int row;
	char name[16];

#ifdef TRACE_FUNCTION
	printf( "add_from_search\n" );
#endif

	row = GPOINTER_TO_INT( GTK_CLIST( found_list )->selection->data );
	new_uin = GPOINTER_TO_INT(
		gtk_clist_get_row_data( GTK_CLIST( found_list ), row ) );
	sprintf( name, "%d", new_uin );
	Add_User( data->sok, new_uin, name );
}

unsigned int sound_pid;

void handle_signal( int signum )
{
	int status;
	
	if( signum == SIGCHLD )
	{
		waitpid( sound_pid, &status, WNOHANG );
		if( WIFEXITED( status ) || WIFSIGNALED( status ) )
			sound_pid = 0;
	}
	if( signum == SIGUSR1 )
		printf( "Received SIGUSR1.  Not responding.\n" );
}

int hide_ch_window( GtkWidget *widget, GdkEvent *event, GtkWidget *window )
{
#ifdef TRACE_FUNCTION
	printf( "hide_ch_window\n" );
#endif
	gtk_widget_hide( window );
	return TRUE;
}

void toggle_log_window( GtkWidget *widget, gpointer data )
{
#ifdef TRACE_FUNCTION
	printf( "toggle_log_window\n" );
#endif

	if( !GTK_WIDGET_VISIBLE( log_window ) );
		gtk_widget_show( log_window );
}

/*
 * Connect to 'hostname' on port 'port'
 * 'hostname' can be DNS or n.n.n.n
 */
 
int Connect_Remote( char *hostname, int port, FD_T aux )
{
	int conct, length;
	int sok;

	/* Used for inet address */
	struct sockaddr_in sin;

	/* Used for DNS lookup */
	struct hostent *host_struct;
   
	/* Check for n.n.n.n notation */
	sin.sin_addr.s_addr = inet_addr( hostname );

#ifdef TRACE_FUNCTION
	printf( "Connect_Remote\n" );
#endif

	/* If name isn't n.n.n.n, it must be DNS */
/*	if ( sin.sin_addr.s_addr  == -1 )
	{*/
		host_struct = gethostbyname( hostname );

		if ( host_struct == NULL )
		{
			return 0;
		}
/*	}
	else
		host_struct = gethostbyaddr( hostname, strlen( hostname ), AF_INET );
*/
	sin.sin_addr = *( ( struct in_addr * )host_struct->h_addr );

	/* Use inet, not AppleTalk */
	sin.sin_family = AF_INET;
	
	/* Port */
	sin.sin_port = htons( port );

	/* Create unconnected socket */
	sok = socket( AF_INET, SOCK_DGRAM, 0 );

	if ( sok == -1 )
	{
		printf( "Could not create socket\n" );
		exit( 1 );
	}   

	conct = connect( sok, ( struct sockaddr * ) &sin, sizeof( sin ) );

	/* Connection Failure */
	if ( conct == -1 )
	{
		printf( "Could not connect to socket\n" );
		return 0;
	}
	
	length = sizeof( sin ) ;
	getsockname( sok, ( struct sockaddr * ) &sin, &length );
	our_ip   = sin.sin_addr.s_addr;
	if( !our_port )
		our_port = ntohs( sin.sin_port + 2 );

	/* Connected */
	return sok;
}

/* Handle packets from Server */
#if ICQ_VER == 0x0004
static void Multi_Packet( struct sokandlb *data, BYTE *pdata )
{
	int num_pack, i;
	int len;
	BYTE *j;
	srv_net_icq_pak pak;
	num_pack = (unsigned char) pdata[ 0 ];
	j = pdata;
	j++;

#ifdef TRACE_FUNCTION
	printf( "Multi_Packet\n" );
#endif
	
	for( i = 0; i < num_pack; i ++ )
	{
		len = Chars_2_Word( j );
		pak = *( srv_net_icq_pak *) j;
		j += 2;
		Server_Response( data, pak, len - sizeof( pak.head ), Chars_2_Word( pak.head.cmd ),
		                 Chars_2_Word( pak.head.ver ), Chars_2_Word( pak.head.seq ),
		                 Chars_2_Word( pak.head.UIN ) );
		j += len;
	}
}
#endif

void Server_Response( struct sokandlb *data, srv_net_icq_pak pak, DWORD len, WORD cmd, WORD ver, WORD seq, WORD uin )
{
	ProgressData *pdata = NULL;
	GtkWidget *window;
	GtkWidget *scrollwindow;
	GtkWidget *box;
	GtkWidget *button;
	GtkWidget *buttonbox;

	SIMPLE_MESSAGE_PTR s_mesg;
	int i, cx;

#ifdef TRACE_FUNCTION
	printf( "Server_Response\n" );
#endif
   
	switch ( Chars_2_Word( pak.head.cmd ) )
	{
#if ICQ_VER == 0x0004
		case SRV_MULTI_PACKET:
			Multi_Packet( data, pak.data );
			break;
#endif
		case SRV_WEBPRESENCE_ACK:
			icq_finishwebpresence( data->sok );
			break;
		case SRV_ACK:
			/* Server Acknowledged Command */
			Check_Queue( seq );
			break;
		case SRV_NEW_UIN:
			UIN = Chars_2_DW( pak.head.UIN );
			Create_RC_File();
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			seq_num = 0;
			memset( serv_mess, FALSE, 1024 );
			close( data->sok );
			data->sok = Connect_Remote( server, remote_port, STDERR );
			Login( data->sok, UIN, passwd, our_ip, our_port );
			break;
		case SRV_LOGIN_REPLY:
#if ICQ_VER == 0x0002
			UIN = Chars_2_DW( &pak.data[0] );
			our_ip = Chars_2_DW( &pak.data[4] );
#else
			our_ip = Chars_2_DW( &pak.data[0] );
#endif
			log_window_add( "Connected to ICQ Server", 1, UIN );
			gtk_timeout_remove( connecting );
			icq_change_status( data->sok, Current_Status, data );
			ready_set();
			Time_Stamp();

			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			snd_login_1( data->sok );
			snd_contact_list( data->sok );
			snd_invis_list( data->sok );
			snd_vis_list( data->sok );
			if( webpresence_toggle )
				icq_sendwebpresence( data->sok );
			break;
		case SRV_RECV_MESSAGE:
			Recv_Message( data->sok, pak, data );
			break;
		case SRV_X1:
			/* Unknown message sent after login */
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_X2:
			/* Unknown message sent after login */
			Show_Quick_Status( data );
			Done_Login = TRUE;
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			snd_got_messages( data->sok );
			is_new_user = FALSE;
			break;
		case SRV_INFO_REPLY:
			Display_Info_Reply( data->sok, pak, data );
			break;
		case SRV_EXT_INFO_REPLY:
			Display_Ext_Info_Reply( data->sok, pak );
			break;
		case SRV_USER_OFFLINE:
			User_Offline( data->sok, pak );
			break;
		case SRV_USER_ONLINE:
			User_Online( data->sok, pak );
			break;
		case SRV_STATUS_UPDATE:
			Status_Update( data->sok, pak );
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_TRY_AGAIN:
			/* Server busy, retrying */
			Login( data->sok, UIN, &passwd[0], our_ip, our_port );
			for( i = 0; i< 1024; i++ )
			{
				serv_mess[ i ]=FALSE;
			}
			break;
		case SRV_FORCE_DISCONNECT:
		case SRV_GO_AWAY:
			log_window_add( "Server forced disconnect", 1, UIN );
			Time_Stamp();
			Quit = TRUE;
			if( Connected )
			{
				Quit_ICQ( data->sok );
				gdk_input_remove( udp_gdk_input );
				for( cx = 0; cx < Num_Contacts; cx ++ )
				{
					Contacts[ cx ].last_status = Contacts[ cx ].status;
					Contacts[ cx ].status = STATUS_OFFLINE;
				}
				Show_Quick_Status( data );
				seq_num = 0;
				memset( serv_mess, FALSE, 1024 );
				close( data->sok );

				Connected = FALSE;
				msg_queue_clear();

				ready_set();
   

				Done_Login = FALSE;
				data->sok = Connect_Remote( server, remote_port, STDERR );
				if( data->sok > 0 )
				{
					udp_gdk_input = gdk_input_add( data->sok, GDK_INPUT_READ, (GdkInputFunction) icq_refresh, data );
					Connected = TRUE;
					connecting = show_wait( "Connecting to Server", pdata );
					gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 1, " Connecting to Server..." );

					Login( data->sok, UIN, &passwd[0], our_ip, our_port );
				}
			}

			break;
		case SRV_END_OF_SEARCH:
			if( found_list )
			{
				gtk_timeout_remove( search_wait );
				gtk_statusbar_pop( GTK_STATUSBAR( statusbar ), 2 + search_wait );
				window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
				gtk_widget_set_usize( window, 700, 360 );
				gtk_container_border_width( GTK_CONTAINER( window ), 0 );
				gtk_window_set_title( GTK_WINDOW( window ), "Search Results" );
	
				box = gtk_vbox_new( FALSE, 0 );
				gtk_container_add( GTK_CONTAINER( window ), box );
				gtk_widget_show( box );
			
				scrollwindow = gtk_scrolled_window_new( NULL, NULL );
				gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrollwindow ),
				                                GTK_POLICY_AUTOMATIC,
				                                GTK_POLICY_AUTOMATIC );
				gtk_box_pack_start( GTK_BOX( box ), scrollwindow, FALSE, FALSE, 0 );
				gtk_widget_set_usize( scrollwindow, 600, 300 );
				gtk_widget_show( scrollwindow );
			
				gtk_clist_set_selection_mode( GTK_CLIST( found_list ),
				                              GTK_SELECTION_BROWSE );
				gtk_container_add( GTK_CONTAINER( scrollwindow ), found_list );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 0, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 1, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 2, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 3, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 4, 150 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 5, 150 );
				gtk_widget_show( found_list );

				buttonbox = gtk_hbox_new( FALSE, 0 );
				gtk_box_pack_end( GTK_BOX( box ), buttonbox, FALSE, FALSE, 0 );
				button = gtk_button_new_with_label( "Add to List" );
				gtk_widget_set_usize( button, 70, 25 );
				gtk_box_pack_start( GTK_BOX( buttonbox ), button, FALSE, FALSE, 5 );

				gtk_signal_connect( GTK_OBJECT( button ), "clicked",
				                    GTK_SIGNAL_FUNC( add_from_search ),
				                    data );
				gtk_widget_show( button );
				
				button = gtk_button_new_with_label( "Close" );
				gtk_widget_set_usize( button, 70, 25 );
				gtk_box_pack_start( GTK_BOX( buttonbox ), button, FALSE, FALSE, 5 );

				gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
				                           GTK_SIGNAL_FUNC( gtk_widget_destroy ),
				                           GTK_OBJECT( window ) );
				gtk_widget_show( button );
				gtk_widget_show( buttonbox );			
				gtk_widget_show( window );
			}
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_USER_FOUND:
			Display_Search_Reply( data->sok, pak );
			break;
		case SRV_SYS_DELIVERED_MESS:
			s_mesg = ( SIMPLE_MESSAGE_PTR ) pak.data;
			last_recv_uin = Chars_2_DW( s_mesg->uin );
			Time_Stamp();
			Do_Msg( Chars_2_Word( s_mesg->type ), ( s_mesg->len + 2 ),
			        last_recv_uin, data, 'm' ); 
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );

			if ( 0xfe != *( ((unsigned char *) s_mesg ) + sizeof( s_mesg ) ) )
			{
			}
			break;
		case SRV_BAD_PASSWORD:
			OK_Box( "Error: Bad password", "The password you've provided is incorrect" );
			gtk_timeout_remove( connecting );
			Current_Status = STATUS_OFFLINE;
			gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 1, " Server Disconnected" );
			break;
		default:
			/* Unhandled Commands */
			printf( "\nThe response was %04X\t", Chars_2_Word( pak.head.cmd ) );
			printf( "The version was %X\t", Chars_2_Word( pak.head.ver ) );
			Time_Stamp();
			printf( "\nThe SEQ was %04X\t", Chars_2_Word( pak.head.seq ) );

			printf( "The sizea was %ld\n", len );
			/* Fake */
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
	}
}

#if ICQ_VER == 0x0004
void Handle_Server_Response( struct sokandlb *data )
{
   srv_net_icq_pak pak;
   int s;

#ifdef TRACE_FUNCTION
	printf( "Handle_Server_Response\n" );
#endif

   s = SOCKREAD( data->sok, &pak.head.ver, sizeof( pak ) - 2  );
   if ( s < 0 )
      return;
   if ( ( serv_mess[ Chars_2_Word( pak.head.seq ) ] ) &&
      ( Chars_2_Word( pak.head.cmd ) != SRV_NEW_UIN ) )
   {
      if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK ) /* ACKs don't matter */
      {
         ack_srv( data->sok, Chars_2_Word( pak.head.seq ) ); /* LAGGGGG!! */
         return;
      }
   }
   if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK )
   {
      serv_mess[ Chars_2_Word( pak.head.seq ) ] = TRUE;
      ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
   }

	packet_print( pak.head.ver, s,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_RECEIVE );

   Server_Response( data, pak, s - ( sizeof( pak.head ) - 2 ),
      Chars_2_Word( pak.head.cmd ), Chars_2_Word( pak.head.ver ),
      Chars_2_Word( pak.head.seq ), Chars_2_DW( pak.head.UIN ) );
}
#else
void Handle_Server_Response( struct sokandlb *data )
{
	GtkWidget *window;
	GtkWidget *scrollwindow;
	GtkWidget *box;
	GtkWidget *button;
	GtkWidget *buttonbox;

	srv_net_icq_pak pak;
	SIMPLE_MESSAGE_PTR s_mesg;
	int s, i, len, cx;

#ifdef TRACE_FUNCTION
	printf( "Handle_Server_Response\n" );
#endif
   
	s = SOCKREAD( data->sok, &pak.head.ver, sizeof( pak ) - 2  );
	packet_print( pak.head.ver, s,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_RECEIVE );

	if ( ( serv_mess[ Chars_2_Word( pak.head.seq ) ] ) && ( Chars_2_Word( pak.head.cmd ) != SRV_NEW_UIN ) )
	{
		if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK ) /* ACKs don't matter */
		{
			/* Ignore Message */
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			return;
		}
	}
	
	if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK )
	{
		serv_mess[ Chars_2_Word( pak.head.seq ) ] = TRUE;
	}

	switch ( Chars_2_Word( pak.head.cmd ) )
	{
		case SRV_ACK:
			/* Server Acknowledged Command */
			break;
		case SRV_NEW_UIN:
			UIN = Chars_2_DW( &pak.data[2] );
			Create_RC_File();
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			seq_num = 0;
			memset( serv_mess, FALSE, 1024 );
			close( data->sok );
			data->sok = Connect_Remote( server, remote_port, STDERR );
			if( data->sok > 0 )
				Login( data->sok, UIN, passwd, our_ip, our_port );
			break;
		case SRV_LOGIN_REPLY:
			UIN = Chars_2_DW( &pak.data[0] );
			our_ip = Chars_2_DW( &pak.data[4] );
			log_window_add( "Connected to ICQ Server", 1, UIN );
			gtk_timeout_remove( connecting );
			ready_set();
			Time_Stamp();

			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			snd_login_1( data->sok );
			snd_contact_list( data->sok );
			snd_invis_list( data->sok );
			snd_vis_list( data->sok );
			break;
		case SRV_RECV_MESSAGE:
			Recv_Message( data->sok, pak, data );
			break;
		case SRV_X1:
			/* Unknown message sent after login */
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_X2:
			/* Unknown message sent after login */
			Show_Quick_Status( data );
			Done_Login = TRUE;
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			snd_got_messages( data->sok );
			is_new_user = FALSE;
			break;
		case SRV_INFO_REPLY:
			Display_Info_Reply( data->sok, pak, data );
			break;
		case SRV_EXT_INFO_REPLY:
			Display_Ext_Info_Reply( data->sok, pak );
			break;
		case SRV_USER_OFFLINE:
			User_Offline( data->sok, pak );
			break;
		case SRV_USER_ONLINE:
			User_Online( data->sok, pak );
			break;
		case SRV_STATUS_UPDATE:
			Status_Update( data->sok, pak );
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_TRY_AGAIN:
			/* Server busy, retrying */
			Login( data->sok, UIN, &passwd[0], our_ip, our_port );
			for( i = 0; i< 1024; i++ )
			{
				serv_mess[ i ]=FALSE;
			}
			break;
		case SRV_FORCE_DISCONNECT:
		case SRV_GO_AWAY:
			log_window_add( "Server forced disconnect", 1, UIN );
			Time_Stamp();
			Quit = TRUE;
			if( Connected )
			{
				Quit_ICQ( data->sok );
				gdk_input_remove( udp_gdk_input );
				for( cx = 0; cx < Num_Contacts; cx ++ )
				{
					Contacts[ cx ].last_status = Contacts[ cx ].status;
					Contacts[ cx ].status = STATUS_OFFLINE;
				}
				Show_Quick_Status( data );
				seq_num = 0;
				memset( serv_mess, FALSE, 1024 );
				close( data->sok );

				Connected = FALSE;
				msg_queue_clear();

				ready_set();

				Done_Login = FALSE;
				data->sok = Connect_Remote( server, remote_port, STDERR );
				if( data->sok > 0 )
				{
					udp_gdk_input = gdk_input_add( data->sok, GDK_INPUT_READ, (GdkInputFunction) icq_refresh, data );
					Connected = TRUE;
					connecting = show_wait( "Connecting to Server", pdata );
					gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 1, " Connecting to Server..." );

					Login( data->sok, UIN, &passwd[0], our_ip, our_port );
				}
			}

			break;
		case SRV_END_OF_SEARCH:
			if( found_list )
			{
				gtk_timeout_remove( search_wait );
				gtk_statusbar_pop( GTK_STATUSBAR( statusbar ), 2 + search_wait );
				window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
				gtk_widget_set_usize( window, 700, 360 );
				gtk_container_border_width( GTK_CONTAINER( window ), 0 );
				gtk_window_set_title( GTK_WINDOW( window ), "Search Results" );
	
				box = gtk_vbox_new( FALSE, 0 );
				gtk_container_add( GTK_CONTAINER( window ), box );
				gtk_widget_show( box );
			
				scrollwindow = gtk_scrolled_window_new( NULL, NULL );
				gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrollwindow ),
				                                GTK_POLICY_AUTOMATIC,
				                                GTK_POLICY_AUTOMATIC );
				gtk_box_pack_start( GTK_BOX( box ), scrollwindow, FALSE, FALSE, 0 );
				gtk_widget_set_usize( scrollwindow, 600, 300 );
				gtk_widget_show( scrollwindow );
			
/*				gtk_clist_set_policy( GTK_CLIST( found_list ),
				                      GTK_POLICY_AUTOMATIC,
				                      GTK_POLICY_AUTOMATIC );*/
				gtk_clist_set_selection_mode( GTK_CLIST( found_list ),
				                              GTK_SELECTION_BROWSE );
				gtk_container_add( GTK_CONTAINER( scrollwindow ), found_list );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 0, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 1, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 2, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 3, 75 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 4, 150 );
				gtk_clist_set_column_width( GTK_CLIST( found_list ), 5, 150 );
				gtk_widget_show( found_list );

				buttonbox = gtk_hbox_new( FALSE, 0 );
				gtk_box_pack_end( GTK_BOX( box ), buttonbox, FALSE, FALSE, 0 );
				button = gtk_button_new_with_label( "Add to List" );
				gtk_widget_set_usize( button, 70, 25 );
				gtk_box_pack_start( GTK_BOX( buttonbox ), button, FALSE, FALSE, 5 );

				gtk_signal_connect( GTK_OBJECT( button ), "clicked",
				                    GTK_SIGNAL_FUNC( add_from_search ),
				                    data );
				gtk_widget_show( button );
				
				button = gtk_button_new_with_label( "Close" );
				gtk_widget_set_usize( button, 70, 25 );
				gtk_box_pack_start( GTK_BOX( buttonbox ), button, FALSE, FALSE, 5 );

				gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
				                           GTK_SIGNAL_FUNC( gtk_widget_destroy ),
				                           GTK_OBJECT( window ) );
				gtk_widget_show( button );
				gtk_widget_show( buttonbox );			
				gtk_widget_show( window );
			}
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
			break;
		case SRV_USER_FOUND:
			Display_Search_Reply( data->sok, pak );
			break;
		case SRV_SYS_DELIVERED_MESS:
			s_mesg = ( SIMPLE_MESSAGE_PTR ) pak.data;
			last_recv_uin = Chars_2_DW( s_mesg->uin );
			Time_Stamp();
			Do_Msg( Chars_2_Word( s_mesg->type ), ( s_mesg->len + 2 ),
			        last_recv_uin, data, 'm' ); 
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );

			if ( 0xfe != *( ((unsigned char *) s_mesg ) + sizeof( s_mesg ) ) )
			{
			}
			break;
		default:
			/* Unhandled Commands */
			printf( "\nThe response was %04X\t", Chars_2_Word( pak.head.cmd ) );
			printf( "The version was %X\t", Chars_2_Word( pak.head.ver ) );
			Time_Stamp();
			printf( "\nThe SEQ was %04X\t", Chars_2_Word( pak.head.seq ) );

			len = s - ( sizeof( pak.head ) - 2 );
			printf( "The sizea was %d\n", len );
			/* Fake */
			ack_srv( data->sok, Chars_2_Word( pak.head.seq ) );
	}
}
#endif

/* Verify that we are in the correct endian */
void Check_Endian( void )
{
	int i;
	char passwd[10] = { '1', '0', '0', '0', '0', '0', '0', '0', '0' };

#ifdef TRACE_FUNCTION
	printf( "Check_Endian\n" );
#endif

	i = *( DWORD * ) passwd;
	if ( i == 1 )
	{
		Int_End = TRUE;
	}
	else
	Int_End = FALSE;
}

int main( int argc, char *argv[] )
{
	struct sokandlb sal;
	int i;
	int next;

	int l = 1;
	int temp_sok;
	struct sockaddr_in addr;

	ProgressData *pdata = NULL;

	GtkWidget *handlebox;
	GtkWidget *menubar;
	GtkWidget *main_window;
	GtkStyle  *style;
	GtkWidget *box, *box2, *box3;
	GtkWidget *scrollwindow;
	GtkWidget *button;
	GtkWidget *log_table, *bbox;
	GtkWidget *statusbox;
	GtkWidget *scroll_win;

	char *lltitles[] = { "Time", "Date", "User", "Action" };

#if 0
	char *sys[2] = { "", "System" };
#endif

	MainData = &sal;

	sal.online = 0;
	sal.offline = 0;

#ifdef TRACE_FUNCTION
	printf( "main\n" );
#endif

	sprintf( WindowTitle, "GtkICQ" );

	signal( SIGCHLD, &handle_signal );
	signal( SIGUSR1, &handle_signal );

	gtk_init( &argc, &argv );

	gtk_rc_parse( ".gtkrc" );

	strcpy( ChatFontString, "" );

	Get_Unix_Config_Info();

        /***************INITIALIZE PIXMAPS/MAIN_WINDOW/COLOR MAPS*****************/
	/* Main_Window, Init_Colors and Init_Pixmaps procedures and variables    */
	/* must be defined before menubar is defined or loaded because gtk       */
	/* will look for the pixmaps in the status menu.  If these items are not */
	/* defined before menubar loads, the pixmaps in the Status menu will     */
	/* have NULL values and GtkICQ will seg fault.                           */
        /*************************************************************************/

        sal.lb_userwin = gtk_clist_new( 2 );
        style = gtk_widget_get_style( sal.lb_userwin );
        main_window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
        sal.window = main_window;
        gtk_window_set_policy( GTK_WINDOW( main_window ), TRUE, TRUE, TRUE );

        gtk_signal_connect_object( GTK_OBJECT ( main_window ), "destroy",
                                   GTK_SIGNAL_FUNC ( icq_quit_object ),
                                   (gpointer)&sal );

        gtk_widget_set_usize( main_window, WindowWidth, WindowHeight );

        gtk_container_border_width( GTK_CONTAINER ( main_window ), 0 );

        gtk_window_set_title(GTK_WINDOW ( main_window ), "GtkICQ");

        gtk_widget_show( main_window );

        init_colors( &sal );
        init_pixmaps( style, main_window );

	if( !strcmp( ChatFontString, "" ) )
		strcpy( ChatFontString, "-adobe-courier-medium-r-normal-*-*-140-*-*-m-*-iso8859-1" );

	menubar = main_menu( &sal );

	box = gtk_vbox_new( FALSE, 0);
	box2 = gtk_vbox_new( FALSE, 0 );
	box3 = gtk_table_new( 1, 2, FALSE );

	gtk_container_add( GTK_CONTAINER( main_window ), box2 );
	handlebox = gtk_handle_box_new();
	gtk_container_add( GTK_CONTAINER( handlebox ), menubar );

	gtk_box_pack_start( GTK_BOX( box2 ), handlebox, FALSE, FALSE, 0 );
	gtk_widget_show( handlebox );
	gtk_widget_show( menubar );

	gtk_container_border_width( GTK_CONTAINER( box ), 5 );

	gtk_container_add( GTK_CONTAINER( box2 ), box );

	sal.lb_userwin = gtk_clist_new( 2 );

	gtk_widget_show( sal.lb_userwin );

	scroll_win = gtk_scrolled_window_new( NULL, NULL );
	gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW (scroll_win),
	                                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
	
	gtk_container_add( GTK_CONTAINER( scroll_win ), sal.lb_userwin );

/*	gtk_widget_set_usize( sal.lb_userwin,
	                      ( WindowWidth - 68 ) < 0 ? 0 : WindowWidth - 68, -1 );
*/
	gtk_clist_set_row_height( GTK_CLIST( sal.lb_userwin ), 20 );
	gtk_clist_set_column_width( GTK_CLIST( sal.lb_userwin ), 0, 16 );
	gtk_clist_set_column_width( GTK_CLIST( sal.lb_userwin ), 1, 10 );
	gtk_clist_set_selection_mode( GTK_CLIST( sal.lb_userwin ),
	                              GTK_SELECTION_BROWSE);

	gtk_signal_connect( GTK_OBJECT( sal.lb_userwin ), "button_press_event",
		                    GTK_SIGNAL_FUNC( icq_sendmessage_window ), &sal );

	gtk_signal_connect( GTK_OBJECT( sal.lb_userwin ), "key_press_event",
	                    GTK_SIGNAL_FUNC( icq_sendmessage_window ), &sal );

	gtk_box_pack_start( GTK_BOX( box ), scroll_win, TRUE, TRUE, 0 );
	gtk_widget_show( scroll_win );

#if 0
	sal.lb_syswin = gtk_clist_new( 2 );
	gtk_widget_set_usize( sal.lb_syswin, 100, 21 );
	gtk_clist_set_column_width( GTK_CLIST( sal.lb_syswin ), 0, 20 );
	gtk_clist_set_selection_mode( GTK_CLIST( sal.lb_syswin ), GTK_SELECTION_EXTENDED );

	gtk_clist_unselect_row( GTK_CLIST( sal.lb_syswin ), 0, 0 );

	gtk_signal_connect( GTK_OBJECT( sal.lb_syswin ), "button_press_event",
		                    GTK_SIGNAL_FUNC( icq_sysmessage_window ), &sal );

	gtk_box_pack_start( GTK_BOX( box ), sal.lb_syswin, FALSE, FALSE, 0 );
	gtk_clist_append( GTK_CLIST( sal.lb_syswin ), sys );
	gtk_widget_show( sal.lb_syswin );
#endif

	statusbox = gtk_hbox_new( FALSE, 0 );
	gtk_box_pack_end( GTK_BOX( box2 ), statusbox, FALSE, FALSE, 0 );
	gtk_widget_show( statusbox );

	statusbar = gtk_statusbar_new();
	gtk_widget_set_usize( statusbar, 0, 20 );
	gtk_box_pack_start( GTK_BOX( statusbox ), statusbar, TRUE, TRUE, 0 );
	gtk_widget_show( statusbar );

	progress_bar_make();
	gtk_box_pack_end( GTK_BOX( box2 ), progress_indicator, FALSE, FALSE, 0 );
	gtk_widget_show( progress_indicator );

	status_tooltip = gtk_tooltips_new();
	gtk_tooltips_set_tip( status_tooltip,
	                      statusbar, "foo\nbar", NULL );
	gtk_tooltips_enable( status_tooltip );

	/* init_colors( &sal ); */

	/* Initialize the XPMs */

	/* style = gtk_widget_get_style( sal.lb_userwin ); */
	/* init_pixmaps( style, main_window ); */

#if 0
	gtk_clist_set_pixmap( GTK_CLIST( sal.lb_syswin ), 0, 0,
	                      icon_online_pixmap, icon_online_bitmap );
#endif

	log_window = gtk_window_new( GTK_WINDOW_DIALOG );

	gtk_widget_set_usize( log_window, 625, 250 );

	gtk_container_border_width( GTK_CONTAINER ( log_window ), 5 );

	gtk_window_set_title(GTK_WINDOW ( log_window ), "GtkICQ: Connection History");

	scrollwindow = gtk_scrolled_window_new( NULL, NULL );
	gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrollwindow ),
	                                GTK_POLICY_AUTOMATIC,
	                                GTK_POLICY_AUTOMATIC );

	log_list = gtk_clist_new_with_titles( 4, lltitles );
	gtk_widget_show( log_list );

	gtk_container_add( GTK_CONTAINER( scrollwindow ), log_list );
	gtk_widget_show( scrollwindow );

	gtk_clist_column_titles_passive( GTK_CLIST( log_list ) );
	gtk_clist_set_row_height( GTK_CLIST( log_list ), 20 );
	gtk_clist_set_selection_mode (GTK_CLIST( log_list ),
	                              GTK_SELECTION_EXTENDED );

	log_table = gtk_table_new( 3, 1, FALSE );
	gtk_container_add( GTK_CONTAINER( log_window ), log_table );
	bbox = gtk_hbox_new( FALSE, 0 );

	gtk_table_attach( GTK_TABLE( log_table ), scrollwindow, 0, 1, 0, 1,
	GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 5 );

	gtk_clist_set_column_width( GTK_CLIST( log_list ), 0, 80 );
	gtk_clist_set_column_width( GTK_CLIST( log_list ), 1, 65 );
	gtk_clist_set_column_width( GTK_CLIST( log_list ), 2, 100 );

	button = gtk_button_new_with_label( "Close" );
	gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
	                           GTK_SIGNAL_FUNC( gtk_widget_hide_on_delete ),
	                           GTK_OBJECT( log_window ) );

	gtk_widget_set_usize( button, 100, 30 );
	gtk_container_add( GTK_CONTAINER( bbox ), button );
	gtk_table_attach( GTK_TABLE( log_table ), bbox, 0, 1, 1, 2, 0, 0, 0, 0 );
	gtk_widget_show( button );
	gtk_widget_show( log_table );
	gtk_widget_show( bbox );
	gtk_widget_show( box );
	gtk_widget_show( box2 );

	for( i = 0; i < Num_Contacts; i ++ )
	{
		Contacts[ i ].icon_p = icon_offline_pixmap;
		Contacts[ i ].icon_b = icon_offline_bitmap;
	}

	Show_Quick_Status( &sal );

	memset( serv_mess, FALSE, 1024 );

	msg_queue_init();

	if (argc > 1 )
	{
		for ( i = 1; i < argc; i++ )
		{
			if ( argv[i][0] != '-' )
			{
			}
			else if ( (argv[i][1] == 'v' ) || (argv[i][1] == 'V' ) )
			{
				Verbose = ! Verbose;
			}
		}
	}

	Check_Endian();

	sal.sok = Connect_Remote( server, remote_port, STDERR );

	if ( sal.sok > 0 )
	{
		gtk_timeout_add( 120000, (GtkFunction) stay_connected, &sal );
		gtk_timeout_add( 500, (GtkFunction) flash_messages, &sal );
#ifdef USE_XSCREENSAVER
		gtk_timeout_add( 60000, (GtkFunction) auto_away, &sal );
#endif
		udp_gdk_input = gdk_input_add( sal.sok, GDK_INPUT_READ, (GdkInputFunction) icq_refresh, &sal );

		addr.sin_addr.s_addr = htonl( INADDR_ANY );
		addr.sin_family = AF_INET;
		addr.sin_port = htons( our_port );
	
		temp_sok = socket( AF_INET, SOCK_STREAM, 0 );
		setsockopt( temp_sok, SOL_SOCKET, SO_REUSEADDR, &l, 4 );

		our_sok = bind( temp_sok, (struct sockaddr *)&addr, sizeof( addr ) );
		if( -1 == our_sok )
		{
			printf( "Could not bind to socket, port %ld\n", our_port );
			exit( 1 );
		}

		our_sok = temp_sok;
	
		if( -1 == listen( our_sok, 10 ) )
		{
			printf( "Cannot listen to socket, port %ld\n", our_port );
			exit( 1 );
		}

		l = sizeof( addr );

		tcp_gdk_input = gdk_input_add( our_sok, GDK_INPUT_READ, (GdkInputFunction) TCPAcceptIncoming, &sal );

		Connected = TRUE;

		connecting = show_wait( "Connecting to Server", pdata );
		gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 1, " Connecting to Server..." );

		while( gtk_events_pending() )
			gtk_main_iteration();

		if( !is_new_user )
			Login( sal.sok, UIN, &passwd[0], our_ip, our_port );
		else
			reg_new_user( sal.sok, passwd );

		next = time( NULL );
		next += 60;
	}

		gtk_main();

	Quit_ICQ( sal.sok );
	return 0;
}
