#include "playsound.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gtkicq.h"
#include "datatype.h"
#include "dialog.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 <gdk/gdkkeysyms.h>
#include "gtkfunc.h"

#include "tcp.h"
#include "chatdlg.h"

typedef struct
{
	struct sokandlb *data;
	int i;
} dataandint;

const unsigned long int LOCALHOST = 0x0100007F;

int TCPChatReadServer( GtkWidget *widget, int sock, GdkInputCondition cond );
int TCPChatReadClient( GtkWidget *widget, int sock, GdkInputCondition cond );
int TCPChatHandshake( int cindex, int sock, GdkInputCondition cond );
int TCPConnectChat( DWORD port, DWORD uin, struct sokandlb *data );
int TCPAckPacket( int sock, int cindex, WORD cmd, int seq );
void TCPProcessPacket( BYTE *packet, int packet_length, int sock, struct sokandlb *data );
int TCPInitChannel( dataandint *data, int sock, GdkInputCondition cond );
int TCPGainConnection( DWORD ip, WORD port, int cindex, struct sokandlb *data );

void packet_print( BYTE *packet, int size, int type )
{
	int cx;

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

#ifndef DEBUG
	if( !packet_toggle )
		return;
#endif

	switch( type )
	{
		case ( PACKET_TYPE_TCP | PACKET_DIRECTION_SEND ):
			printf( "Sending packet (TCP):\n" );
			break;
		case( PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE ):
			printf( "Recieved packet (TCP):\n" );
			break;
		case ( PACKET_TYPE_UDP | PACKET_DIRECTION_SEND ):
			printf( "Sending packet (UDP):\n" );
			break;
		case( PACKET_TYPE_UDP | PACKET_DIRECTION_RECEIVE ):
			printf( "Recieved packet (UDP):\n" );
			break;
	}

	for( cx = 0; cx < size; cx ++ )
	{
		if( cx % 16 == 0 && cx )
			printf( "\n" );
		printf("%02x ", packet[cx] );
	}
	printf( "\n\n" );
}

void TCPTerminateChat( GtkWidget *widget, gpointer data )
{
	int sock = GPOINTER_TO_INT( data );
	int cx;
	char message[ 256 ];

#ifdef TRACE_FUNCTION
	printf( "TCPTerminateChat\n" );
#endif
	
	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( sock == Contacts[ cx ].chat_sok )
			break;
	}
	
	if( cx == Num_Contacts )
		return;

	if( Contacts[ cx ].chat_gdk_input )
	{
		gdk_input_remove( Contacts[ cx ].chat_gdk_input );
		Contacts[ cx ].chat_gdk_input = 0;
	}
	
	sprintf( message, "Chat Session terminated:\n%s", Contacts[ cx ].nick );
	OK_Box( message, "" );
	close( sock );
	if( Contacts[ cx ].chat_file != NULL )
	{
		fclose( Contacts[ cx ].chat_file );
		Contacts[ cx ].chat_file = NULL;
	}
	Contacts[ cx ].chat_sok = 0;
	Contacts[ cx ].chat_port = 0;
	Contacts[ cx ].chat_active = Contacts[ cx ].chat_active2 = FALSE;
}

