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

#include "datatype.h"
#include "gtkicq.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <time.h>
#include <string.h>

#include "gtkfunc.h"

#ifdef SOUND
#include "playsound.h"
#endif

/*
 * This function is called every 2 minutes
 * so that the server doesn't force disconnect
 */
void Keep_Alive( int sok )
{
	net_icq_pak pak;

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

	if( !Done_Login )
		return;

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_KEEP_ALIVE );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
/*
	packet_print( (pak.head.ver), sizeof( pak.head ) - 2,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &(pak.head.ver), sizeof( pak.head ) - 2 );
	last_cmd[(seq_num - 1) & 0x3ff ] = Chars_2_Word( pak.head.cmd );

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_KEEP_ALIVE2 );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
/*
	packet_print( (pak.head.ver), sizeof( pak.head ) - 2,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &(pak.head.ver), sizeof( pak.head ) - 2 );
	last_cmd[(seq_num - 1) & 0x3ff ] = Chars_2_Word( pak.head.cmd );
}

void Recv_Message( int sok, srv_net_icq_pak pak, struct sokandlb *data )
{
	RECV_MESSAGE_PTR r_mesg;
	unsigned short int year;

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

	r_mesg = ( RECV_MESSAGE_PTR )pak.data;
	last_recv_uin = Chars_2_DW( r_mesg->uin );

	memcpy( (BYTE *)(&year), &r_mesg->year[0], 2 );

/*
	printf( "%d %d %d %d %d\n", year,
	        r_mesg->month, r_mesg->day, r_mesg->hour, r_mesg->minute );
*/	
	Do_Msg( Chars_2_Word( r_mesg->type ), ( r_mesg->len + 2 ), last_recv_uin, data, 'm' );

	ack_srv( sok, Chars_2_Word( pak.head.seq ) );
}

/*
 * This is called to remove messages
 * from the server
 */
void snd_got_messages( int sok )
{
	net_icq_pak pak;

#ifdef TRACE_FUNCTION
	printf( "snd_got_messages\n" );
#endif
   
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_ACK_MESSAGES );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
/*
	packet_print( (pak.head.ver), sizeof( pak.head ) - 2,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &(pak.head.ver), sizeof( pak.head ) - 2 );
	last_cmd[ (seq_num - 1) & 0x3ff ] = Chars_2_Word( pak.head.cmd );
}

/* Sends contact list */
void snd_contact_list( int sok )
{
	net_icq_pak pak;
	int num_used;
	int i, size;
	char *tmp;

#ifdef TRACE_FUNCTION
	printf( "snd_contact_list\n" );
#endif
   
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_CONT_LIST );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
	tmp = pak.data;
	tmp++;

	for ( i = 0, num_used = 0; i < Num_Contacts; i++ )
	{
		if ( (S_DWORD) Contacts[ i ].uin >  0 )
		{
			DW_2_Chars( tmp, Contacts[ i ].uin );
			tmp += 4;
			num_used++;
		}
	}
	
	pak.data[0] = num_used;
	size = GPOINTER_TO_INT(tmp) - GPOINTER_TO_INT(pak.data);
	size += sizeof( pak.head ) - 2;
/*
	packet_print( (pak.head.ver), size,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &( pak.head.ver ), size );
	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
}

/* Send second login command to finish logging in */
void snd_login_1( int sok )
{
	net_icq_pak pak;

#ifdef TRACE_FUNCTION
	printf( "snd_login_1\n" );
#endif
   
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_LOGIN_1 );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
/*
	packet_print( (pak.head.ver), sizeof( pak.head ) - 2,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &(pak.head.ver), sizeof( pak.head ) - 2 );

	if( is_new_user )
		Update_User_Info( sok, &our_user_info );
}

/* Called when user goes Offline */
void User_Offline( int sok, srv_net_icq_pak pak )
{
	int remote_uin;
	int index;

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

	remote_uin = pak.data[3];
	remote_uin <<=8;
	remote_uin += pak.data[2];
	remote_uin <<=8;
	remote_uin += pak.data[1];
	remote_uin <<=8;
	remote_uin += pak.data[0];

	index = Print_UIN_Name( remote_uin );
	log_window_add( "Logout", 1, remote_uin );
	Time_Stamp();

	if ( index != -1 )
	{
		Contacts[ index ].status = STATUS_OFFLINE;
		Contacts[ index ].true_status = STATUS_OFFLINE;
		Contacts[ index ].last_time = time( NULL );
	}
	ack_srv( sok, Chars_2_Word( pak.head.seq ) );

}

