/*
 * libtoc 
 *
 * Copyright (C) 1999, Torrey Searle <tsearle@uci.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <gtk/gtk.h>
#include <ctype.h>
#include "libproxy/libproxy.h"
#include "../src/service.h"
#include "libtoc.h"

#define TOC_HOST "toc.oscar.aol.com"
#define TOC_PORT 80

#define REVISION "Everybuddy"
#define ROAST "Tic/Toc"

static GList * toc_buddies = NULL; 
static GList * aim_buddies = NULL;
static char user_info_id[1024];

enum Type
{
	SIGNON = 1,
	DATA = 2,
	ERROR = 3, //not used by TOC
	SIGNOFF = 4, //not used by TOC
	KEEP_ALIVE = 5
};
unsigned long flap_version = 1;


typedef struct _flap_header
{
	char ast;
	char type;
	short seq;
	short len;
} flap_header;

void (*toc_im_in)(toc_conn  * conn, gchar * user, gchar * message );
void (*toc_chat_im_in)(toc_conn  * conn, gchar * id, gchar * user, gchar * message );
void (*toc_chat_invite)(toc_conn * conn, gchar * id, gchar * name, 
	   gchar * sender, gchar * message );

void (*toc_join_ack)(toc_conn * conn, gchar * id, gchar * name);
void (*update_user_status)(gchar * user, gboolean online, time_t idle, gint evil, gboolean unavailable );
void (*toc_error_message)(gchar * message);
void (*toc_disconnect)(toc_conn * conn);
void (*toc_chat_update_buddy)(toc_conn * conn, gchar * id, 
		                      gchar * user, gboolean online );

void (*toc_begin_file_recieve)( char * filename, unsigned long size );
void (*toc_update_file_status)( unsigned long progress );
void (*toc_complete_file_recieve)();
void (*toc_file_offer)( toc_conn * conn, char * nick, char * ip, short port,
		             char * cookie, char * filename );


void (*toc_user_info)(toc_conn  * conn, gchar * user, gchar * message );


unsigned int get_address(char *hostname)
{
    struct hostent *hp;
    if ((hp = proxy_gethostbyname(hostname))) 
	{
    	return ((struct in_addr *)(hp->h_addr))->s_addr;
	}
	printf("unknown host %s\n", hostname);
	return 0;
}

static int connect_address(unsigned int addy, unsigned short port)
{
        int fd;
    struct sockaddr_in sin;

    sin.sin_addr.s_addr = addy;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);

    fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd > -1) 
	{
        //quad_addr=strdup(inet_ntoa(sin.sin_addr));
        if (proxy_connect(fd, (struct sockaddr *)&sin, sizeof(sin)) > -1) 
		{
            return fd;
        }
    }
    return -1;
}

static char char_decode( char c )
{
	if( c >= 'A' && c <= 'Z' )
	{
		return c - 'A';
	}
	if( c >= 'a' && c <= 'z' )
	{
		return c - 'a' + 26;
	}
	if( c >= '0' && c <= '9' )
	{
		return c - '0' + 52;
	}
	if( c == '+' )
	{
		return 62;
	}
	if( c == '/' )
	{
		return 63;
	}
	return 0;
}

static char * base64_decode( char * input )
{
	char * output = g_new0( char, strlen(input) );
	int i = 0;
	int j = 0;

#ifdef DEBUG
	printf("Decoding %s\n", input );
#endif

	for( i = 0, j = 0; input[i]; i+=4, j += 3 )
	{
		char value[4];
		value[0] = char_decode( input[i] );
		value[1] = char_decode( input[i+1] );
		value[2] = char_decode( input[i+2] );
		value[3] = char_decode( input[i+3] );

		value[0] = value[0] << 2;
		value[0] = value[0] | ( (value[1] & 0x30) >> 4 );
		value[1] = ( value[1] & 0x0F ) << 4;
		value[1] = value[1] | ( (value[2] & 0x3C) >> 2 );
		value[2] = value[2] << 6;
		value[2] = value[2] | value[3] ;

		output[j] = value[0];
		output[j+1] = value[1];
		output[j+2] = value[2];
		output[j+3] = 0;

	}
	output[j] = 0;

#ifdef DEBUG
	for(i = 0; i < j; i += 2 )
	{
		printf("%c%c", output[i], output[i+1] );
	}
	printf("\n");
#endif
	return output;
}

//ERROR:<Error Code>:Var args
char * parse_error(gchar * data)
{
	int code;
	static char message[1024];
	data[3] = 0;  //terminate the string at the :
	code = atoi(data);

	switch(code)
	{
		case 901:
			g_snprintf(message,1024,"%s not currently available", data+4);
			break;
		case 902:
			g_snprintf(message, 1024, "Warning of %s not currently available", data+4);
			break;
		case 903:
			g_snprintf(message, 1024, "A message has been dropped, you are exceeding the server speed limit");
			break;
		case 950:
			g_snprintf(message, 1024, "Chat in %s is unavailable.", data+4);
			break;

		case 960:
			g_snprintf(message, 1024, "You are sending message too fast to %s", data+4);
			break;

		case 961:
			g_snprintf(message, 1024, "Your missed an im from %s because it was too big.", data+4);
			break;
		case 962:
			g_snprintf(message, 1024, "Your missed an im from %s because it was sent too fast.", data+4);
			break;
		case 970:
			g_snprintf(message, 1024, "Failure");
			break;
		case 971:
			g_snprintf(message, 1024, "Too many matches");
			break;
		case 972:
			g_snprintf(message, 1024, "Need more qualifiers");
			break;
		case 973:
			g_snprintf(message, 1024, "Dir service temporarily unavailable");
			break;
		case 974:
			g_snprintf(message, 1024, "Email lookup restricted");
			break;
		case 975:
			g_snprintf(message, 1024, "Keyword Ignored");
			break;
		case 976:
			g_snprintf(message, 1024, "No Keywords");
			break;
		case 977:
			g_snprintf(message, 1024, "Language not supported");
			break;
		case 978:
			g_snprintf(message, 1024, "Country not supported");
			break;
		case 979:
			g_snprintf(message, 1024, "Failure unknown %s", data+4);
			break;
		case 980:
			g_snprintf(message, 1024, "Incorrect nickname or password");
			break;
		case 981:
			g_snprintf(message, 1024, "The service is temporarily unavailable.");
			break;
		case 982:
			g_snprintf(message, 1024, "Your warning level is currently too high to sign on.");
			break;
		case 983:
			g_snprintf(message, 1024, "You have been connecting and disconnecting too frequently.  Wait 10 minutes and try again.  If you continue to try, you will need to wait even longer");
			break;
		case 989:
			g_snprintf(message, 1024, "An unknown signon error has occured %s", data+4);
			break;
		default:
			g_snprintf(message, 1024, "Unknown error code");
			break;
	}
	return message;
}


char *aim_encode(char * s)
{
	int len = strlen(s);
	int i = 0;
	int j = 0;
	static char buff[2048];

	for( i = 0; i < len+1 && j < 2048; i++ )
	{
		switch(s[i])
		{
			case '$':
			case '{':
			case '}':
			case '[':
			case ']':
			case '(':
			case ')':
			case '\"':
			case '\\':
				buff[j++] = '\\';
				buff[j++] = s[i];
				break;

			default:
				buff[j++] = s[i];
				break;
		}
	}
	return buff;
}



char *aim_normalize(char *s)
{
    static char buf[255];
        char *t, *u;
        int x=0;

        u = t = g_malloc(strlen(s) + 1);

        strncpy(t, s, strlen(s)+1);
        g_strdown(t);

    while(*t) {
        if (*t != ' ') {
            buf[x] = *t;
            x++;
        }
        t++;
    }
        buf[x]='\0';
        g_free(u);
    return buf;
}

unsigned char *roast_password(char *pass)
{
    /* Trivial "encryption" */
    static char rp[256];
    static char *roast = ROAST;
    int pos=2;
    int x;
    strcpy(rp, "0x");
    for (x=0;(x<150) && pass[x]; x++)
        pos+=sprintf(&rp[pos],"%02x", pass[x] ^ roast[x % strlen(roast)]);
    rp[pos]='\0';
        return rp;
}