int TCPChatSend( GtkWidget *widget, GdkEventKey *ev, int sock )
{
	char c;

	static char *oneline = NULL;

	static GtkStyle *style = NULL;
	GdkColor *foreground, *background;

	int cx;

#ifdef TRACE_FUNCTION
	printf( "TCPChatSend\n" );
#endif
	
	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( sock == Contacts[ cx ].chat_sok )
			break;
	}

	if( ev == NULL )
		return FALSE;

	if( oneline == NULL )
	{
		oneline = (char *)malloc( 1024 );
		strcpy( oneline, "" );
	}

	foreground = (GdkColor *)malloc( sizeof( GdkColor ) );
	background = (GdkColor *)malloc( sizeof( GdkColor ) );

	foreground->red = 256 * chat_fg_red;
	foreground->green = 256 * chat_fg_green;
	foreground->blue = 256 * chat_fg_blue;
	foreground->pixel = (gulong)(
	                    chat_fg_red * 65536 +
	                    chat_fg_green * 256 +
	                    chat_fg_blue );

	background->red = 256 * chat_bg_red;
	background->green = 256 * chat_bg_green;
	background->blue = 256 * chat_bg_blue;
	background->pixel = (gulong)(
	                    chat_bg_red * 65536 +
	                    chat_bg_green * 256 +
	                    chat_bg_blue );

	gdk_color_alloc( gtk_widget_get_colormap( widget ), foreground );
	gdk_color_alloc( gtk_widget_get_colormap( widget ), background );

	if( style == NULL )
	{
		style = gtk_style_new();
		memcpy( &style->fg[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
		memcpy( &style->text[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
		memcpy( &style->base[ GTK_STATE_NORMAL ], background, sizeof( GdkColor ) );
	}

	gtk_widget_set_style( widget, style );

/*	gtk_style_unref( style );*/

	switch( ev->keyval )
	{
		case GDK_Shift_L:
		case GDK_Shift_R:
		case GDK_Control_L:
		case GDK_Control_R:
		case GDK_Alt_L:
		case GDK_Alt_R:
			return FALSE;
		case 'g':
			if( ev->state & GDK_CONTROL_MASK )
			{
#ifdef SOUND
				playsound( "/usr/share/ICQ/Sounds/ChatBeep.au" );
#endif
				c = 0x07;
			}
			else
				c = ev->keyval;
			break;
		case GDK_Return:
			c = 0x0D;
			break;
		case GDK_BackSpace:
			c = 0x08;
			oneline[ strlen( oneline ) - 1 ] = 0x00;
			break;
		case GDK_Tab:
			c = 0x09;
			break;
		default:
			c = ev->keyval;
	}

	if( c != 0x07 && c != 0x08 && c != 0x09 && c != 0x0D &&
	    strlen( oneline ) < 1023 )
	{
		oneline[ strlen( oneline ) + 1 ] = 0x00;
		oneline[ strlen( oneline ) ] = c;
	}

	if( c == 0x0D )
	{
		if( Contacts[ cx ].chat_file != NULL )
		{
			fprintf( Contacts[ cx ].chat_file, "%s> %s\n", nickname, oneline );
			fflush( Contacts[ cx ].chat_file );
		}
		strcpy( oneline, "" );
	}

	write( sock, &c, 1 );
	return FALSE;

	free( foreground );
	free( background );
}

int TCPChatReadServer( GtkWidget *widget, int sock, GdkInputCondition cond )
{
	int cx;
	unsigned short packet_size;
	BYTE *packet;
	BYTE buffer[1024];
	BYTE c;
	BYTE zero;

	WORD font_length, font_family;
	DWORD font_style;

	char *font_name = "Arial";
	WORD font_size = 12;

	char message[256];

	static GtkStyle *style;
	GdkColor *background;
	GdkColor *foreground;

	typedef struct
	{
		BYTE version[4];
		BYTE chat_port[4];
		BYTE ip_local[4];
		BYTE ip_remote[4];
		BYTE four;
		BYTE our_port[2];
		BYTE font_size[4];
		BYTE font_face[4];
		BYTE font_length[2];
	} begin_chat_a;
	
	typedef struct
	{
		BYTE one[2];
	} begin_chat_b;

	typedef struct
	{
		DWORD code;
		DWORD uin;
		WORD name_length;
		BYTE foreground[4];
		BYTE background[4];
		BYTE version[4];
		BYTE chat_port[4];
		BYTE ip_local[4];
		BYTE ip_remote[4];
		BYTE four;
		BYTE our_port[2];
		BYTE font_size[4];
		BYTE font_face[4];
		BYTE font_length[2];
	} read_pak;

	static char *oneline;
	
	read_pak rpak;
	begin_chat_a paka;
	begin_chat_b pakb;

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

	if( oneline == NULL )
	{
		oneline = (char *)malloc( 1024 );
		strcpy( oneline, "" );
	}

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

	if( Contacts[ cx ].chat_active == FALSE )
	{
		read( sock, (char*)(&packet_size), 1 );
		read( sock, (char*)(&packet_size) + 1, 1 );

		packet = (BYTE *)malloc( packet_size );
		read( sock, packet, packet_size );
		packet_print( packet, packet_size,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE );

		memcpy( &rpak.code, packet, 4 );
		memcpy( &rpak.uin, packet + 4, 4 );

		memcpy( &rpak.name_length, packet + 8, 2 );
		/* Read in name here */
		memcpy( &rpak.foreground, ( packet + 10 + GPOINTER_TO_INT( rpak.name_length ) ), 4 );
		memcpy( &rpak.background, ( packet + 14 + GPOINTER_TO_INT( rpak.name_length ) ), 4 );
		Contacts[ cx ].chat_fg_red = rpak.foreground[0];
		Contacts[ cx ].chat_fg_green = rpak.foreground[1];
		Contacts[ cx ].chat_fg_blue = rpak.foreground[2];
		Contacts[ cx ].chat_bg_red = rpak.background[0];
		Contacts[ cx ].chat_bg_green = rpak.background[1];
		Contacts[ cx ].chat_bg_blue = rpak.background[2];

		DW_2_Chars( paka.version, 0x00000004 );
		DW_2_Chars( paka.chat_port, Contacts[ cx ].chat_port );
		DW_2_Chars( paka.ip_local, LOCALHOST );
		DW_2_Chars( paka.ip_remote, LOCALHOST );
		paka.four = 0x04;
		Word_2_Chars( paka.our_port, our_port );
		DW_2_Chars( paka.font_size, font_size );
		DW_2_Chars( paka.font_face, FONT_PLAIN );
		Word_2_Chars( paka.font_length, strlen( font_name ) + 1 );

		Word_2_Chars( pakb.one, 0x0001 );

		packet_size = sizeof( begin_chat_a ) + sizeof( begin_chat_b ) + strlen( font_name ) + 1;
		packet = (BYTE *)malloc( packet_size );
		memcpy( &buffer[0], &packet_size, 2 );
		memcpy( &buffer[2], &paka, sizeof( begin_chat_a ) );
		memcpy( &buffer[2 + sizeof( begin_chat_a )], font_name, strlen( font_name ) + 1 );
		memcpy( &buffer[3 + sizeof( begin_chat_a ) + strlen( font_name )], &pakb, sizeof( begin_chat_b ) );
		write( sock, buffer, packet_size + 2 );
		packet_print( buffer, packet_size + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );

		free( packet );

		Contacts[ cx ].chat_active = TRUE;
	}
	else
	{
		foreground = (GdkColor *)malloc( sizeof( GdkColor ) );
		background = (GdkColor *)malloc( sizeof( GdkColor ) );
		
		foreground->red = 256 * Contacts[ cx ].chat_fg_red;
		foreground->green = 256 * Contacts[ cx ].chat_fg_green;
		foreground->blue = 256 * Contacts[ cx ].chat_fg_blue;
		foreground->pixel = (gulong)(
		                    Contacts[ cx ].chat_fg_red * 65536 +
		                    Contacts[ cx ].chat_fg_green * 256 +
		                    Contacts[ cx ].chat_fg_blue );
		
		background->red = 256 * Contacts[ cx ].chat_bg_red;
		background->green = 256 * Contacts[ cx ].chat_bg_green;
		background->blue = 256 * Contacts[ cx ].chat_bg_blue;
		background->pixel = (gulong)(
		                    Contacts[ cx ].chat_bg_red * 65536 +
		                    Contacts[ cx ].chat_bg_green * 256 +
		                    Contacts[ cx ].chat_bg_blue );

		gdk_color_alloc( gtk_widget_get_colormap( widget ), foreground );
		gdk_color_alloc( gtk_widget_get_colormap( widget ), background );

		if( style == NULL )
		{
			style = gtk_style_new();
			memcpy( &style->fg[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
			memcpy( &style->text[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
			memcpy( &style->base[ GTK_STATE_NORMAL ], background, sizeof( GdkColor ) );
		}

		gtk_widget_set_style( widget, style );

/*		gtk_style_unref( style );*/

		if( ( read( sock, &c, 1 ) ) <= 0 )
		{
			if( Contacts[ cx ].chat_gdk_input )
			{
				gdk_input_remove( Contacts[ cx ].chat_gdk_input );
				Contacts[ cx ].chat_gdk_input = 0;
			}
			sprintf( message, "Chat Session terminated:\n%s", Contacts[ cx ].nick );
			OK_Box( message, "" );
			close( sock );
			Contacts[ cx ].chat_sok = 0;
			Contacts[ cx ].chat_port = 0;
			Contacts[ cx ].chat_active = Contacts[ cx ].chat_active2 = FALSE;
			return TRUE;
		}

		if( ChatFont == NULL )
			ChatFont = gdk_font_load( ChatFontString );
		switch( c )
		{
			case 0x00: /* Change foreground color */
				read( sock, &Contacts[ cx ].chat_fg_red, 1 );
				read( sock, &Contacts[ cx ].chat_fg_green, 1 );
				read( sock, &Contacts[ cx ].chat_fg_blue, 1 );
				read( sock, &zero, 1 );
				return FALSE;

			case 0x01: /* Change background color */
				read( sock, &Contacts[ cx ].chat_bg_red, 1 );
				read( sock, &Contacts[ cx ].chat_bg_green, 1 );
				read( sock, &Contacts[ cx ].chat_bg_blue, 1 );
				read( sock, &zero, 1 );
				return FALSE;

			case 0x03: /* Sent often for no apparent reason */
			case 0x04:
				return FALSE;

			case 0x07: /* Beep */
#ifdef SOUND
				playsound( "/usr/share/ICQ/Sounds/ChatBeep.au" );
#endif
				return FALSE;

			case 0x08: /* Backspace */
				oneline[ strlen( oneline ) - 1 ] = 0x00;
				gtk_text_backward_delete( GTK_TEXT( widget ), 1 );
				break;

			case 0x0D: /* CR/LF */
				if( Contacts[ cx ].chat_file != NULL )
				{
					fprintf( Contacts[ cx ].chat_file, "%s> %s\n", Contacts[ cx ].nick, oneline );
					fflush( Contacts[ cx ].chat_file );
				}
				strcpy( oneline, "" );

				gtk_text_insert( GTK_TEXT( widget ), NULL, foreground, background, "\n", 1 );
				break;

			case 0x10: /* Change font */
				read( sock, &font_length, 2 );
				font_name = (char*)malloc( font_length );
				read( sock, font_name, font_length );
				read( sock, &font_family, 2 );
				free( font_name );
				return FALSE;

			case 0x11: /* Change font style */
				read( sock, &font_style, 4 );
				return FALSE;

			case 0x12: /* Change font size */
				read( sock, &font_size, 4 );
				return FALSE;

			default: /* Any other characters */
				if( c != 0x07 && c != 0x08 && c != 0x09 && c != 0x0D &&
	   			 strlen( oneline ) < 1023 )
				{
					oneline[ strlen( oneline ) + 1 ] = 0x00;
					oneline[ strlen( oneline ) ] = c;
				}

				gtk_text_insert( GTK_TEXT( widget ), ChatFont, foreground, background, &c, 1 );
				break;
		}
	}
	return TRUE;

	free( foreground );
	free( background );
}

int TCPChatReadClient( GtkWidget *widget, int sock, GdkInputCondition cond )
{
	int cx;
	unsigned short packet_size;
	BYTE *packet;
	BYTE buffer[1024];
	BYTE c;
	BYTE zero;

	WORD font_length;
	WORD font_family;
	DWORD font_style;

	char *font_name = "Arial";
	DWORD font_size = 12;

	char message[256];

	static GtkStyle *style;
	GdkColor *background;
	GdkColor *foreground;

	static char *oneline = NULL;

	typedef struct
	{
		BYTE code[4];
		BYTE uin[4];
		BYTE name_length[2];
	} begin_chat_a;
	
	typedef struct
	{
		BYTE foreground[4];
		BYTE background[4];
		BYTE version[4];
		BYTE chat_port[4];
		BYTE ip_local[4];
		BYTE ip_remote[4];
		BYTE four;
		BYTE our_port[2];
		BYTE font_size[4];
		BYTE font_face[4];
		BYTE font_length[2];
	} begin_chat_b;
	
	typedef struct
	{
		BYTE zeroa[2];
		BYTE zerob;
	} begin_chat_c;

	begin_chat_a paka;
	begin_chat_b pakb;
	begin_chat_c pakc;

	WORD name_length;

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

	if( oneline == NULL )
	{
		oneline = (char *)malloc( 1024 );
		strcpy( oneline, "" );
	}

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

	if( Contacts[ cx ].chat_active == FALSE )
	{
		read( sock, (char*)(&packet_size), 1 );
		read( sock, (char*)(&packet_size) + 1, 1 );

		packet = (BYTE *)malloc( packet_size );
		read( sock, packet, packet_size );
		packet_print( packet, packet_size,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE );

		if( Contacts[ cx ].chat_active2 == TRUE )
		{
			/* Put here interpretation of 0x040000000 packet */
		}

		free( packet );
	
		DW_2_Chars( paka.code, 0x00000064 );
		DW_2_Chars( paka.uin, UIN );
		Word_2_Chars( paka.name_length, strlen( nickname ) + 1 );
		DW_2_Chars( pakb.foreground, 0x00FFFFFF );
		DW_2_Chars( pakb.background, 0x00000000 );
		DW_2_Chars( pakb.version, 0x00000004 );
		DW_2_Chars( pakb.chat_port, Contacts[ cx ].chat_port );
		DW_2_Chars( pakb.ip_local, LOCALHOST );
		DW_2_Chars( pakb.ip_remote, LOCALHOST );
		pakb.four = 0x04;
		Word_2_Chars( pakb.our_port, our_port );
		DW_2_Chars( pakb.font_size, font_size );
		DW_2_Chars( pakb.font_face, FONT_PLAIN );
		Word_2_Chars( pakb.font_length, strlen( font_name ) + 1 );
		Word_2_Chars( pakc.zeroa, 0x0000 );
		pakc.zerob = 0x00;

		if( Contacts[ cx ].chat_active2 == FALSE )
		{
			read( sock, (char*)(&packet_size), 1 );
			read( sock, (char*)(&packet_size) + 1, 1 );
			if( packet_size < 1024 )
			{
				packet = (BYTE *)malloc( packet_size );
				read( sock, packet, packet_size );
				packet_print( packet, packet_size,
				              PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE );

				memcpy( &paka.code, packet, 4 );
				memcpy( &paka.uin, packet + 8, 4 );
				memcpy( &name_length, packet + 12, 2 );
				/* Read in name here */
				memcpy( &pakb.foreground, packet + 16 + GPOINTER_TO_INT( name_length ), 4 );
				memcpy( &pakb.background, packet + 20 + GPOINTER_TO_INT( name_length ), 4 );
				Contacts[ cx ].chat_fg_red = pakb.foreground[0];
				Contacts[ cx ].chat_fg_green = pakb.foreground[1];
				Contacts[ cx ].chat_fg_blue = pakb.foreground[2];
				Contacts[ cx ].chat_bg_red = pakb.background[0];
				Contacts[ cx ].chat_bg_green = pakb.background[1];
				Contacts[ cx ].chat_bg_blue = pakb.background[2];

				free( packet );
			}

			packet_size = sizeof( begin_chat_a ) + sizeof( begin_chat_b ) + sizeof( begin_chat_c ) + strlen( nickname ) + 1 + strlen( font_name ) + 1;
			packet = (BYTE *)malloc( packet_size );
			memcpy( &buffer[0], &packet_size, 2 );
			memcpy( &buffer[2], &paka, sizeof( begin_chat_a ) );
			memcpy( &buffer[2 + sizeof( begin_chat_a )], nickname, strlen( nickname ) + 1 );
			memcpy( &buffer[3 + sizeof( begin_chat_a ) + strlen( nickname )], &pakb, sizeof( begin_chat_b ) );
			memcpy( &buffer[3 + sizeof( begin_chat_a ) + strlen( nickname ) + sizeof( begin_chat_b )], font_name, strlen( font_name ) + 1 );
			memcpy( &buffer[4 + sizeof( begin_chat_a ) + strlen( nickname ) + sizeof( begin_chat_b ) + strlen( font_name )], &pakc, sizeof( begin_chat_c ) );
			write( sock, buffer, packet_size + 2 );
			packet_print( buffer, packet_size + 2,
			              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );

			free( packet );
			Contacts[ cx ].chat_active2 = TRUE;
		}
		else
			Contacts[ cx ].chat_active = TRUE;
	}
	else
	{
		foreground = (GdkColor *)malloc( sizeof( GdkColor ) );
		background = (GdkColor *)malloc( sizeof( GdkColor ) );
		
		foreground->red = 256 * Contacts[ cx ].chat_fg_red;
		foreground->green = 256 * Contacts[ cx ].chat_fg_green;
		foreground->blue = 256 * Contacts[ cx ].chat_fg_blue;
		foreground->pixel = (gulong)(
		                    Contacts[ cx ].chat_fg_red * 65536 +
		                    Contacts[ cx ].chat_fg_green * 256 +
		                    Contacts[ cx ].chat_fg_blue );
		
		background->red = 256 * Contacts[ cx ].chat_bg_red;
		background->green = 256 * Contacts[ cx ].chat_bg_green;
		background->blue = 256 * Contacts[ cx ].chat_bg_blue;
		background->pixel = (gulong)(
		                    Contacts[ cx ].chat_bg_red * 65536 +
		                    Contacts[ cx ].chat_bg_green * 256 +
		                    Contacts[ cx ].chat_bg_blue );

		gdk_color_alloc( gtk_widget_get_colormap( widget ), foreground );
		gdk_color_alloc( gtk_widget_get_colormap( widget ), background );

		style = gtk_style_new();
		memcpy( &style->fg[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
		memcpy( &style->text[ GTK_STATE_NORMAL ], foreground, sizeof( GdkColor ) );
		memcpy( &style->base[ GTK_STATE_NORMAL ], background, sizeof( GdkColor ) );

		gtk_widget_set_style( widget, style );

		gtk_style_unref( style );

		if( ( read( sock, &c, 1 ) ) <= 0 )
		{
			if( Contacts[ cx ].chat_gdk_input )
			{
				gdk_input_remove( Contacts[ cx ].chat_gdk_input );
				Contacts[ cx ].chat_gdk_input = 0;
			}
			sprintf( message, "Chat Session terminated:\n%s", Contacts[ cx ].nick );
			OK_Box( message, "" );
			close( sock );
			Contacts[ cx ].chat_sok = 0;
			Contacts[ cx ].chat_port = 0;
			Contacts[ cx ].chat_active = Contacts[ cx ].chat_active2 = FALSE;
			return TRUE;
		}

		if( ChatFont == NULL )
			ChatFont = gdk_font_load( ChatFontString );
		switch( c )
		{
			case 0x00: /* Change foreground color */
				read( sock, &Contacts[ cx ].chat_fg_red, 1 );
				read( sock, &Contacts[ cx ].chat_fg_green, 1 );
				read( sock, &Contacts[ cx ].chat_fg_blue, 1 );
				read( sock, &zero, 1 );
				return FALSE;

			case 0x01: /* Change background color */
				read( sock, &Contacts[ cx ].chat_bg_red, 1 );
				read( sock, &Contacts[ cx ].chat_bg_green, 1 );
				read( sock, &Contacts[ cx ].chat_bg_blue, 1 );
				read( sock, &zero, 1 );
				return FALSE;

			case 0x03: /* Sent often for no apparent reason */
			case 0x04:
				return FALSE;

			case 0x07: /* Beep */
#ifdef SOUND
				playsound( "ChatBeep.au" );
#endif
				return FALSE;

			case 0x08: /* Backspace */
				oneline[ strlen( oneline ) - 1 ] = 0x00;
				gtk_text_backward_delete( GTK_TEXT( widget ), 1 );
				break;

			case 0x0D: /* CR/LF */
				if( Contacts[ cx ].chat_file != NULL )
				{
					fprintf( Contacts[ cx ].chat_file, "%s> %s\n", Contacts[ cx ].nick, oneline );
					fflush( Contacts[ cx ].chat_file );
				}
				strcpy( oneline, "" );

				gtk_text_insert( GTK_TEXT( widget ), NULL, foreground, background, "\n", 1 );
				break;

			case 0x10: /* Change font */
				read( sock, &font_length, 2 );
				font_name = (char*)malloc( font_length );
				read( sock, font_name, font_length );
				read( sock, &font_family, 2 );
				free( font_name );
				return FALSE;

			case 0x11: /* Change font style */
				read( sock, &font_style, 4 );
				return FALSE;

			case 0x12: /* Change font size */
				read( sock, &font_size, 4 );
				return FALSE;

			default: /* Any other characters */
				if( c != 0x07 && c != 0x08 && c != 0x09 && c != 0x0D &&
	   			 strlen( oneline ) < 1023 )
				{
					oneline[ strlen( oneline ) + 1 ] = 0x00;
					oneline[ strlen( oneline ) ] = c;
				}

				gtk_text_insert( GTK_TEXT( widget ), ChatFont, foreground, background, &c, 1 );
				break;
		}
	}

	return TRUE;
}

int TCPChatHandshake( int cindex, int sock, GdkInputCondition cond )
{
	GtkWidget *textbox;
	int new_sock;
	int size = sizeof( struct sockaddr );
	struct sockaddr_in their_addr;

#ifdef TRACE_FUNCTION
	printf( "TCPChatHandshake\n" );
#endif
	
	new_sock = accept( sock, ( struct sockaddr * )&their_addr, &size );
	Contacts[ cindex ].chat_sok = new_sock;
	Contacts[ cindex ].chat_port = ntohs( their_addr.sin_port );

	textbox = ChatWindowNew( cindex, new_sock );

	if( Contacts[ cindex ].chat_gdk_input )
		gtk_input_remove( Contacts[ cindex ].chat_gdk_input );

	fcntl( new_sock, O_NONBLOCK );
	Contacts[ cindex ].chat_gdk_input = gdk_input_add( new_sock, GDK_INPUT_READ, (GdkInputFunction) TCPChatReadClient, textbox );
	return TRUE;
}

int TCPConnectChat( DWORD port, DWORD uin, struct sokandlb *data )
{
	GtkWidget *textbox;
	DWORD localport;
	struct sockaddr_in local, remote;
	int sizeofSockaddr = sizeof( struct sockaddr );
	int sock;
	int cindex;
	DWORD ip;
	unsigned short size;
	BYTE buffer[1024];

	typedef struct
	{
		BYTE code;
		BYTE version[4];
		BYTE chat_porta[4];
		BYTE uin[4];
		BYTE ip_local[4];
		BYTE ip_remote[4];
		BYTE four;
		BYTE chat_portb[4];
	} handshake_a;

	typedef struct
	{
		BYTE code[4];
		BYTE biga[4];
		BYTE uin[4];
		BYTE name_length[2];
	} handshake_b;
	
	typedef struct
	{
		BYTE revporta;
		BYTE revportb;
		BYTE foreground[4];
		BYTE background[4];
		BYTE zero;
	} handshake_c;

	handshake_a hsa;
	handshake_b hsb;
	handshake_c hsc;

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

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

	if( Contacts[ cindex ].chat_sok > 0 )
		return Contacts[ cindex ].chat_sok;

	ip = Contacts[ cindex ].current_ip;

	if( ip == 0 )
	{
		return -1;
	}

	sock = socket( AF_INET, SOCK_STREAM, 0 );
	if( sock == -1 )
	{
		return -1;
	}

	fcntl( sock, O_NONBLOCK );

	bzero( &local.sin_zero, 8 );
	bzero( &remote.sin_zero, 8 );
	
	local.sin_family = AF_INET;
	remote.sin_family = AF_INET;
	local.sin_port = htons( 0 );
	local.sin_addr.s_addr = htonl( INADDR_ANY );

	remote.sin_port = htons( port );
	remote.sin_addr.s_addr = htonl( ip );

	if( connect( sock, (struct sockaddr *)&remote, sizeofSockaddr ) < 0 )
	{
		return -1;
	}

	getsockname( sock, (struct sockaddr*)&local, &sizeofSockaddr );
	localport = ntohs( local.sin_port );

	textbox = ChatWindowNew( cindex, sock );

	fcntl( sock, O_NONBLOCK );
	Contacts[ cindex ].chat_gdk_input = gdk_input_add( sock, GDK_INPUT_READ, (GdkInputFunction) TCPChatReadServer, textbox );

	Contacts[ cindex ].chat_sok = sock;

	hsa.code = 0xFF;
	DW_2_Chars( hsa.version, 0x00000004 );
	DW_2_Chars( hsa.uin, UIN );
	DW_2_Chars( hsa.ip_local, LOCALHOST );
	DW_2_Chars( hsa.ip_remote, LOCALHOST );
	hsa.four = 0x04;
	DW_2_Chars( hsa.chat_portb, (DWORD)localport );
	DW_2_Chars( hsa.chat_porta, (DWORD)localport );
	
	size = sizeof( handshake_a );
	memcpy( &buffer[0], &size, 2 );
	memcpy( &buffer[2], &hsa, size );
	write( sock, buffer, size + 2 );
	packet_print( buffer, size + 2,
	              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	
	DW_2_Chars( hsb.code, 0x00000064 );
	DW_2_Chars( hsb.biga, 0xFFFFFFFD );
	DW_2_Chars( hsb.uin, UIN );
	Word_2_Chars( hsb.name_length, strlen( nickname ) + 1 );
	hsc.revporta = ((char *)(&localport))[1];
	hsc.revportb = ((char *)(&localport))[0];
	DW_2_Chars( hsc.foreground, 0x00FFFFFF );
	DW_2_Chars( hsc.background, 0x00000000 );
	hsc.zero = 0x00;

	size = sizeof( handshake_b ) + sizeof( handshake_c ) + strlen( nickname );
	bzero( (void *)buffer, 1024 );
	memcpy( &buffer[0], &size, 2 );
	memcpy( &buffer[2], &hsb, sizeof( handshake_b ) );
	memcpy( &buffer[ 2 + sizeof( handshake_b ) ], nickname, strlen( nickname ) + 1 );
	memcpy( &buffer[ 3 + sizeof( handshake_b ) + strlen( nickname ) ], &hsc, sizeof( handshake_c ) );
	write( sock, buffer, size + 2 );
	packet_print( buffer, size + 2,
	              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );

	return sock;
}

int TCPRefuseChat( int sock, int cindex, DWORD seq )
{
	char buffer[1024];
	unsigned short intsize;

	typedef struct
	{
		BYTE uin1[4];
		BYTE version[2];
		BYTE command[2];
		BYTE zero[2];
		BYTE uin2[4];
		BYTE cmd[2];
		BYTE message_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE ip_real[4];
		BYTE porta[4];
		BYTE junk;
		BYTE status[4];
		BYTE zeroa[4];
		BYTE zerob[4];
		BYTE zeroc[2];
		BYTE zerod;
		BYTE seq[4];
	} tcp_tail;

	tcp_head pack_head;
	tcp_tail pack_tail;

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

	DW_2_Chars( pack_head.uin1, UIN );
	Word_2_Chars( pack_head.version, 0x0003 );
	Word_2_Chars( pack_head.command, ICQ_CMDxTCP_ACK );
	Word_2_Chars( pack_head.zero, 0x0000 );
	DW_2_Chars( pack_head.uin2, UIN );
	DW_2_Chars( pack_head.cmd, ICQ_CMDxTCP_CHAT );
	DW_2_Chars( pack_head.message_length, 1 );
	
	DW_2_Chars( pack_tail.ip, our_ip );
	DW_2_Chars( pack_tail.ip_real, LOCALHOST );
	DW_2_Chars( pack_tail.porta, our_port );
	pack_tail.junk = 0x04;
	DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_REFUSE );

	DW_2_Chars( pack_tail.zeroa, 0x00000001 );
	DW_2_Chars( pack_tail.zerob, 0x00000000 );
	DW_2_Chars( pack_tail.zeroc, 0x00000000 );
	pack_tail.zerod = 0x00;
	DW_2_Chars( pack_tail.seq, seq );

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + 1;
		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &pack_head, sizeof( pack_head ) );
		buffer[2 + sizeof( pack_head )] = 0x00;
		memcpy( &buffer[2 + sizeof( pack_head ) + 1 ],
		        &pack_tail, sizeof( pack_tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return -1;
	}
	return 1;
}

int TCPAcceptChat( int sock, int cindex, DWORD seq )
{
	char buffer[1024];
	unsigned short intsize;
	int chat_sock;
	unsigned short chat_port, backport, tempport;
	int length = sizeof( struct sockaddr );

	typedef struct
	{
		BYTE uin1[4];
		BYTE version[2];
		BYTE command[2];
		BYTE zero[2];
		BYTE uin2[4];
		BYTE cmd[2];
		BYTE message_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE ip_real[4];
		BYTE porta[4];
		BYTE junk;
		BYTE status[4];
		BYTE one[2];
		BYTE zero;
		BYTE back_port[4];
		BYTE portb[4];
		BYTE seq[4];
	} tcp_tail;

	tcp_head pack_head;
	tcp_tail pack_tail;

	struct sockaddr_in my_addr;

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

	chat_sock = socket( AF_INET, SOCK_STREAM, 0 );
	if( chat_sock <= 0 )
		return FALSE;

	fcntl( chat_sock, O_NONBLOCK );
	
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = 0;
	my_addr.sin_addr.s_addr = INADDR_ANY;
	bzero(&(my_addr.sin_zero), 8);
	
	if( bind( chat_sock, (struct sockaddr *)&my_addr, sizeof( struct sockaddr ) ) == -1 )
		return FALSE;

	listen( chat_sock, 1 );
	Contacts[ cindex ].chat_gdk_input =
		gdk_input_add( chat_sock, GDK_INPUT_READ,
			       (GdkInputFunction) TCPChatHandshake,
			       GINT_TO_POINTER( cindex ) );

	getsockname( chat_sock, ( struct sockaddr * ) &my_addr, &length );
	
	chat_port = ntohs( my_addr.sin_port );

	tempport = chat_port;
	backport = ( tempport >> 8 ) + ( tempport << 8 );

	DW_2_Chars( pack_head.uin1, UIN );
	Word_2_Chars( pack_head.version, 0x0003 );
	Word_2_Chars( pack_head.command, ICQ_CMDxTCP_ACK );
	Word_2_Chars( pack_head.zero, 0x0000 );
	DW_2_Chars( pack_head.uin2, UIN );
	DW_2_Chars( pack_head.cmd, ICQ_CMDxTCP_CHAT );
	DW_2_Chars( pack_head.message_length, 1 );

	DW_2_Chars( pack_tail.ip, 0x00000000 );
	DW_2_Chars( pack_tail.ip_real, LOCALHOST );
	DW_2_Chars( pack_tail.porta, our_port );
	pack_tail.junk = 0x04;
	DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_ONLINE );

	DW_2_Chars( pack_tail.one, 0x0001 );
	pack_tail.zero = 0x00;
	DW_2_Chars( pack_tail.back_port, backport );
	DW_2_Chars( pack_tail.portb, chat_port );
	DW_2_Chars( pack_tail.seq, seq );
	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + 1;
		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &pack_head, sizeof( pack_head ) );
		buffer[2 + sizeof( pack_head )] = 0x00;
		memcpy( &buffer[2 + sizeof( pack_head ) + 1 ],
		        &pack_tail, sizeof( pack_tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return -1;
	}

	return 1;
}

int TCPAckPacket( int sock, int cindex, WORD cmd, int seq )
{
	char buffer[1024];
	unsigned short intsize;
	char *sent_message;

	typedef struct
	{
		BYTE uin1[4];
		BYTE version[2];
		BYTE command[2];
		BYTE zero[2];
		BYTE uin2[4];
		BYTE cmd[2];
		BYTE message_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE ip_real[4];
		BYTE port[4];
		BYTE junk;
		BYTE status[4];
		BYTE seq[4];
	} tcp_tail;

	tcp_head pack_head;
	tcp_tail pack_tail;

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

	sent_message = "";
	if( Current_Status != STATUS_ONLINE && Current_Status != STATUS_FREE_CHAT )
		sent_message = Away_Message;
	
	DW_2_Chars( pack_head.uin1, UIN );
	Word_2_Chars( pack_head.version, 0x0003 );
	Word_2_Chars( pack_head.command, ICQ_CMDxTCP_ACK );
	Word_2_Chars( pack_head.zero, 0x0000 );
	DW_2_Chars( pack_head.uin2, UIN );
	DW_2_Chars( pack_head.cmd, cmd );
	DW_2_Chars( pack_head.message_length, strlen( sent_message ) + 1 );
	
	DW_2_Chars( pack_tail.ip, our_ip );
	DW_2_Chars( pack_tail.ip_real, LOCALHOST );
	DW_2_Chars( pack_tail.port, our_port );
	pack_tail.junk = 0x04;
	DW_2_Chars( pack_tail.seq, seq );

	switch( Current_Status )
	{
		case STATUS_ONLINE:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_ONLINE );
			break;
		case STATUS_AWAY:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_AWAY );
			break;
		case STATUS_DND:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_DND );
			break;
		case STATUS_OCCUPIED:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_OCC );
			break;
		case STATUS_NA:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_NA );
			break;
		case STATUS_INVISIBLE:
			DW_2_Chars( pack_tail.status, ICQ_ACKxTCP_REFUSE );
			break;
		}

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + strlen( sent_message ) + 1;
		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &pack_head, sizeof( pack_head ) );
		memcpy( &buffer[2 + sizeof( pack_head )], sent_message,
		        strlen( sent_message ) + 1 );
		memcpy( &buffer[2 + sizeof( pack_head ) + strlen( sent_message ) + 1 ],
		        &pack_tail, sizeof( pack_tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return -1;
	}
	return 1;
}


void TCPProcessPacket( BYTE *packet, int packet_length, int sock, struct sokandlb *data )
{
	int chat_sock;
	int cx;

	typedef struct
	{
		DWORD uin1;
		WORD version;
		WORD command;
		WORD zero;
		DWORD uin2;
		WORD cmd;
		WORD message_length;
	} tcp_head;

	typedef struct
	{
		DWORD ip_sender;
		DWORD ip_local;
		DWORD port;
		BYTE junk;
		DWORD status;
		DWORD chat_port;
		DWORD seq;
	} tcp_tail;

	char *message;
	char *away_message;
	tcp_head pack_head;
	tcp_tail pack_tail;

	int cindex;

	unsigned long i;

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

	if( packet[0] == 0xFF ) /* 0xFF means it's just a "Hello" packet; ignore */
		return;

	memcpy( &pack_head.uin1, packet, 4 );
	memcpy( &pack_head.version, ( packet + 4 ), 2 );
	memcpy( &pack_head.command, ( packet + 6 ), 2 );
	memcpy( &pack_head.zero, ( packet + 8 ), 2 );
	memcpy( &pack_head.uin2, ( packet + 10 ), 4 );
	memcpy( &pack_head.cmd, ( packet + 14 ), 2 );
	memcpy( &pack_head.message_length, ( packet + 16 ), 2 );

	message = (char *)malloc( pack_head.message_length );
	memcpy( message, ( packet + 18 ),
	        pack_head.message_length );

	memcpy( &pack_tail.ip_sender,
	        ( packet + 18 + pack_head.message_length ),
	        4 );
	memcpy( &pack_tail.ip_local,
	        ( packet + 18 + pack_head.message_length + 4 ),
	        4 );
	memcpy( &pack_tail.port,
	        ( packet + 18 + pack_head.message_length + 8 ),
	        4 );
	memcpy( &pack_tail.status,
	        ( packet + 18 + pack_head.message_length + 13 ),
	        4 );
	memcpy( &pack_tail.seq,
	        ( packet + packet_length - 4 ),
	        4 );
	memcpy( &pack_tail.chat_port,
	        ( packet + packet_length - 8 ),
	        4 );

	i = pack_tail.ip_sender;
	pack_tail.ip_sender = ( ( i << 24 ) | ( ( i & 0xff00 ) << 8 ) | ( ( i & 0xff0000 ) >> 8 ) | ( i >> 24 ) );
	i = pack_tail.ip_local;
	pack_tail.ip_local = ( ( i << 24 ) | ( ( i & 0xff00 ) << 8 ) | ( ( i & 0xff0000 ) >> 8 ) | ( i >> 24 ) );

   switch( pack_head.command )
   {
   case ICQ_CMDxTCP_START:
      switch( pack_head.cmd )
      {
      case ICQ_CMDxTCP_MSG:
			cindex = Do_Msg( 0, message, pack_head.uin1, data, 'm' );
			if( RecvMessage )
			{
				if( sound_toggle )
					playsound( RecvMessageSound );
				if( beep_toggle )
					gdk_beep();
			}
			log_window_add( "Received Message (TCP)", 1, pack_head.uin1 );
			TCPAckPacket( sock, cindex, pack_head.cmd, pack_tail.seq );
         break;
      case ICQ_CMDxTCP_READxAWAYxMSG:
      case ICQ_CMDxTCP_READxOCCxMSG:
      case ICQ_CMDxTCP_READxDNDxMSG:
      case ICQ_CMDxTCP_READxNAxMSG:
			for( cindex = 0; cindex < Num_Contacts; cindex ++ )
			{
				if( Contacts[ cindex ].uin == pack_head.uin1 )
					break;
			}
			if( Current_Status != STATUS_ONLINE &&
			    Current_Status != STATUS_FREE_CHAT && cindex != Num_Contacts )
				TCPAckPacket( sock, cindex, ICQ_CMDxTCP_READxAWAYxMSG,
				              pack_tail.seq );
         break;

      case ICQ_CMDxTCP_URL:  // url sent
			cindex = Do_Msg( 0, message, pack_head.uin1, data, 'u' );
			if( RecvMessage )
			{
				if( sound_toggle )
					playsound( RecvMessageSound );
				if( beep_toggle )
					gdk_beep();
			}
			TCPAckPacket( sock, cindex, pack_head.cmd, pack_tail.seq );
         break;

      case ICQ_CMDxTCP_CHAT:
         /* 50 A5 82 00 03 00 EE 07 00 00 50 A5 82 00 02 00 0D 00 63 68 61 74 20 72
            65 71 75 65 73 74 00 CF 60 AD D3 CF 60 AD D3 28 12 00 00 04 00 00 10 00
            01 00 00 00 00 00 00 00 00 00 00 06 00 00 00 */
			cindex = Do_Chat( 0, message, pack_head.uin1, data, pack_tail.seq );
			if( RecvChat )
			{
				if( sound_toggle )
					playsound( RecvChatSound );
				if( beep_toggle )
					gdk_beep();
			}
			if( Current_Status == STATUS_FREE_CHAT )
				TCPAcceptChat( sock, cindex, pack_tail.seq );
			break;

      case ICQ_CMDxTCP_FILE:
         /* 50 A5 82 00 03 00 EE 07 00 00 50 A5 82 00 03 00 0F 00 74 68 69 73 20 69
            73 20 61 20 66 69 6C 65 00 CF 60 AD D3 CF 60 AD D3 60 12 00 00 04 00 00
            10 00 00 00 00 00 09 00 4D 61 6B 65 66 69 6C 65 00 55 0C 00 00 00 00 00
            00 04 00 00 00 */
			printf( "Received file transfer request\n" );
         break;      

      default:
         break;
      }
      break;

   case ICQ_CMDxTCP_ACK:
      
      switch ( pack_head.cmd )
      {
		case ICQ_CMDxTCP_MSG:
      case ICQ_CMDxTCP_READxAWAYxMSG:
      case ICQ_CMDxTCP_READxOCCxMSG:
      case ICQ_CMDxTCP_READxDNDxMSG:
      case ICQ_CMDxTCP_READxNAxMSG:
		case ICQ_CMDxTCP_URL:
         /* 8F 76 20 00 03 00 DA 07 00 00 8F 76 20 00 01 00 01 00 00 CF 60 AD D3 7F 
            00 00 01 5A 12 00 00 04 00 00 00 00 14 00 00 00 */
			for( cx = 0; cx < Num_Contacts; cx ++ )
			{
				if( Contacts[ cx ].uin == pack_head.uin1 )
					break;
			}

			if( cx != Num_Contacts && ( pack_head.cmd == ICQ_CMDxTCP_MSG ||
			                            pack_head.cmd == ICQ_CMDxTCP_URL ) )
			{
				gtk_timeout_remove( Contacts[ cx ].wait );
				gtk_statusbar_pop( GTK_STATUSBAR( statusbar ),
				                   2 + Contacts[ cx ].wait );
			}

			if( pack_tail.status == ICQ_ACKxTCP_AWAY ||
			    pack_tail.status == ICQ_ACKxTCP_NA   ||
			    pack_tail.status == ICQ_ACKxTCP_DND  ||
			    pack_tail.status == ICQ_ACKxTCP_OCC )
			{
				away_message = (char*)malloc( strlen( Contacts[ cx ].nick ) + 27 );
				switch( pack_tail.status )
				{
					case ICQ_ACKxTCP_AWAY:
						sprintf( away_message, "User %s is Away:", Contacts[ cx ].nick );
						break;
					case ICQ_ACKxTCP_NA:
						sprintf( away_message, "User %s is Not Available:", Contacts[ cx ].nick );
						break;
					case ICQ_ACKxTCP_DND:
						sprintf( away_message, "User %s cannot be disturbed:", Contacts[ cx ].nick );
						break;
					case ICQ_ACKxTCP_OCC:
						sprintf( away_message, "User %s is Occupied:", Contacts[ cx ].nick );
						break;
				}
	
				OK_Box( away_message, message );
				free( away_message );
			}
         break;
      case ICQ_CMDxTCP_CHAT:
         /* 50 A5 82 00 03 00 DA 07 00 00 50 A5 82 00 02 00 03 00 6E 6F 00 CF 60 AD
            95 CF 60 AD 95 1E 3C 00 00 04 01 00 00 00 01 00 00 00 00 00 00 00 00 00
            00 01 00 00 00 */
			if( pack_tail.chat_port > 0 )
				chat_sock = TCPConnectChat( pack_tail.chat_port, pack_head.uin1, data );
         break;
      case ICQ_CMDxTCP_FILE:
			printf( "Received file transfer ack\n" );
         break;
      default: break;
      }
      break;
      
   case ICQ_CMDxTCP_CANCEL:
      switch ( pack_head.cmd )
      {
      case ICQ_CMDxTCP_CHAT:
         /* 50 A5 82 00 03 00 D0 07 00 00 50 A5 82 00 02 00 01 00 00 CF 60 AD D3 CF
            60 AD D3 28 12 00 00 04 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 06
            00 00 00 */
			printf( "Chat request cancelled\n" );
         break;
      
      case ICQ_CMDxTCP_FILE:
         /* 50 A5 82 00 03 00 D0 07 00 00 50 A5 82 00 02 00 01 00 00 CF 60 AD D3 CF
            60 AD D3 28 12 00 00 04 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 06
            00 00 00 */
			printf( "File transfer cancelled\n" );
         break;
      
      default:
         break;
      }
      break;
   
   default:
      break;
   }

	free( message );
} 

int TCPInitChannel( dataandint *data, int sock, GdkInputCondition cond )
{
	DWORD uin;
	int cx;
	unsigned short packet_size;
	BYTE *packet;

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

#ifdef TRACE_FUNCTION
	printf( "TCPInitChannel\n" );
#endif
	
	read( sock, (char*)(&packet_size), 1 );
	if( ( read( sock, (char*)(&packet_size) + 1, 1 ) ) <= 0 )
	{
		Contacts[ cx ].sok = 0;
		if( Contacts[ cx ].tcp_gdk_input )
		{
			gdk_input_remove( Contacts[ cx ].tcp_gdk_input );
			Contacts[ cx ].tcp_gdk_input = 0;
		}
		close( sock );
		return TRUE;
	}

	packet = (BYTE *)malloc( packet_size );
	read( sock, packet, packet_size );

	memcpy( &uin, ( packet + 9 ), 4 );
	
	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( Contacts[ cx ].uin == uin )
		{
			Contacts[ cx ].sok = sock;
			break;
		}
	}

	if( packet_size < 1024 )
	{
		packet_print( packet, packet_size,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE );
		TCPProcessPacket( packet, packet_size, sock, data->data );
	}

	gdk_input_remove( data->i );
	Contacts[ cx ].tcp_gdk_input = gdk_input_add( sock, GDK_INPUT_READ, (GdkInputFunction) TCPReadPacket, data->data );

	free( data );
	free( packet );
	return TRUE;
}

int TCPAcceptIncoming( struct sokandlb *data, int sock, GdkInputCondition cond )
{
	dataandint *pass_data = (dataandint *)malloc( sizeof( dataandint ) );
	struct sockaddr_in addr;
	int size = sizeof( struct sockaddr_in );
	int new_sock;

#ifdef TRACE_FUNCTION
	printf( "TCPAcceptIncoming\n" );
#endif
	
	new_sock = accept( sock, (struct sockaddr *)&addr, &size );
	if( new_sock == -1 )
	{
		return 0;
	}

	pass_data->data = data;
	pass_data->i = gdk_input_add( new_sock, GDK_INPUT_READ, (GdkInputFunction) TCPInitChannel, pass_data );

	return 1;
}

int TCPReadPacket( struct sokandlb *data, int sock, GdkInputCondition cond )
{
	DWORD uin;
	int cx;
	unsigned short packet_size;
	BYTE *packet;

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

	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( Contacts[ cx ].sok == sock )
		break;
	}
	
	read( sock, (char*)(&packet_size), 1 );
	if( ( read( sock, (char*)(&packet_size) + 1, 1 ) ) <= 0 )
	{
		if( cx != Num_Contacts )
		{
			Contacts[ cx ].sok = 0;
		}
		if( Contacts[ cx ].tcp_gdk_input )
		{
			gdk_input_remove( Contacts[ cx ].tcp_gdk_input );
			Contacts[ cx ].tcp_gdk_input = 0;
		}
		close( sock );
		return TRUE;
	}

	packet = (BYTE *)malloc( packet_size );
	read( sock, packet, packet_size );

	memcpy( &uin, packet, 4 );
	
	for( cx = 0; cx < Num_Contacts; cx ++ )
	{
		if( Contacts[ cx ].uin == uin )
		{
			Contacts[ cx ].sok = sock;
			break;
		}
	}

	if( packet_size < 1024 )
	{
		packet_print( packet, packet_size,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_RECEIVE );
		TCPProcessPacket( packet, packet_size, sock, data );
	}
	free( packet );
	return TRUE;
}

int TCPGainConnection( DWORD ip, WORD port, int cindex, struct sokandlb *data )
{
	struct
	{
		BYTE size[2];
		BYTE command;
		BYTE version[4];
		BYTE szero[4];
		BYTE uin[4];
		BYTE ip[4];
		BYTE real_ip[4];
		BYTE four;
		BYTE port[4];
	} hello_packet;

	struct sockaddr_in local, remote;
	int sizeofSockaddr = sizeof( struct sockaddr );
	int sock;

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

	if( Contacts[ cindex ].sok > 0 )
		return Contacts[ cindex ].sok;

	Word_2_Chars( hello_packet.size, 0x001A );
	hello_packet.command = 0xFF;
	DW_2_Chars( hello_packet.version, 0x00000003 );
	DW_2_Chars( hello_packet.szero, 0x00000000 );
	DW_2_Chars( hello_packet.uin, UIN );
	DW_2_Chars( hello_packet.ip, LOCALHOST );
	DW_2_Chars( hello_packet.real_ip, LOCALHOST );
	hello_packet.four = 0x04;
	DW_2_Chars( hello_packet.port, (DWORD)our_port );

	if( ip == 0 )
	{
		return -1;
	}

	sock = socket( AF_INET, SOCK_STREAM, 0 );
	if( sock == -1 )
	{
		return -1;
	}

	fcntl( sock, O_NONBLOCK );

	bzero( &local.sin_zero, 8 );
	bzero( &remote.sin_zero, 8 );
	
	local.sin_family = AF_INET;
	remote.sin_family = AF_INET;
	local.sin_port = htons( 0 );
	local.sin_addr.s_addr = htonl( INADDR_ANY );
	if( ( bind( sock, (struct sockaddr*)&local, sizeof( struct sockaddr ) ) )== -1 )
	{
		return -1;
	}
	getsockname( sock, (struct sockaddr*)&local, &sizeofSockaddr );

	remote.sin_port = htons( port );
	remote.sin_addr.s_addr = htonl( ip );
/*
 *	localport = ntohs( local.sin_port );
 *	localip = ntohl( local.sin_addr.s_addr );
 */
	if( connect( sock, (struct sockaddr *)&remote, sizeofSockaddr ) < 0 )
	{
		return -1;
	}

	write( sock, &hello_packet, sizeof( hello_packet ) );
	packet_print( (BYTE *)&hello_packet, sizeof( hello_packet ),
	              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );

	Contacts[ cindex ].sok = sock;

	Contacts[ cindex ].tcp_gdk_input = gdk_input_add( sock, GDK_INPUT_READ, (GdkInputFunction) TCPReadPacket, data );
	
	return sock;
}

int TCPSendMessage( DWORD uin, char *msg, struct sokandlb *data )
{
	int cx;
	int sock;
	unsigned short intsize;
	char buffer[1024];

	typedef struct
	{
		BYTE uin_a[4];
		BYTE version[2];
		BYTE cmd[2];
		BYTE zero[2];
		BYTE uin_b[4];
		BYTE command[2];
		BYTE msg_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE real_ip[4];
		BYTE port[4];
		BYTE four;
		BYTE zero[4];
		BYTE seq[4];
	} tcp_tail;

	struct
	{
		tcp_head head;
		char *body;
		tcp_tail tail;
	} packet;

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

	DW_2_Chars( packet.head.uin_a, UIN );
	Word_2_Chars( packet.head.version, 0x0003 );
	Word_2_Chars( packet.head.cmd, ICQ_CMDxTCP_START );
	Word_2_Chars( packet.head.zero, 0x0000 );
	DW_2_Chars( packet.head.uin_b, UIN );
	Word_2_Chars( packet.head.command, ICQ_CMDxTCP_MSG );
	Word_2_Chars( packet.head.msg_length, ( strlen( msg ) + 1 ) );

	packet.body = msg;

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

	DW_2_Chars( packet.tail.ip, our_ip );
	DW_2_Chars( packet.tail.real_ip, our_ip );
	DW_2_Chars( packet.tail.port, our_port );
	packet.tail.four = 0x04;
	if( Contacts[ cx ].status == STATUS_DND || Contacts[ cx ].status == STATUS_OCCUPIED )
		DW_2_Chars( packet.tail.zero, 0x00200000 );
	else
		DW_2_Chars( packet.tail.zero, 0x00100000 );
	DW_2_Chars( packet.tail.seq, seq_num ++ );

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

	/* If the user is offline, we use UDP to avoid hanging */
	if( Contacts[ cx ].status == STATUS_OFFLINE )
		return 0;

	sock = TCPGainConnection( Contacts[ cx ].current_ip, Contacts[ cx ].port, cx, data );

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + strlen( msg ) + 1;

		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &packet.head, sizeof( packet.head ) );
		memcpy( &buffer[2 + sizeof( packet.head )], packet.body,
		        strlen( packet.body ) + 1 );
		memcpy( &buffer[2 + sizeof( packet.head ) + strlen( packet.body ) + 1 ],
		        &packet.tail, sizeof( packet.tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return 0;
	}

	Contacts[ cx ].wait = show_wait( "Sending Message",
	                                 Contacts[ cx ].pdata );
	gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 2 + Contacts[ cx ].wait,
	                    " Sending Message..." );

	return 1;
}