void User_Online( int sok, srv_net_icq_pak pak )
{
	int remote_uin, new_status;
	int index;

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

/**************************************
dw uin
dw ip
word pord
word zero
dw ip
byte 04/06
dw status
word TCP version
**************************************/

	remote_uin = Chars_2_DW( &pak.data[0] );

	new_status = Chars_2_DW( &pak.data[17] ) & 0x00ff;
   
	index = Print_UIN_Name( remote_uin );
	log_window_add( "Login", 1, remote_uin );
	Time_Stamp();

	if ( index != -1 )
	{
		Contacts[ index ].status = new_status;
		Contacts[ index ].true_status = Chars_2_DW( &pak.data[17] );
		Contacts[ index ].current_ip = ntohl( Chars_2_DW( &pak.data[4] ) );
		Contacts[ index ].port = Chars_2_DW( &pak.data[8] );
		Contacts[ index ].last_time = time( NULL );
	}

	Time_Stamp();

	if( !Done_Login )
	{
		for ( index = 0; index < Num_Contacts; index++ )
		{
			if ( Contacts[index].uin == remote_uin )
			{
				Contacts[ index ].status = new_status;
				Contacts[ index ].current_ip = ntohl( Chars_2_DW( &pak.data[4] ) );
				Contacts[ index ].port = Chars_2_DW( &pak.data[8] );
				Contacts[ index ].last_time = time( NULL );
				break;
			}
		}
	 }
	ack_srv( sok, Chars_2_Word( pak.head.seq ) );

}

void Status_Update( int sok, srv_net_icq_pak pak )
{
	char buf[64];
	char sts[ 64 ];
	int remote_uin, new_status;
	int index;

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

	remote_uin = pak.data[3];
	remote_uin <<=8;
	remote_uin += pak.data[2];
	remote_uin <<=8;
	remote_uin += pak.data[1];
	remote_uin <<=8;
	remote_uin += pak.data[0];

	new_status = pak.data[7];
	new_status <<=8;
	new_status += pak.data[6];
	new_status <<=8;
	new_status += pak.data[5];
	new_status <<=8;
	new_status += pak.data[4];

	index = Print_UIN_Name( remote_uin );

	if( index != -1 )
		Contacts[ index ].true_status = new_status;

	new_status = new_status & 0x01ff;

	if( new_status == STATUS_OCCUPIED_MAC )
		new_status = STATUS_OCCUPIED;
	if( new_status == STATUS_NA_99A )
		new_status = STATUS_NA;

	if ( new_status == STATUS_OFFLINE ) /* this because -1 & 0xFFFF is not -1 */
	{
		strcpy( sts, "Offline" );
	}
   
	switch ( new_status & 0xffff )
	{
		case STATUS_ONLINE:
			strcpy( sts,  "Online" );
			break;
		case STATUS_DND:
			strcpy( sts, "Do Not Disturb" );
			break;
		case STATUS_AWAY:
			strcpy( sts, "Away" );
			break;
		case STATUS_OCCUPIED:
			strcpy( sts, "Occupied" );
			break;
		case STATUS_NA:
			strcpy( sts, "Not Available" );
			break;
		case STATUS_INVISIBLE:
			strcpy( sts, "Invisible" );
			break;
		case STATUS_FREE_CHAT:
			strcpy( sts, "Free for Chat" );
			break;
		default:
			strcpy( sts, "" );
			break;
	}

	sprintf( buf, "Status Change: %s", sts );
	log_window_add( buf, 1, remote_uin );

	if ( index != -1 )
	{
		Contacts[ index ].status = new_status;
	}

}


/*
 * Login with UIN and password.
 * Gives IP and Port
 * Does NOT wait for any kind of response
 */