void send_flap( toc_conn * conn, int type, gchar * data )
{
	char buff[2048];
	int i;
	/*
	 * FLAP Header (6 bytes)
	 * -----------
	 * Offset   Size  Type
	 * 0        1     ASTERISK (literal ASCII '*')
	 * 1        1     Frame Type
	 * 2        2     Sequence Number
	 * 4        2     Data Length
	 */ 

	 
	flap_header fh;

#ifdef DEBUG
	printf( "send_flap BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif

	fh.ast = '*';
	fh.type = type;
	fh.seq = htons(conn->seq_num++);
	fh.len = htons((short)(strlen(data)+1));

	memcpy(buff, &fh, sizeof(flap_header));
	memcpy(buff+sizeof(flap_header), data, strlen(data)+1);
	i = 0;
	while(i < sizeof(flap_header)+strlen(data)+1 )
	{
		i += write(conn->fd,buff+i,sizeof(flap_header)+strlen(data)+1-i);
	}

#ifdef DEBUG
	printf("%s\n", data);
	printf( "send_flap AFTER %d %d\n", conn->fd, conn->seq_num );
#endif	
}

static void toc_get_file_data( gpointer data, gint source, 
							   GdkInputCondition condition )
{

	char val[1025];
	toc_file_conn * conn = data;
	long total_len = ntohl(*((long*)(conn->header2+22)));
	short header_size =ntohs(*((short*)(conn->header1+4)));
	int read_size;
	if( total_len - conn->amount > 1024 )
	{
		read_size = 1024;
	}
	else
	{
		read_size = total_len - conn->amount;
	}

	if( conn->amount < total_len  
			&&  (read_size = recv( conn->fd, val, read_size, O_NONBLOCK )) > 0 )
	{
		int i;
		conn->amount += read_size;
		for( i = 0; i < read_size; i++ )
		{
			fprintf(conn->file, "%c", val[i] );
		}
		toc_update_file_status(conn->amount);
			
	}
	if( conn->amount >= total_len )
	{
		fclose(conn->file);
		*((short*)(conn->header2 + 18)) = 0;
		*((short*)(conn->header2 + 20)) = 0;
		conn->header2[94] = 0;
		conn->header2[1] = 0x04;
		memcpy(conn->header2+58, conn->header2+34, 4);
		memcpy(conn->header2+54, conn->header2+22, 4);
		fprintf(stderr, "sending final packet\n");
		send( conn->fd, conn->header1, 6, 0 );
		send( conn->fd, conn->header2, header_size - 6, 0 );
		gdk_input_remove(conn->handle);
		close(conn->fd);
		g_free(conn);
		toc_complete_file_recieve();
	}
}

void toc_get_file( char * ip, short port, char * cookie, char * filename )
{
		int fd;
		char buff[2048];
		char buff2[7];

		FILE * file;

		toc_file_conn * conn = g_new0( toc_file_conn, 1 );
	

		typedef struct _file_header
		{
			short magic;
			char cookie[8];
			short encryption;
			short compression;
			short total_num_files;
			short total_num_files_left;
			short total_num_parts;
			short total_num_parts_left;
			long total_file_size;
			long file_size;
			long modified_time;
			long checksum;
			long res_fork_checksum;
			long res_fork_size;
			long creation_time;
			long res_fork_checksum2;
			long num_recieved;
			long recieved_checksum;
			char id_string[32];
			char flags;
			char list_name_offset;
			char list_size_offset;
			char dummy[69];
			char mac_file_info[16];
			short name_encoding;
			short name_language;
		} file_header;

		file_header * fh = (file_header *)buff;
		short header_size;
		char * cookie2 = base64_decode(cookie);
		int i;
		
		for( i = 0; (fd = connect_address( inet_addr(ip), port )) <= 0 && i < 10; i++ );

		
#ifdef DEBUG
		fprintf(stderr, "connected to %s\n", ip );
#endif

		recv( fd, buff2, 6, 0 );
		buff2[6] = '\0';
		header_size =ntohs(*((short*)(buff2+4)));

#ifdef DEBUG
		fprintf(stderr, "header_size = %d\n", header_size );
#endif
		
		recv( fd, buff, header_size - 6, 0 );

		if( fh->magic != 0x0101 )
		{
			fprintf(stderr, "bad magic number %x\n", fh->magic );
			close(fd);
			return;
		}

#ifdef DEBUG
		fprintf( stderr, "magic = %04x\n", fh->magic );
#endif
		
		fh->magic = htons(0x0202);
		memcpy(fh->cookie, cookie2, 8);
		g_free(cookie2);
#ifdef DEBUG
		fprintf(stderr, "id_string = %s\n", buff + 62 );
		fprintf(stderr, "file_name = %s\n", buff + 186 );
#endif
		memset(buff + 62, 0, 32);
		strcpy(buff + 62, "TIK");
		fh->encryption = 0;
		fh->compression = 0;
		fh->total_num_parts = htons(1);
		fh->total_num_parts_left = htons(1);

#ifdef DEBUG
		fprintf(stderr, "total_num_parts = %04x total_num_parts_left = %04x file_size = %ld\n",
		fh->total_num_parts, fh->total_num_parts_left, ntohl(*((long*)(buff+22))));

#endif

		send( fd, buff2, 6, 0 );
		send( fd, buff, header_size - 6, 0 );

		file = fopen( filename, "w" );

		memcpy(conn->header1, buff2, 7 );
		memcpy(conn->header2, buff, 2048 );
		conn->fd = fd;
		conn->amount = 0;
		conn->file = file;

		toc_begin_file_recieve( filename,  ntohl(*((long*)(buff+22))) );

		conn->handle = gdk_input_add(fd, GDK_INPUT_READ, toc_get_file_data, conn);

}

void toc_get_talk( char * ip, short port, char * cookie )
{
		int fd;
		int i;
		char c;


		toc_file_conn * conn = g_new0( toc_file_conn, 1 );

		fprintf( stderr, "Trying to connect to %s:%d\n", ip, port );
	

		
		port = 4000;
		do
		{
			for( i = 0; (fd = connect_address( inet_addr(ip), ++port )) <= 0 && i < 100; i++ );
		}while(fd<0);
		fprintf(stderr, "Our socket is %d\n", port);

		/*
		for(;;)
		{
			recv(fd, &c, 1, 0);
			printf("0x%02x", c);
			if(isprint(c))
			{
				printf("\t%c", c);
			}
			printf("\n");
		}*/

}

void toc_send_keep_alive( toc_conn * conn )
{
	char buff[2048];
	int i;
	/*
	 * FLAP Header (6 bytes)
	 * -----------
	 * Offset   Size  Type
	 * 0        1     ASTERISK (literal ASCII '*')
	 * 1        1     Frame Type
	 * 2        2     Sequence Number
	 * 4        2     Data Length
	 */ 

	 
	flap_header fh;
	
#ifdef DEBUG
	printf( "toc_send_keep_alive BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif
	
	fh.ast = '*';
	fh.type = KEEP_ALIVE;
	fh.seq = htons(conn->seq_num++);
	fh.len = 0;

	memcpy(buff, &fh, sizeof(flap_header));
	i = 0;
	while(i < sizeof(flap_header) )
	{
		i += write(conn->fd,buff+i,sizeof(flap_header)-i);
	}
#ifdef DEBUG
	printf( "toc_send_keep_alive AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
	
}
char * get_flap(toc_conn * conn )
{
	static char buff[8192];
	flap_header fh;
	int len = 0;
	int stat = 0;
	fd_set fs;
	int sb,ind;

#ifdef DEBUG
	printf( "get_flap BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif
	FD_ZERO(&fs);
	FD_SET(conn->fd, &fs);

	select(conn->fd+1, &fs, NULL, NULL, NULL );
	stat = read(conn->fd, &fh, sizeof(flap_header));
	
	if(stat <= 0 )
	{
		printf("Server Disconnect");
		toc_disconnect(conn);
		return NULL;
	}
	while(len < ntohs(fh.len) && len < 8192 )
	{
		int val;
		FD_ZERO(&fs);
		FD_SET(conn->fd, &fs);

		select(conn->fd+1, &fs, NULL, NULL, NULL );
		val = read( conn->fd, buff+len, ntohs(fh.len)-len);
		if(val > 0)
			len += val;
		else
		{
			printf("Server Disconnect");
			toc_disconnect(conn);
			return NULL;
		}
	}
	buff[len] = 0;
	
	for (sb=0;sb<len;sb++)
        {
            if (buff[sb] == 0)
            {
                for(ind=sb;ind<len;ind++)
                {
                    buff[ind] = buff[ind+1];
                }
                sb--;
                len--;
            }
/*            printf("%d:",buff[sb]); */
        }
/*        printf("\r\n"); */

#ifdef DEBUG
	fprintf(stderr, "Flap length = %d\n", len);
	printf( "get_flap AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
	return buff;
}

void toc_callback( toc_conn * conn )
{
	char buff[8192];
	char c[8192];
	int i = 0;
	int j = 0;
	char * temp;
	GList * node;
	gchar buddies[2048];

#ifdef DEBUG
	printf( "toc_callback BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif

	temp = get_flap(conn);
	if( temp )
	{
		strncpy(buff, temp,8192);
	}
	else
		return;

#ifdef DEBUG
	fprintf(stderr,"Recieved flap: %s\n", buff);
#endif


	for( j = 0; buff[i] != ':' && buff[i]; j++, i++ )
	{
		c[j] = buff[i];
	}
	c[j] = 0;
	i++;

	if(!strcmp(c, "SIGN_ON"))
	{
		toc_add_buddies(conn, aim_buddies);
		strncpy(buddies,"toc_add_buddy",2048);
		for( node = toc_buddies; node; node=node->next )
		{
				strcat(buddies, " ");
				strcat(buddies, node->data);
		}
		send_flap(conn,DATA, buddies);
	

		send_flap(conn,DATA, "toc_add_permit");
		send_flap(conn,DATA, "toc_add_deny");
	
		send_flap(conn, DATA, "toc_init_done");
		send_flap(conn, DATA, "toc_set_info \"Visit the Everybuddy website at <A HREF=\\\"http://www.everybuddy.com\\\">http://www.everybuddy.com</A>.\"");

		send_flap(conn, DATA, "toc_set_caps 09461343-4C7F-11D1-8222-444553540000 09461344-4C7F-11D1-8222-444553540000 09461341-4C7F-11D1-8222-444553540000");
	}
	else if(!strcmp(c, "IM_IN"))
	{
		/*
		 * IM_IN:<Source User>:<Auto Response T/F?>:<Message>
		 */
		 
	
		char user[255];
		char message[2048];

		for( j = 0; buff[i] != ':' ; j++, i++ )
		{
			user[j] = buff[i];
		}
		user[j] = 0;
		i++;

		for(; buff[i] != ':'; i++ );
		i++;
		strncpy(message, buff+i,2048);

		toc_im_in(conn, user, message);
	}
	else if(!strcmp(c, "UPDATE_BUDDY"))
	{
		/*
		 * UPDATE_BUDDY:<Buddy User>:<Online? T/F>:<Evil Amount>:
		 * <Signon Time>:<IdleTime>:<UC>
		 */
		char user[255];
		char idle[255], evil[256];
		time_t idle_time = 0;
		time_t signon;
		int unavailable = 0;
		int online;

		/* get username */
		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			user[j] = buff[i];
		}
		user[j] = 0;
		i++;

		/* are we online? */
		online = (buff[i]=='T')?1:0;
		i+=2;
		

		/*skip evil for now*/

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			evil[j] = buff[i];
		}
		evil[j] = 0;
		i++;

		/*skip online time for now*/

		for(; buff[i] != ':'; i++ );
		i++;

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			idle[j] = buff[i];
		}
		idle[j] = 0;
		i++;

		for ( j = 0; j < 3; j++, i++)
		{
			if ((j==2) && (buff[i]=='U'))
				unavailable = 1;
		}

		if (atoi(idle)) {
			time(&idle_time);
			idle_time -= atoi(idle)*60;
		}

		update_user_status(user, online, idle_time, atoi(evil), unavailable);
		
	}
	else if(!strcmp(c, "CHAT_UPDATE_BUDDY"))
	{
		/*
		 * CHAT_UPDATE_BUDDY:<Chat Room Id>:<Inside? T/F>:<User 1>:<User 2>...
		 */

		gchar id[255];
		gboolean inside;

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			id[j] = buff[i];
		}
		id[j] = 0;
		i++;

		inside = (buff[i]=='T'?1:0);
		i+=2;

		while( i < strlen(buff) )
		{
			gchar user[255];
			for( j = 0; buff[i] != ':' && buff[i] != '\0'; j++, i++ )
			{
				user[j] = buff[i];
			}
			user[j] = 0;
			i++;

#ifdef DEBUG
			fprintf(stderr, "toc_chat_update_buddy %s, %s, %d\n", id, user, inside);
#endif
			toc_chat_update_buddy( conn, id, user, inside);
		}
	}
	else if(!strcmp(c, "CHAT_IN"))
	{
		/*
		 * CHAT_IN:<Chat Room Id>:<Source User>:<Whisper? T/F>:<Message>
		 */

		gchar user[255];
		gchar id[255];
		gchar message[2048];
		
		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			id[j] = buff[i];
		}
		id[j] = 0;
		i++;

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			user[j] = buff[i];
		}
		user[j] = 0;
		i++;
		
		for(; buff[i] != ':'; i++ );
		i++;
		strncpy(message, buff+i,2048);

		toc_chat_im_in(conn, id, user, message );
	}
	else if(!strcmp(c, "CHAT_INVITE"))
	{
		/*
		 * CHAT_INVITE:<Chat Room Name>:<Chat Room Id>:<Invite Sender>:<Message>
		 */

		gchar name[255];
		gchar id[255];
		gchar user[255];
		gchar message[2048];

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			name[j] = buff[i];
		}
		name[j] = 0;
		i++;
		
		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			id[j] = buff[i];
		}
		id[j] = 0;
		i++;
		
		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			user[j] = buff[i];
		}
		user[j] = 0;
		i++;
		
		strncpy(message, buff+i,2048);

		toc_chat_invite(conn, id, name, user, message );


	}

	else if(!strcmp(c, "CHAT_JOIN"))
	{

		/*
		 * CHAT_JOIN:<Chat Room Id>:<Chat Room Name>
		 */

		char name[255];
		char id[255];

		for( j = 0; buff[i] != ':'; j++, i++ )
		{
			id[j] = buff[i];
		}
		id[j] = 0;
		i++;
		
		strncpy(name, buff+i,255);

		toc_join_ack(conn, id, name);

	}
	else if(!strcmp(c, "RVOUS_PROPOSE") )
	{
		char * file_tlv;
		char tlv[2048];
		char nick[200];
		char cookie[20];
		char uuid[100];
		char port[5];
		char ip[20];
		char filename[255];

		
		for(j=0; buff[i] != ':'; i++, j++ )
		{
			nick[j] = buff[i];
		}
		nick[j] = '\0';
		i++;

		for(j=0; buff[i] != ':'; i++, j++ )
		{
			uuid[j] = buff[i];
		}
		uuid[j] = '\0';
		i++;
		for(j=0; buff[i] != ':'; i++, j++ )
		{
			cookie[j] = buff[i];
		}
		cookie[j] = '\0';
		i++;
		for(; buff[i] != ':'; i++ );
		i++;
		for(; buff[i] != ':'; i++ );
		i++;
		for(j=0; buff[i] != ':'; i++, j++ )
		{
			ip[j] = buff[i];
		}
		ip[j] = 0;
		i++;
		for(; buff[i] != ':'; i++ );
		i++;
		for(j=0; buff[i] != ':'; i++,j++ )
		{
			port[j] = buff[i];
		}
		port[j] = '\0';

		while( buff[i] )
		{
			char type[10];
			i++;
			for(j=0; buff[i] != ':'; i++, j++ )
			{
				type[j] = buff[i];
			}
			i++;
			type[j] = '\0';
			
			for( j = 0; buff[i] && buff[i] != ':'; j++, i++ ) 
			{
				tlv[j] = buff[i];
			}
			tlv[j] = '\0';


			file_tlv = base64_decode(tlv);

			for( j = strlen(8+file_tlv); j > 0 && file_tlv[8+j] != '\\'; j-- );

			g_snprintf(filename, 255, "%s/.everybuddy/files/%s", getenv("HOME"),
					   file_tlv +j +9 );


#ifdef DEBUG
			printf( "TLV value = %s\n", file_tlv + 8 );
#endif
			g_free( file_tlv );
		}

		if(!strcmp(uuid, "09461343-4C7F-11D1-8222-444553540000"))
		{
			toc_file_offer( conn, nick, ip, atoi(port), cookie, filename );
		}
		else if(!strcmp(uuid, "09461341-4C7F-11D1-8222-444553540000"))
		{
			toc_talk_accept( conn, nick, ip, atoi(port), cookie );
		}

	}
	else if(!strcmp(c, "GOTO_URL"))
	{
		char url[1024];
		char http_req[1024];
		GString * string = g_string_sized_new(1024);
		int start_saving = 0;
		char byte;
		int fd;
		for(; buff[i] != ':'; i++ );
		i++;
		for(j=0; buff[i] != ':'; i++, j++ )
		{
			url[j] = buff[i];
		}
		fd = connect_address( get_address(eb_services[AIM_SERVICE_ID].server),
							  eb_services[AIM_SERVICE_ID].port);
		g_snprintf(http_req, 1024, "GET /%s HTTP/1.0\n\n", url);
		write(fd, http_req, strlen(http_req));

		while(read(fd, &byte, 1))
		{
			if(byte == '<')
				start_saving = 1;

			if(start_saving)
				g_string_append_c(string, byte);
		}

		close(fd);
		toc_user_info(conn, user_info_id, string->str);
		g_string_free(string, TRUE);

	}
	else if(!strcmp(c, "ERROR"))
	{
		toc_error_message(parse_error(buff+6));
	}