int TCPSendURL( DWORD uin, char *msg, struct sokandlb *data )
{
	int cx;
	int sock;
	unsigned short intsize;
	char buffer[1024];

	typedef struct
	{
		BYTE uin_a[4];
		BYTE version[2];
		BYTE cmd[2];
		BYTE zero[2];
		BYTE uin_b[4];
		BYTE command[2];
		BYTE msg_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE real_ip[4];
		BYTE port[4];
		BYTE four;
		BYTE zero[4];
		BYTE seq[4];
	} tcp_tail;

	struct
	{
		tcp_head head;
		char *body;
		tcp_tail tail;
	} packet;

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

	DW_2_Chars( packet.head.uin_a, UIN );
	Word_2_Chars( packet.head.version, 0x0003 );
	Word_2_Chars( packet.head.cmd, ICQ_CMDxTCP_START );
	Word_2_Chars( packet.head.zero, 0x0000 );
	DW_2_Chars( packet.head.uin_b, UIN );
	Word_2_Chars( packet.head.command, ICQ_CMDxTCP_URL );
	Word_2_Chars( packet.head.msg_length, ( strlen( msg ) + 1 ) );

	packet.body = msg;

	DW_2_Chars( packet.tail.ip, our_ip );
	DW_2_Chars( packet.tail.real_ip, our_ip );
	DW_2_Chars( packet.tail.port, our_port );
	packet.tail.four = 0x04;
	DW_2_Chars( packet.tail.zero, 0x00100000 );
	DW_2_Chars( packet.tail.seq, seq_num ++ );

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

	sock = TCPGainConnection( Contacts[ cx ].current_ip, Contacts[ cx ].port, cx, data );

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + strlen( msg ) + 1;

		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &packet.head, sizeof( packet.head ) );
		memcpy( &buffer[2 + sizeof( packet.head )], packet.body,
		        strlen( packet.body ) + 1 );
		memcpy( &buffer[2 + sizeof( packet.head ) + strlen( packet.body ) + 1 ],
		        &packet.tail, sizeof( packet.tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return 0;
	}

	Contacts[ cx ].wait = show_wait( "Sending URL",
	                                 Contacts[ cx ].pdata );
	gtk_statusbar_push( GTK_STATUSBAR( statusbar ), 2 + Contacts[ cx ].wait,
	                    " Sending URL..." );

	return 1;
}