void Login( int sok, int UIN, char *pass, int ip, int port )
{
	net_icq_pak pak;
	int size;
	login_1 s1;
	login_2 s2;
	struct sockaddr_in sin;  /* used to store inet addr stuff  */

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

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_LOGIN );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
	DW_2_Chars( s1.port, port );
	Word_2_Chars( s1.len, strlen( pass ) + 1 );
   
	DW_2_Chars( s2.ip, ip );
	sin.sin_addr.s_addr = Chars_2_DW( s2.ip );
	DW_2_Chars( s2.status, Current_Status );
	DW_2_Chars( s2.X1, LOGIN_X1_DEF );
	s2.X2[0] = LOGIN_X2_DEF;
	DW_2_Chars( s2.X3, LOGIN_X3_DEF );
	DW_2_Chars( s2.X4, LOGIN_X4_DEF );
	DW_2_Chars( s2.X5, LOGIN_X5_DEF );
   
	memcpy( pak.data, &s1, sizeof( s1 ) );
	size = sizeof( s1 );
	memcpy( &pak.data[size], pass, Chars_2_Word( s1.len ) );
	size += Chars_2_Word( s1.len );
	memcpy( &pak.data[size], &s2.X1, sizeof( s2.X1 ) );
	size += sizeof( s2.X1 );
	memcpy( &pak.data[size], &s2.ip, sizeof( s2.ip ) );
	size += sizeof( s2.ip );
	memcpy( &pak.data[size], &s2.X2, sizeof( s2.X2 ) );
	size += sizeof( s2.X2 );
	memcpy( &pak.data[size], &s2.status, sizeof( s2.status ) );
	size += sizeof( s2.status );
	memcpy( &pak.data[size], &s2.X3, sizeof( s2.X3 ) );
	size += sizeof( s2.X3 );
	memcpy( &pak.data[size], &s2.X4, sizeof( s2.X4 ) );
	size += sizeof( s2.X4 );
	memcpy( &pak.data[size], &s2.X5, sizeof( s2.X5 ) );
	size += sizeof( s2.X5 );
	last_cmd[ seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head )- 2 );
} 

/*
 * Sends aknowlegment to server
 * Appears that this must be done after
 * everything the server sends us
 */
void ack_srv( int sok, int seq )
{
	net_icq_pak pak;

#ifdef TRACE_FUNCTION
	printf( "ack_srv\n" );
#endif
   
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_ACK );
	Word_2_Chars( pak.head.seq, seq );
	DW_2_Chars( pak.head.UIN, UIN);
   
/*
	packet_print( (pak.head.ver), sizeof( pak.head ) - 2,
	              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
	SOCKWRITE( sok, &(pak.head.ver), sizeof( pak.head ) - 2 );
}

void Display_Info_Reply( int sok, srv_net_icq_pak pak, struct sokandlb *data )
{
	char buf[5][256];
	char *tmp;
	int len;
	int cx;

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

	sprintf( buf[0], "Info for %ld", Chars_2_DW( &pak.data [0] ) );

	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( Contacts[ cx ].uin == Chars_2_DW( &pak.data[ 0 ] ) )
			break;
	}

	len = Chars_2_Word( &pak.data[4] );
	sprintf( buf[1], "Nick Name : %s", &pak.data[6] );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_nick ), &pak.data[ 6 ] );

	if( atoi( Contacts[ cx ].nick ) == Contacts[ cx ].uin && Contacts[ cx ].uin )
	{
		strcpy( Contacts[ cx ].nick , &pak.data[6] );
		gtk_clist_set_text( GTK_CLIST( data->lb_userwin ),
		                    Contacts[ cx ].lb_index, 1, &pak.data[ 6 ] );
	}

	tmp = &pak.data[ 6 + len ];

	len = Chars_2_Word( tmp );
	sprintf( buf[2], "First name : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_fname ), tmp + 2 );
	tmp += len + 2;
	
	len = Chars_2_Word( tmp );
	sprintf( buf[3], "Last name : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_lname ), tmp + 2 );
	tmp += len + 2;
   
	len = Chars_2_Word( tmp );
	sprintf( buf[4], "Email Address : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_email ), tmp + 2 );
	tmp += len + 2;

	if ( *tmp == 1 )
	{
		gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_auth ),
		                    "No authorization needed." );
	}
	else
	{
		gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_auth ),
		                    "Must request authorization." );
	}

	ack_srv( sok, Chars_2_Word( pak.head.seq ) );
}

void Display_Search_Reply( int sok, srv_net_icq_pak pak )
{
	char *data[6];
	char dsr_uin[32];
	char auth_msg[32];
	char *titles[] = { "UIN", "Nick", "F. Name", "L. Name", "E-mail", "Authorization" };

	char *tmp;
	int len;
	int row;

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

	sprintf( dsr_uin, "%ld", Chars_2_DW( &pak.data[ 0 ] ) );
	data[ 0 ] = dsr_uin;
	len = Chars_2_Word( &pak.data[4] );

	/* Nick Name */
	data[ 1 ] = &pak.data[ 6 ];
	tmp = &pak.data[ 6 + len ];
	len = Chars_2_Word( tmp );
	
	/* First Name */
	data[ 2 ] = tmp + 2;
	tmp += len + 2;
	len = Chars_2_Word( tmp );
	
	/* Last Name */
	data[ 3 ] = tmp + 2;
	tmp += len + 2;
	len = Chars_2_Word( tmp );
	
	/* E-Mail Address */
	data[ 4 ] = tmp + 2;
	tmp += len + 2;

	if ( *tmp == 1 )
	{
		sprintf( auth_msg, "No authorization needed." );
	}
	else
	{
		sprintf( auth_msg, "Must request authorization." );
	}

	data[ 5 ] = auth_msg;

	if( !found_list )
	{
		found_list = gtk_clist_new_with_titles( 6, titles );
	}
	
	row = gtk_clist_append( GTK_CLIST( found_list ), data );

	gtk_clist_set_row_data( GTK_CLIST( found_list ), row, (gpointer)Chars_2_DW( &pak.data[ 0 ] ) );

	ack_srv( sok, Chars_2_Word( pak.head.seq ) );
}