#ifdef DEBUG
	printf( "toc_callback AFTER %d %d\n", conn->fd, conn->seq_num );
#endif

		

}



toc_conn * toc_signon( char * username, char * password )
{
	fd_set fs;
	toc_conn * conn = g_new0(toc_conn, 1);

	/*
	 *     4 byte FLAP version (1)
	 *     2 byte TLV Tag (1)
	 */

	gchar sflap_header[] = {0,0,0,1,0,1};

	gchar buff[2048];


	char * normalized_username = aim_normalize(username);
	unsigned short username_length = htons(strlen(normalized_username));
//	char * c;

	flap_header fh;



	conn->fd = connect_address(get_address(eb_services[AIM_SERVICE_ID].server),
										   eb_services[AIM_SERVICE_ID].port);

    if ( conn->fd <= 0 )
    {
        g_free(conn);
        return NULL;
    }

	/* Client sends "FLAPON\r\n\r\n" */

	write(conn->fd, "FLAPON\r\n\n\0", 10);

	/* TOC sends Client FLAP SIGNON */

	FD_ZERO(&fs);
	FD_SET(conn->fd, &fs);

	//select(conn->fd+1, &fs, NULL, NULL, NULL );


    memcpy( buff, get_flap(conn),10);	
	buff[10] = 0;

	/* Client sends TOC FLAP SIGNON */


	fh.ast = '*';
	fh.type = 1;
	fh.seq = htons(conn->seq_num++);
	fh.len = htons((short)(strlen(normalized_username)+8));

	memcpy(buff, &fh, sizeof(flap_header));
	memcpy(buff+6, sflap_header, 6);
	memcpy(buff+12,&username_length,2);
	memcpy(buff+14,normalized_username, strlen(normalized_username));

	write(conn->fd, buff, strlen(normalized_username)+14);

	/* Client sends TOC "toc_signon" message */

	/*toc_signon <authorizer host> <authorizer port> <User Name> <Password>
	 *            <language> <version>
	 */

	g_snprintf(buff, 2048, "toc_signon %s %d %s %s %s \"%s\"", 
			"login.oscar.aol.com", 5190, normalized_username, roast_password(password),
			"english", REVISION );

	send_flap(conn, DATA, buff );

#ifdef DEBUG
	printf( "toc_signon AFTER %d %d\n", conn->fd, conn->seq_num );
#endif

	return conn;

}