int TCPRetrieveAwayMessage( int cindex, struct sokandlb *data )
{
	int sock;
	unsigned short intsize;
	char buffer[1024];
	int status_type;

	typedef struct
	{
		BYTE uin_a[4];
		BYTE version[2];
		BYTE cmd[2];
		BYTE zero[2];
		BYTE uin_b[4];
		BYTE command[2];
		BYTE msg_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE real_ip[4];
		BYTE port[4];
		BYTE four;
		BYTE zero[4];
		BYTE seq[4];
	} tcp_tail;

	struct
	{
		tcp_head head;
		char *body;
		tcp_tail tail;
	} packet;

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

	switch( Contacts[ cindex ].status & 0xffff )
	{
		case STATUS_AWAY:
			status_type = ICQ_CMDxTCP_READxAWAYxMSG;
			break;
		case STATUS_NA:
			status_type = ICQ_CMDxTCP_READxNAxMSG;
			break;
		case STATUS_OCCUPIED:
			status_type = ICQ_CMDxTCP_READxOCCxMSG;
			break;
		case STATUS_DND:
			status_type = ICQ_CMDxTCP_READxDNDxMSG;
			break;
		default:
			status_type = ICQ_CMDxTCP_READxAWAYxMSG;
			break;
	}

	DW_2_Chars( packet.head.uin_a, UIN );
	Word_2_Chars( packet.head.version, 0x0003 );
	Word_2_Chars( packet.head.cmd, ICQ_CMDxTCP_START );
	Word_2_Chars( packet.head.zero, 0x0000 );
	DW_2_Chars( packet.head.uin_b, UIN );
	Word_2_Chars( packet.head.command, status_type );
	Word_2_Chars( packet.head.msg_length, 0x0001 );

	packet.body = "";

	DW_2_Chars( packet.tail.ip, our_ip );
	DW_2_Chars( packet.tail.real_ip, LOCALHOST );
	DW_2_Chars( packet.tail.port, our_port );
	packet.tail.four = 0x04;
	DW_2_Chars( packet.tail.zero, 0x00001000 );
	DW_2_Chars( packet.tail.seq, seq_num ++ );

	sock = TCPGainConnection( Contacts[ cindex ].current_ip, Contacts[ cindex ].port, cindex, data );

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + 1;

		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &packet.head, sizeof( packet.head ) );
		memcpy( &buffer[2 + sizeof( packet.head )], packet.body,
		        strlen( packet.body ) + 1 );
		memcpy( &buffer[2 + sizeof( packet.head ) + strlen( packet.body ) + 1 ],
		        &packet.tail, sizeof( packet.tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return 0;
	}

	return 1;
}

