/*
Send Message Function for ICQ... 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Author : zed@mentasm.com
*/
#include "datatype.h"
#include "micq.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
  #include <winsock2.h>
#else
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/socket.h>
#endif
#include <string.h>
#include <assert.h>

/********************************************************
The following data constitutes fair use for compatibility.
*********************************************************/
unsigned char icq_check_data[257] = {
	0x0a, 0x5b, 0x31, 0x5d, 0x20, 0x59, 0x6f, 0x75, 
	0x20, 0x63, 0x61, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 
	0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 
	0x73, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x49,
	0x43, 0x51, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73, 
	0x2e, 0x20, 0x4a, 0x75, 0x73, 0x74, 0x20, 0x73, 
	0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x22, 0x53, 
	0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x66, 
	0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
	0x22, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
	0x6e, 0x63, 0x65, 0x73, 0x2f, 0x6d, 0x69, 0x73,
	0x63, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x43,
	0x51, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x72, 0x6f,
	0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53,
	0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x69,
	0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
	0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x70, 0x61,
	0x6e, 0x65, 0x6c, 0x2e, 0x20, 0x43, 0x72, 0x65,
	0x64, 0x69, 0x74, 0x3a, 0x20, 0x45, 0x72, 0x61,
	0x6e, 0x0a, 0x5b, 0x32, 0x5d, 0x20, 0x43, 0x61,
	0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x6d, 0x65,
	0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61,
	0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x61,
	0x69, 0x64, 0x3f, 0x20, 0x20, 0x44, 0x6f, 0x75,
	0x62, 0x6c, 0x65, 0x2d, 0x63, 0x6c, 0x69, 0x63,
	0x6b, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x75,
	0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x67,
	0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x61,
	0x6c, 0x6f, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
	0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
	0x67, 0x65, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
	0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0
};

/***************************************************
Sends a message thru the server to uin.  Text is the
message to send.
****************************************************/
void icq_sendmsg( SOK_T sok, DWORD uin, char *text, DWORD msg_type)
{
	SIMPLE_MESSAGE msg;
	net_icq_pak pak;
	int size, len; 

	log_event("You sent instant message",text,uin);
	rus_conv ("kw",text);
	

	len = strlen(text);
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_SENDM );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );

	DW_2_Chars( msg.uin, uin );
	Word_2_Chars(msg.type, msg_type);
	Word_2_Chars( msg.len, len + 1 );		/* length + the NULL */

	memcpy(&pak.data, &msg, sizeof( msg ) );
	memcpy(&pak.data[8], text, len + 1);

	size = sizeof( msg ) + len + 1;

	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/**************************************************
Sends a authorixation to the server so the Mirabilis
client can add the user.
***************************************************/
void icq_sendauthmsg( SOK_T sok, DWORD uin)
{
	SIMPLE_MESSAGE msg;
	net_icq_pak pak;
	int size; 

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_SENDM );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );

	DW_2_Chars( msg.uin, uin );
	DW_2_Chars( msg.type, AUTH_MESSAGE );		/* A type authorization msg*/
	Word_2_Chars( msg.len, 2 );		

	memcpy(&pak.data, &msg, sizeof( msg ) );

   pak.data[ sizeof(msg) ]=0x03;
   pak.data[ sizeof(msg) + 1]=0x00;

	size = sizeof( msg ) + 2;

	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/***************************************************
Changes the users status on the server
****************************************************/
void icq_change_status( SOK_T sok, DWORD status )
{
	net_icq_pak pak;
	int size ;


	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_STATUS_CHANGE );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );

   DW_2_Chars( pak.data, status);
   Current_Status = status;

	size = 4;

	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/******************************************************************
Logs off ICQ should handle other cleanup as well
********************************************************************/
void Quit_ICQ( int sok )
{
	net_icq_pak pak;
	int size, len;
   
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_SEND_TEXT_CODE );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
   len = strlen( "B_USER_DISCONNECTED" ) + 1;
   *(short * ) pak.data = len;
   size = len + 4;
   
   memcpy( &pak.data[2], "B_USER_DISCONNECTED", len );
   pak.data[ 2 + len ] = 05;
   pak.data[ 3 + len ] = 00;

   last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
   SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);

   SOCKCLOSE(sok);
   SOCKCLOSE( sok );
}