void toc_signoff( toc_conn * conn )
{
#ifdef DEBUG
	printf( "toc_signoff BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif
	close(conn->fd);
#ifdef DEBUG
	printf( "toc_signoff AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
}

void toc_chat_join( toc_conn * conn, gchar * chat_room_name )
{
	gchar buff[2048];

	g_snprintf(buff,2048, "toc_chat_join 4 \"%s\"", aim_encode(chat_room_name));

	send_flap(conn, DATA, buff);
}

void toc_chat_accept( toc_conn * conn, gchar * id)
{
	gchar buff[2048];

	g_snprintf(buff,2048, "toc_chat_accept %s", id);

	send_flap(conn, DATA, buff);


}

void toc_chat_send( toc_conn * conn, gchar * id, gchar * message)
{
	gchar buff[2048];
	g_snprintf(buff,2048, "toc_chat_send %s \"%s\"", id, aim_encode(message));
	send_flap(conn, DATA, buff);
}

void toc_chat_leave( toc_conn * conn, gchar * id )
{
	gchar buff[2048];
	g_snprintf(buff,2048, "toc_chat_leave %s", id);
	send_flap(conn, DATA, buff);
}

void toc_set_away( toc_conn * conn, gchar * message)
{
	gchar buff[2048];
    if (message)
		g_snprintf(buff, sizeof(buff), "toc_set_away \"%s\"", message);
	else
		g_snprintf(buff, sizeof(buff), "toc_set_away");
	send_flap(conn, DATA, buff);
}

void toc_send_im( toc_conn * conn, gchar * username, gchar * message )
{
	/* toc_send_im <Destination User> <Message> [auto] */

	gchar buff[2048];
#ifdef DEBUG
	printf( "toc_send_im BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif 
	g_snprintf(buff, 2048, "toc_send_im %s \"%s\"", aim_normalize(username), aim_encode(message));
	send_flap(conn, DATA, buff);
#ifdef DEBUG
	printf( "toc_send_im AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
}

void toc_aim_buddies_pointer(GList * list)
{
	aim_buddies = list;
}

void toc_add_buddies( toc_conn * conn, GList * list )
{
	gchar buff[2001];
	gchar buff2[2048];
	GList * node;

	buff[0] = '\0';
	for( node = list; node; node=node->next )
	{
		char * handle = node->data;	

		strcat( buff, " ");
		strcat( buff, aim_normalize(handle) );

		if(strlen(buff) > 100 )
		{
			g_snprintf(buff2, 2048, "toc_add_buddy%s", buff); 
			send_flap(conn, DATA, buff2);
			buff[0] = '\0';
		}

	}
	if(strlen(buff) > 0 )
	{
		g_snprintf(buff2, 2048, "toc_add_buddy%s", buff); 
		send_flap(conn, DATA, buff2);
	}

}

void toc_add_buddy( toc_conn * conn, gchar * user )
{
	/* toc_add_buddy <Buddy User 1> [<Buddy User2> [<Buddy User 3> [...]]] */

	gchar buff[2048];
#ifdef DEBUG
	printf( "toc_add_buddy BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif

	g_snprintf(buff, 2048, "toc_add_buddy %s", aim_normalize(user));
	toc_buddies = g_list_append(toc_buddies, strdup(aim_normalize(user)));
	send_flap(conn, DATA, buff);
#ifdef DEBUG
	printf( "toc_add_buddy AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
}

void toc_get_info( toc_conn * conn, gchar * user )
{
	gchar buff[2048];

	g_snprintf(buff, 2048, "toc_get_info %s", aim_normalize(user));
	strcpy(user_info_id, user);
	send_flap(conn, DATA, buff);
}

void toc_set_idle( toc_conn * conn, int idle )
{
	gchar buff[2048];

#ifdef DEBUG
	printf( "toc_set_idle BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif
	g_snprintf(buff, 2048, "toc_set_idle %d", idle);
	send_flap(conn, DATA, buff);
#ifdef DEBUG
	printf( "toc_set_idle AFTER %d %d\n", conn->fd, conn->seq_num );
#endif

}

void toc_remove_buddy( toc_conn * conn, gchar * user )
{
	gchar buff[2048];

#ifdef DEBUG
	printf( "toc_remove_buddy BEFORE %d %d\n", conn->fd, conn->seq_num );
#endif
	g_snprintf( buff, 2048, "toc_remove_buddy %s", aim_normalize(user));
	send_flap(conn, DATA, buff);
#ifdef DEBUG
	printf( "toc_remove_buddy AFTER %d %d\n", conn->fd, conn->seq_num );
#endif
}

void toc_invite( toc_conn * conn, gchar * id, gchar * buddy, char * message )
{
	/*
	 * toc_chat_invite <Chat Room ID> <Invite Msg> <buddy1> [<buddy2> [<buddy3> [...]]]
	 */

	gchar buff[2048];
	g_snprintf( buff, 2048, "toc_chat_invite %s \"%s\" %s", id, 
				aim_encode(message), aim_normalize(buddy) );
	send_flap(conn, DATA, buff );
}

void toc_file_accept( toc_conn * conn, char * nick, char * ip, short port, 
					  char * cookie, char * filename )
{
	char message[2048];
	char uuid[] = "09461343-4C7F-11D1-8222-444553540000";

	g_snprintf( message, 2048, "toc_rvous_accept %s %s %s",
				aim_normalize(nick), cookie, uuid );

	send_flap(conn, DATA, message );

	toc_get_file( ip, port, cookie, filename );

}

void toc_talk_accept( toc_conn * conn, char * nick, char * ip, short port, 
					  char * cookie )
{
	char message[2048];
	char uuid[] = "09461341-4C7F-11D1-8222-444553540000";

	g_snprintf( message, 2048, "toc_rvous_accept %s %s %s 3 GADJ4Q==",
				aim_normalize(nick), cookie, uuid );

	send_flap(conn, DATA, message );

	toc_get_talk( ip, port, cookie );

}

void toc_file_cancel( toc_conn * conn, char * nick, char * cookie )
{
	char message[2048];
	char uuid[] = "09461343-4C7F-11D1-8222-444553540000";

	g_snprintf( message, 2048, "toc_rvous_cancel %s %s %s",
				aim_normalize(nick), cookie, uuid );

	send_flap(conn, DATA, message );

}
	


#if 0

void toc_test_im_in(toc_conn * conn, gchar * user, gchar * message )
{
	char buff[2048];
	g_snprintf(buff,2048, "Hello %s", user );
	toc_send_im(conn, user, buff );
}

void toc_callback_handler(gpointer data, gint source, GdkInputCondition condition )
{
	toc_callback(data);
}
	

int main()
{
	toc_conn * conn;
	char buff[] = "toc_send_im tsearle256 \"if you see this message send a message to tsearle256\"";
	proxy_set_proxy( PROXY_NONE, "", 0 );
	conn = toc_signon("", "");
	toc_im_in = toc_test_im_in;
	gdk_input_add(conn->fd, GDK_INPUT_READ, toc_callback_handler, conn);

	gtk_main();
}
#endif