int TCPSendChatRequest( DWORD uin, char *msg, struct sokandlb *data )
{
	int cx;
	int sock;
	unsigned short intsize;
	char buffer[1024];

	typedef struct
	{
		BYTE uin_a[4];
		BYTE version[2];
		BYTE cmd[2];
		BYTE zero[2];
		BYTE uin_b[4];
		BYTE command[2];
		BYTE msg_length[2];
	} tcp_head;

	typedef struct
	{
		BYTE ip[4];
		BYTE real_ip[4];
		BYTE port[4];
		BYTE trail1[4];
		BYTE trail2[4];
		BYTE trail3[4];
		BYTE trail4[4];
		BYTE seq[4];
	} tcp_tail;

	struct
	{
		tcp_head head;
		char *body;
		tcp_tail tail;
	} packet;

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

	DW_2_Chars( packet.head.uin_a, UIN );
	Word_2_Chars( packet.head.version, 0x0003 );
	Word_2_Chars( packet.head.cmd, ICQ_CMDxTCP_START );
	Word_2_Chars( packet.head.zero, 0x0000 );
	DW_2_Chars( packet.head.uin_b, UIN );
	Word_2_Chars( packet.head.command, ICQ_CMDxTCP_CHAT );
	Word_2_Chars( packet.head.msg_length, ( strlen( msg ) + 1 ) );

	packet.body = msg;

	DW_2_Chars( packet.tail.ip, our_ip );
	DW_2_Chars( packet.tail.real_ip, LOCALHOST );
	DW_2_Chars( packet.tail.port, our_port );
	DW_2_Chars( packet.tail.trail1, 0x10000004 );
	DW_2_Chars( packet.tail.trail2, 0x00000100 );
	DW_2_Chars( packet.tail.trail3, 0x00000000 );
	DW_2_Chars( packet.tail.trail4, 0x00000000 );
	DW_2_Chars( packet.tail.seq, seq_num ++ );

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

	sock = TCPGainConnection( Contacts[ cx ].current_ip, Contacts[ cx ].port, cx, data );

	if( sock != -1 )
	{
		intsize = sizeof( tcp_head ) + sizeof( tcp_tail ) + strlen( msg ) + 1;

		memcpy( &buffer[0], &intsize, 2 );
		memcpy( &buffer[2], &packet.head, sizeof( packet.head ) );
		memcpy( &buffer[2 + sizeof( packet.head )], packet.body,
		        strlen( packet.body ) + 1 );
		memcpy( &buffer[2 + sizeof( packet.head ) + strlen( packet.body ) + 1 ],
		        &packet.tail, sizeof( packet.tail ) );
		write( sock, buffer, intsize + 2 );
		packet_print( buffer, intsize + 2,
		              PACKET_TYPE_TCP | PACKET_DIRECTION_SEND );
	}
	else
	{
		return -1;
	}

	return 1;
}