/*********************************************************
Sends a request to the server for info on a specific user
**********************************************************/
void send_info_req( SOK_T sok, DWORD uin )
{
	net_icq_pak pak;
	int size ;


	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_INFO_REQ );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
#if ICQ_VER == 0x0002
   Word_2_Chars( pak.data , seq_num );
   DW_2_Chars( pak.data + 2, uin );

	size = 6;
#else
   DW_2_Chars( pak.data, uin );

	size = 4;
#endif

	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/*********************************************************
Sends a request to the server for info on a specific user
**********************************************************/
void send_ext_info_req( SOK_T sok, DWORD uin )
{
	net_icq_pak pak;
	int size ;


	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_EXT_INFO_REQ );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );

#if ICQ_VER == 0x0002
   Word_2_Chars( pak.data , seq_num );
   DW_2_Chars( pak.data + 2, uin );
	size = 6;
#else
   DW_2_Chars( pak.data , uin );
	size = 4;
#endif


	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/***************************************************************
Initializes a server search for the information specified
****************************************************************/
void start_search( SOK_T sok, char *email, char *nick, char* first, char* last )
{
	net_icq_pak pak;
	int size ;

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_SEARCH_USER );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
/*
   Word_2_Chars( pak.data , seq_num++ );
   size = 2;
*/
   size = 0;
   Word_2_Chars( pak.data + size, strlen( nick ) + 1 );
   size += 2;
   strcpy( pak.data + size , nick );
   size += strlen( nick ) + 1;
   Word_2_Chars( pak.data + size, strlen( first ) + 1 );
   size += 2;
   strcpy( pak.data + size , first );
   size += strlen( first ) + 1;
   Word_2_Chars( pak.data + size, strlen( last ) + 1);
   size += 2;
   strcpy( pak.data + size , last );
   size += strlen( last ) +1 ;
   Word_2_Chars( pak.data + size, strlen( email ) + 1  );
   size += 2;
   strcpy( pak.data + size , email );
   size += strlen( email ) + 1;
	last_cmd[seq_num - 2 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

/***************************************************
Registers a new UIN in the ICQ network
****************************************************/
void reg_new_user( SOK_T sok, char *pass)
{
#if ICQ_VER == 0x0002
	srv_net_icq_pak pak;
#else
	net_icq_pak pak;
#endif
   char len_buf[2];
	int size, len; 

	len = strlen(pass);
	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_REG_NEW_USER );
	Word_2_Chars( pak.head.seq, seq_num++ );
   Word_2_Chars( len_buf, len );
#if ICQ_VER == 0x0002
	memcpy(&pak.data, "\x02\x00", 2 );
   memcpy(&pak.data[2], len_buf, 2 );
	memcpy(&pak.data[4], pass, len + 1);
   DW_2_Chars( &pak.data[4+len], 0x0072 );
   DW_2_Chars( &pak.data[8+len], 0x0000 );
 	size = len + 12;
#else
/*	memcpy(&pak.data, "\x02\x00", 2 );*/
   memcpy(&pak.data[0], len_buf, 2 );
	memcpy(&pak.data[2], pass, len + 1);
   DW_2_Chars( &pak.data[2+len], 0xA0 );
   DW_2_Chars( &pak.data[6+len], 0x2461 );
   DW_2_Chars( &pak.data[10+len], 0xa00000 );
   DW_2_Chars( &pak.data[14+len], 0x00 );
	size = len + 18;
#endif


	last_cmd[seq_num - 1 ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

void Update_User_Info( SOK_T sok, USER_INFO_PTR user)
{
	net_icq_pak pak;
	int size ;

	Word_2_Chars( pak.head.ver, ICQ_VER );
	Word_2_Chars( pak.head.cmd, CMD_UPDATE_INFO );
	Word_2_Chars( pak.head.seq, seq_num++ );
	DW_2_Chars( pak.head.UIN, UIN );
   
#if ICQ_VER == 0x0002
   Word_2_Chars( pak.data , seq_num++ );
   size = 2;
#else
   size = 0;
#endif
   Word_2_Chars( pak.data + size, strlen( user->nick ) + 1 );
   size += 2;
   strcpy( pak.data + size , user->nick );
   size += strlen( user->nick ) + 1;
   Word_2_Chars( pak.data + size, strlen( user->first ) + 1 );
   size += 2;
   strcpy( pak.data + size , user->first );
   size += strlen( user->first ) + 1;
   Word_2_Chars( pak.data + size, strlen( user->last ) + 1);
   size += 2;
   strcpy( pak.data + size , user->last );
   size += strlen( user->last ) +1 ;
   Word_2_Chars( pak.data + size, strlen( user->email ) + 1  );
   size += 2;
   strcpy( pak.data + size , user->email );
   size += strlen( user->email ) + 1;
   pak.data[ size ] = user->auth;
   size++;
	last_cmd[ ( seq_num - 1 ) & 0x3ff ] = Chars_2_Word( pak.head.cmd );
	SOCKWRITE( sok, &(pak.head.ver), size + sizeof( pak.head ) - 2);
}

void icq_sendurl( SOK_T sok, DWORD uin, char *description, char *url )
{
   char buf[450];
   
   sprintf( buf, "%s\xFE%s", url, description );
   icq_sendmsg( sok, uin, buf, URL_MESS );
}

void Wrinkle( void * ptr, size_t len )
{
   BYTE *buf;
   DWORD chksum;
   DWORD key;
   DWORD tkey, temp;
   BYTE r1,r2;
   int n, pos;
   DWORD chk1,chk2;

   buf = ptr;

   buf[2] = rand() & 0xff ;
   buf[3] = 0;
   buf[4] = 0;
   buf[5] = 0;

   r1 = rand() % ( len - 4 );
   r2 = rand() & 0xff;

   chk1 = (BYTE) buf[8];
   chk1 <<= 8;
   chk1 += (BYTE) buf[4];
   chk1 <<= 8;
   chk1 += (BYTE) buf[2];
   chk1 <<= 8;
   chk1 += (BYTE) buf[6];
   chk2 = r1;
   chk2 <<= 8;
   chk2 +=(BYTE) ( buf[ r1 ] );
   chk2 <<= 8;
   chk2 += r2;
   chk2 <<= 8;
   chk2 +=(BYTE) ( icq_check_data[ r2 ] );
   chk2 ^= 0x00ff00ff;
   
   chksum = chk2 ^ chk1;
   
   DW_2_Chars( &buf[ 0x10 ], chksum );
   key = len;
   key *= 0x66756B65;
   key += chksum;
   n = ( len + 3 ) / 4;   
   pos = 0;
   while ( pos < n )
   {
      if ( pos != 0x10 )
      {
         tkey = key + icq_check_data[ pos & 0xff ];
         temp = Chars_2_DW( &buf[ pos ] );
         temp ^= tkey;
         DW_2_Chars( &buf[ pos ], temp );
      }
      pos += 4;
   }
   Word_2_Chars( &buf[0], ICQ_VER );
}

/***************************************************************
This handles actually sending a packet after it's been assembled.
When V5 is implemented this will wrinkle the packet and calculate 
the checkcode.
****************************************************************/
size_t SOCKWRITE( SOK_T sok, void * ptr, size_t len )
{
   Word_2_Chars( &((BYTE *)ptr)[4], 0 );
   ((BYTE *)ptr)[0x0A] = ((BYTE *) ptr)[8];
   ((BYTE *)ptr)[0x0B] = ((BYTE *) ptr)[9];
#if 1
      if ( Verbose > 1 )
         Hex_Dump( ptr, len );
#endif 
   Wrinkle( ptr, len );
#ifdef SPAM
   /* Kinda cheap way of hopefully making connection more stable */
   /*        :)                                                  */
   sockwrite( sok, ptr, len );
   sockwrite( sok, ptr, len );
   sockwrite( sok, ptr, len );
   sockwrite( sok, ptr, len );
   assert( sockwrite( sok, ptr, len ) == len );
#endif
   return sockwrite( sok, ptr, len );
}

size_t SOCKREAD( SOK_T sok, void * ptr, size_t len )
{
   size_t sz;
   
   sz = sockread( sok, ptr, len );
   if ( ( Verbose > 2 ) && ( sz > 0 ) )
      Hex_Dump( ptr, sz );
   return sz;
}