int Do_Msg( DWORD type, char *data, DWORD uin, struct sokandlb *sldata, char message_type )
{
	char sender[50];
	char *tmp;
	char *logbuf;
	int cx = 0;

	struct sokandlb passdata;

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

	/* Mask off the 0x8xxx that's been causing problems */
	type &= 0xFF;

	passdata.sok = sldata->sok;
	passdata.lb_userwin = NULL;

	if ( type == USER_ADDED_MESS )
	{
		tmp = strchr( data, '\xFE' );
		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
		*tmp = 0;
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );

		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
		
		*tmp = 0;
		rus_conv ("wk",data);
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );
		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
	
		*tmp = 0;
		rus_conv ("wk",data);
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\x00' );
		*tmp = 0;
		rus_conv ("wk",data);
		Do_Msg( 0, data, uin, sldata, 'n' );
#ifdef SOUND
		if(RecvMessage && sound_toggle)
			playsound( RecvMessageSound );
#endif
	}
	else if ( type == AUTH_REQ_MESS )
	{
		tmp = strchr( data, '\xFE' );
		*tmp = 0;
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );

		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
		*tmp = 0;
		rus_conv ("wk",data);
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );
		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
		*tmp = 0;
		rus_conv ("wk",data);
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );
		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
	
		*tmp = 0;
		rus_conv ("wk",data);
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\xFE' );
		if ( tmp == NULL )
		{
			/* Bad Packet */
			return 0;
		}
	
		*tmp = 0;
		tmp++;
		data = tmp;
		tmp = strchr( tmp, '\x00' );
	
		if ( tmp == NULL )
		{
			return 0;
		}
		*tmp = 0;
		rus_conv ("wk",data);
		Do_Msg( 0, data, uin, sldata, 'a' );
#ifdef SOUND
		if(RecvMessage && sound_toggle)
			playsound( RecvMessageSound );
#endif
	}
	else if (type == URL_MESS)
	{

		Do_Msg( 0, data, uin, sldata, 'u' );

#ifdef SOUND
		if(RecvMessage && sound_toggle)
			playsound( RecvMessageSound );
#endif
	}
	else
	{
		if( uin != 0 )
		{
			for( cx = 0; cx < Num_Contacts; cx ++ )
			{
				if( uin == Contacts[ cx ].uin )
					break;
			}

			if( cx == Num_Contacts )
			{
				sprintf( Contacts[ cx ].nick, "%ld", uin );
				Add_User( sldata->sok, uin, Contacts[ cx ].nick );
			}

			strcpy( sender, Contacts[ cx ].nick );

			if( message_type == 'c' && Current_Status == STATUS_FREE_CHAT )
				return cx;

			if( Contacts[ cx ].read_next &&
			    GTK_IS_WIDGET( Contacts[ cx ].read_next ) &&
			    GTK_WIDGET_VISIBLE( Contacts[ cx ].read_next ) )
				gtk_widget_set_sensitive( Contacts[ cx ].read_next, TRUE );
		
			Contacts[ cx ].messages ++;
			Contacts[ cx ].need_update = 1;
			Show_Quick_Status( sldata );

			/* Add to personal history file */
			if( uin > 0 )
			{
				add_incoming_to_history( uin, data );
			}

			logbuf = (char *)malloc( 19 );
			sprintf( logbuf, "Received Message" );
			log_window_add( logbuf, 1, uin );
			free( logbuf );

			Contacts[ cx ].message = realloc( Contacts[ cx ].message, sizeof( char ** ) * Contacts[ cx ].messages );
			Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ] = (char *)malloc( strlen( data ) + 2 );
			Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ][ 0 ] = message_type;
			strcpy( ( Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ] + 1 ), data );

#ifdef SOUND
		if(RecvMessage && sound_toggle)
			playsound( RecvMessageSound );
#endif

		}
/*		else
		{
			system_messages ++;

			logbuf = (char *)malloc( sizeof( char ) * ( 19 + strlen( data ) ) );
			sprintf( logbuf, "Received System Message" );
			log_window_add( logbuf, 1, uin );
			free( logbuf );

			system_message = realloc( system_message, sizeof( char ** ) * system_messages );
			system_message[ system_messages - 1 ] = (char *)malloc( strlen( data ) + 1 );
			strcpy( system_message[ system_messages - 1 ], data );

#ifdef SOUND
		if(RecvMessage && sound_toggle)
			playsound( RecvMessageSound );
#endif

		}*/
	}
	return cx;
}

int Do_Chat( DWORD type, char *data, DWORD uin, struct sokandlb *sldata, DWORD seq )
{
	char sender[50];
	char *logbuf;
	int cx = 0;

	struct sokandlb passdata;

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

	passdata.sok = sldata->sok;
	passdata.lb_userwin = NULL;

	if( uin != 0 )
	{
		for( cx = 0; cx < Num_Contacts; cx ++ )
		{
			if( uin == Contacts[ cx ].uin )
				break;
		}

		if( cx == Num_Contacts )
		{
			sprintf( Contacts[ cx ].nick, "%ld", uin );
			Add_User( sldata->sok, uin, Contacts[ cx ].nick );
		}

		strcpy( sender, Contacts[ cx ].nick );
		if( Current_Status == STATUS_FREE_CHAT )
			return cx;

		Contacts[ cx ].messages ++;
		Show_Quick_Status( sldata );

		/* Add to personal history file */
		if( uin > 0 )
		{
			add_incoming_to_history( uin, data );
		}

		logbuf = (char *)malloc( 22 );
		sprintf( logbuf, "Received Chat Request" );
		log_window_add( logbuf, 1, uin );
		free( logbuf );

		Contacts[ cx ].chat_seq = seq;
		Contacts[ cx ].message = realloc( Contacts[ cx ].message, sizeof( char ** ) * Contacts[ cx ].messages );
		Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ] = (char *)malloc( strlen( data ) + 2 );
		Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ][ 0 ] = 'c';
		strcpy( ( Contacts[ cx ].message[ Contacts[ cx ].messages - 1 ] + 1 ), data );

#ifdef SOUND
		if(RecvChat && sound_toggle )
			playsound( RecvChatSound );
#endif
	}

	return cx;
}

void Display_Ext_Info_Reply( int sok, srv_net_icq_pak pak )
{
	char buf[7][512];
	char *tmp;
	int len;
	int cx;

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

	sprintf( buf[0], "Info for %ld", Chars_2_DW( &pak.data[ 0 ] ) );

	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( Contacts[ cx ].uin == Chars_2_DW( &pak.data[ 0 ] ) )
			break;
	}
	
	sprintf( buf[1], "%ld", Chars_2_DW( &pak.data[ 0 ] ) );

	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_uin ), buf[ 1 ] );

	len = Chars_2_Word( &pak.data[4] );
	sprintf( buf[1], "City : %s", &pak.data[6] );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_city ), &pak.data[ 6 ] );
	tmp = &pak.data[6 + len ];

	tmp += 2; /* Bypass COUNTRY_CODE */
	tmp += 1; /* Bypass COUNTRY_STATUS */

	len = Chars_2_Word( tmp );
	sprintf( buf[2], "State : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_state ), tmp + 2 );
	tmp += len + 2;

	tmp += 2; /* Bypass AGE */

	if( tmp[0] == 0x00 )
	{
		sprintf( buf[3], "Sex : Not specified" );
		gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_sex ), "Not specified" );
	}
	else if( tmp[0] == 0x01 )
	{
		sprintf( buf[3], "Sex : Female" );
		gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_sex ), "Female" );
	}
	else if( tmp[0] == 0x02 )
	{
		sprintf( buf[3], "Sex : Male" );
		gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_sex ), "Male" );
	}
	tmp += 1;

	len = Chars_2_Word( tmp );
	sprintf( buf[4], "Phone : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_phone ), tmp + 2 );
	tmp += len + 2;

	len = Chars_2_Word( tmp );
	sprintf( buf[5], "Home Page : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_homepage ), tmp + 2 );
	tmp += len + 2;

	len = Chars_2_Word( tmp );
	sprintf( buf[6], "About : %s", tmp+2 );
	gtk_entry_set_text( GTK_ENTRY( Contacts[ cx ].info_about ), tmp + 2 );

	ack_srv( sok, Chars_2_Word( pak.head.seq ) );
}


/*
 * Sends Invisible List that makes you
 * permanantly invisible to certain users
 */
void snd_invis_list( int sok )
{
	net_icq_pak pak;
	int num_used;
	int i, size;
	char *tmp;

#ifdef TRACE_FUNCTION
	printf( "snd_invis_list\n" );
#endif
	
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_INVIS_LIST );
	Word_2_Chars( pak.head.seq, seq_num );
	DW_2_Chars( pak.head.UIN, UIN );
	
	tmp = pak.data;
	tmp ++;
	for( i = 0, num_used = 0; i < Num_Contacts; i ++ )
	{
		if( (S_DWORD) Contacts[ i ].uin > 0 )
		{
			if( Contacts[ i ].invis_list )
			{
				DW_2_Chars( tmp, Contacts[ i ].uin );
				tmp += 4;
				num_used ++;
			}
		}
	}
	if( num_used )
	{
		pak.data[ 0 ] = num_used;
		size = GPOINTER_TO_INT(tmp) - GPOINTER_TO_INT(pak.data);
		size += sizeof( pak.head ) - 2;
		last_cmd[ seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
/*
		packet_print( (pak.head.ver), size,
		              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
		SOCKWRITE( sok, &(pak.head.ver), size );
		seq_num ++;
	}
}

/*
 * Sends Visible List that allows certain
 * users to see you if you are invisible
 */
void snd_vis_list( int sok )
{
	net_icq_pak pak;
	int num_used;
	int i, size;
	char *tmp;

#ifdef TRACE_FUNCTION
	printf( "snd_vis_list\n" );
#endif
	
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_VIS_LIST );
	Word_2_Chars( pak.head.seq, seq_num );
	DW_2_Chars( pak.head.UIN, UIN );
	
	tmp = pak.data;
	tmp ++;
	for( i = 0, num_used = 0; i < Num_Contacts; i ++ )
	{
		if( (S_DWORD) Contacts[ i ].uin > 0 )
		{
			if( Contacts[ i ].vis_list )
			{
				DW_2_Chars( tmp, Contacts[ i ].uin );
				tmp += 4;
				num_used ++;
			}
		}
	}
	if( num_used )
	{
		pak.data[ 0 ] = num_used;
		size = GPOINTER_TO_INT(tmp) - GPOINTER_TO_INT(pak.data);
		size += sizeof( pak.head ) - 2;
		last_cmd[ seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
/*
		packet_print( (pak.head.ver), size,
		              PACKET_TYPE_UDP | PACKET_DIRECTION_SEND );
*/
		SOCKWRITE( sok, &(pak.head.ver), size );
		seq_num ++;
	}
}
