/*********************************************
**********************************************
This is a file of general utility functions useful
for programming in general and icq in specific

This software is provided AS IS to be used in
whatever way you see fit and is placed in the
public domain.

Author : Matthew Smith April 23, 1998
Contributors : None yet

Changes :
**********************************************
**********************************************/
#include "micq.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
   #include <io.h>
   #define S_IRUSR		_S_IREAD
   #define S_IWUSR		_S_IWRITE
#endif
#ifdef UNIX
   #include <unistd.h>
   #include <termios.h>
#endif

static char rcfile[256];


/***************************************************************
Turns keybord echo off for the password
****************************************************************/
S_DWORD Echo_Off( void )
{
#ifdef UNIX
	struct termios attr; /* used for getting and setting terminal
				attributes */

	/* Now turn off echo */
	if (tcgetattr(STDIN_FILENO, &attr) != 0) return(-1);
		/* Start by getting current attributes.  This call copies
		all of the terminal paramters into attr */

	attr.c_lflag &= ~(ECHO);
		/* Turn off echo flag.  NOTE: We are careful not to modify any
		bits except ECHO */
	if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&attr) != 0) return(-2);
		/* Wait for all of the data to be printed. */
		/* Set all of the terminal parameters from the (slightly)
		   modified struct termios */
		/* Discard any characters that have been typed but not yet read */
#endif
	return 0;
}


/***************************************************************
Turns keybord echo back on after the password
****************************************************************/
S_DWORD Echo_On( void )
{
#ifdef UNIX
	struct termios attr; /* used for getting and setting terminal
				attributes */

	if (tcgetattr(STDIN_FILENO, &attr) != 0) return(-1);

	attr.c_lflag |= ECHO;
	if(tcsetattr(STDIN_FILENO,TCSANOW,&attr) != 0) return(-1);
#endif
	return 0;
}

/**************************************************************
Same as fprintf but for FD_T's
***************************************************************/
void M_fdprint( FD_T fd, char *str, ... )
{
   va_list args;
   int k;
   char buf[2048]; /* this should big enough */
        
   assert( buf != NULL );
   assert( 2048 >= strlen( str ) );
   
   va_start( args, str );
   vsprintf( buf, str, args );
   k = write( fd, buf, strlen( buf ) );
   assert( k == strlen( buf ) );
   va_end( args );
}

/***********************************************************
Reads a line of input from the file descriptor fd into buf
an entire line is read but no more than len bytes are 
actually stored
************************************************************/
int M_fdnreadln( FD_T fd, char *buf, size_t len )
{
   int i,j;
   char tmp;

   assert( buf != NULL );
   assert( len > 0 );
   tmp = 0;
   for ( i=-1; ( tmp != '\n' )  ; )
   {
      if  ( ( i < len ) || ( i == -1 ) )
      {
         i++;
         j = read( fd, &buf[i], 1 );
         tmp = buf[i];
      }
      else
      {
         j = read( fd, &tmp, 1 );
      }
      assert( j != -1 );
      if ( j == 0 )
      {
         buf[i] =  0;
         return -1;
      }
   }
   if ( i < 1 )
   {
      buf[i] = 0;
   }
   else
   {
      if ( buf[i-1] == '\r' )
      {
         buf[i-1] = 0;
      }
      else
      {
         buf[i] = 0;
      }
   } 
   return 0;
}

/********************************************
returns a string describing the status or
a NULL if no such string exists
*********************************************/
char *Convert_Status_2_Str( int status )
{
   if ( STATUS_OFFLINE == status ) /* this because -1 & 0xFFFF is not -1 */
   {
      return "Offline";
   }
   
   switch ( status & 0xffff )
   {
   case STATUS_ONLINE:
      return "Online";
      break;
   case STATUS_DND:
      return "Do not disturb";
      break;
   case STATUS_AWAY:
      return "Away";
      break;
   case STATUS_OCCUPIED:
      return "Occupied";
      break;
   case STATUS_NA:
      return "Not available";
      break;
   case STATUS_INVISIBLE:
      return "Invisible";
      break;
   case STATUS_FREE_CHAT:
      return "Free for chat";
      break;
   default :
      return NULL;
      break;
   }
}

/********************************************
prints out the status of new_status as a string
if possible otherwise as a hex number
*********************************************/
void Print_Status( DWORD new_status  )
{
   if ( Convert_Status_2_Str( new_status ) != NULL )
   {
      printf( "%s", Convert_Status_2_Str( new_status ) );
      if ( Verbose )
         printf( " %02X",( WORD ) ( new_status >> 16 ) );
   }
   else
   {
      printf( "%08lX", new_status );
   }
}

/**********************************************
Prints the name of a user or there UIN if name
is not know.
***********************************************/
int Print_UIN_Name( DWORD uin )
{
   int i;
   
   for ( i=0; i < Num_Contacts; i++ )
   {
      if ( Contacts[i].uin == uin )
         break;
   }

   if ( i == Num_Contacts )
   {
      printf( CLIENTCOL "%lu" NOCOL, uin );
      return -1 ;
   }
   else
   {
      printf( CONTACTCOL "%s" NOCOL ,Contacts[i].nick );
      return i;
   }
}

/*********************************************
Converts a nick name into a uin from the contact
list.
**********************************************/
DWORD nick2uin( char *nick )
{
   int i;
   BOOL non_numeric=FALSE;
   
   for ( i=0; i< Num_Contacts; i++ )
   {
      if ( ! strncasecmp( nick, Contacts[i].nick, 19  ) )
      {
         if ( (S_DWORD) Contacts[i].uin > 0 )
            return Contacts[i].uin;
         else
            return -Contacts[i].uin; /* alias */
      }
   }
   for ( i=0; i < strlen( nick ); i++ )
   {
      if ( ! isdigit( (int) nick[i] ) )
      {
         non_numeric=TRUE;
         break;
      }
   }
   if ( non_numeric )
      return -1; /* not found and not a number */
   else
      return atoi( nick );
}


static void Initalize_RC_File( void )
{
   FD_T rcf;
   time_t t;
   char nickstr[100];
 
   rcf = open( rcfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
   if ( rcf == -1 )
   {
      perror( "Error creating config file " );
      exit( 1);
   }
   M_fdprint( rcf, "# This file was generated by Micq of %s %s\n",__TIME__,__DATE__);
   t = time( NULL );
   M_fdprint( rcf, "# This file was generated on %s", ctime( &t ) );   
   printf( "Enter UIN or nickname : #" );
   fflush( stdout );
   scanf( "%s", nickstr );
   UIN = nick2uin( nickstr );
   M_fdprint( rcf, "UIN %d\n", UIN );
   printf( "Enter password : " );
   fflush( stdout );
   Echo_Off();
   scanf( "%s", passwd );
   Echo_On();
   M_fdprint( rcf, "Password %s\n", passwd );
   set_status = STATUS_ONLINE;
   M_fdprint( rcf, "Status %d\n", STATUS_ONLINE );
   M_fdprint( rcf, "Server %s\n", "icq1.mirabilis.com" );
   strcpy( server, "icq1.mirabilis.com" );
   remote_port = 4000;
   M_fdprint( rcf, "Port %d\n", 4000 );
   M_fdprint( rcf, "# Ok now the contact list\n" );
   M_fdprint( rcf, "Contacts\n" );
   M_fdprint( rcf, "11300897 Linux Master\n" );
   M_fdprint( rcf, "11290140 Morgoth\n" );
   M_fdprint( rcf, "alias1\n" );
   M_fdprint( rcf, "# ^-- Alias to morgtoh\n" );
   Num_Contacts = 3;
   Contacts[0].uin = 11300897;
   strcpy( Contacts[0].nick, "Linux Master" );
   Contacts[0].status = STATUS_OFFLINE;
   Contacts[0].last_time = -1L;
   Contacts[0].current_ip = 0x7f000001;
   Contacts[ 0 ].port = 0;
   Contacts[ 0 ].sok = (SOK_T ) -1L;
   Contacts[1].uin = 11290140;
   strcpy( Contacts[1].nick, "Morgoth" );
   Contacts[1].status = STATUS_OFFLINE;
   Contacts[1].last_time = -1L;
   Contacts[1].current_ip = 0x7f000001;
   Contacts[ 1 ].port = 0;
   Contacts[ 1 ].sok = (SOK_T ) -1L;
   Contacts[2].uin = -11290140;
   strcpy( Contacts[2].nick, "alias1" );
   Contacts[2].status = STATUS_OFFLINE;
   Contacts[2].last_time = -1L;
   Contacts[2].current_ip = 0x7f000001;
   Contacts[ 2 ].port = 0;
   Contacts[ 2 ].sok = (SOK_T ) -1L;
   close( rcf );
}

static void Read_RC_File( FD_T rcf )
{
   char buf[100];
   char *tmp;
   DWORD tmp_uin;
   
   Contact_List = FALSE;
   for ( ; !Contact_List; )
   {
      M_fdnreadln( rcf, buf, sizeof( buf ) );
      if ( ( buf[0] != '#' ) && ( buf[0] != 0 ) )
      {
         tmp = strtok( buf, " " );
         if ( ! strcasecmp( tmp, "Server" ) )
         {
            strcpy( server, strtok( NULL, " \n\t" ) );
         }
         else if ( ! strcasecmp( tmp, "Password" ) )
         {
            strcpy( passwd, strtok( NULL, " \n\t" ) );
         }
         else if ( ! strcasecmp( tmp, "UIN" ) )
         {
             UIN = atoi( strtok( NULL, " \n\t" ) );
         }
         else if ( ! strcasecmp( tmp, "port" ) )
         {
             remote_port = atoi( strtok( NULL, " \n\t" ) );
         }
         else if ( ! strcasecmp( tmp, "Status" ) )
         {
             set_status = atoi( strtok( NULL, " \n\t" ) );
         }
         else if ( ! strcasecmp( tmp, "Contacts" ) )
         {
            Contact_List = TRUE;
         }
      }
   }
   for ( ; ! M_fdnreadln( rcf, buf, sizeof( buf ) ); )
   {
      if ( Num_Contacts == 100 )
         break;
      if ( ( buf[0] != '#' ) && ( buf[0] != 0 ) )
      {
         if ( isdigit( (int) buf[0] ) )
         {
            Contacts[ Num_Contacts ].uin = atoi( strtok( buf, " " ) );
            Contacts[ Num_Contacts ].status = STATUS_OFFLINE;
            Contacts[ Num_Contacts ].last_time = -1L;
            Contacts[ Num_Contacts ].current_ip = -1L;
            tmp = strtok( NULL, "" );
            if ( tmp != NULL )
               memcpy( Contacts[ Num_Contacts ].nick, tmp, sizeof( Contacts->nick )  );
            else
               Contacts[ Num_Contacts ].nick[0] = 0;
            if ( Contacts[ Num_Contacts ].nick[19] != 0 )
               Contacts[ Num_Contacts ].nick[19] = 0;
            if ( Verbose )
               printf( "%ld = %s\n", Contacts[ Num_Contacts ].uin, Contacts[ Num_Contacts ].nick );
            Num_Contacts++;
         }
         else
         {
            tmp_uin = Contacts[ Num_Contacts - 1 ].uin;
            tmp = strtok( buf, ", \t" ); /* aliases may not have spaces */
            for ( ; tmp!=NULL; Num_Contacts++ )
            {
               Contacts[ Num_Contacts ].uin = -tmp_uin;
               Contacts[ Num_Contacts ].status = STATUS_OFFLINE;
               Contacts[ Num_Contacts ].last_time = -1L;
               Contacts[ Num_Contacts ].current_ip = -1L;
               Contacts[ Num_Contacts ].port = 0;
               Contacts[ Num_Contacts ].sok = (SOK_T ) -1L;
               memcpy( Contacts[ Num_Contacts ].nick, tmp, sizeof( Contacts->nick )  );
               tmp = strtok( NULL, ", \t" );
            }
         }
      }
   }
   if ( Verbose )
   {
      printf( "UIN = %ld\n", UIN );
      printf( "port = %ld\n", remote_port );
      printf( "passwd = %s\n", passwd );
      printf( "server = %s\n", server );
      printf( "status = %ld\n", set_status );
      printf( "# of contacts = %d\n", Num_Contacts );
      printf( "UIN of contact[0] = %ld\n", Contacts[0].uin );
   }
}

/*******************************************************
Gets config info from the rc file in the users home 
directory.
********************************************************/
void Get_Unix_Config_Info( void )
{
   char *path;
   FD_T rcf;

#ifdef _WIN32
   path = "./";
#else
   path = getenv( "HOME" );
#endif
   strcpy( rcfile, path );
   strcat( rcfile, "/.micqrc" );
   rcf = open( rcfile, O_RDONLY );
   if ( rcf == -1 )
   {
      if ( errno == ENOENT ) /* file not found */
      {
         Initalize_RC_File();
      }
      else
      {
         perror( "Error reading config file exiting " );
         exit( 1 );
      }
   }
   else
   {
      Read_RC_File( rcf );
   }
}

void Print_IP( DWORD uin )
{
   int i;
   DWORD t;
   
   for ( i=0; i< Num_Contacts; i++ )
   {
      if ( Contacts[i].uin == uin )
      {
         if ( Contacts[i].current_ip != -1L )
         {
            t = Contacts[i].current_ip;
            printf( "%u.%u.%u.%u",(BYTE) (t>>24),(BYTE) (t>>16),(BYTE) (t>>8),(BYTE) t );
         }
         else
         {
            printf( "unknown" );
         }
         return;
      }
   }
   printf( "unknown" );
}

/************************************************
Gets the TCP port of the specified UIN
************************************************/
DWORD Get_Port( DWORD uin )
{
   int i;
   
   for ( i=0; i< Num_Contacts; i++ )
   {
      if ( Contacts[i].uin == uin )
      {
         return Contacts[i].port;
      }
   }
   return -1L;
}

/********************************************
Converts an intel endian character sequence to
a DWORD
*********************************************/
DWORD Chars_2_DW( unsigned char *buf )
{
   DWORD i;
   
   i= buf[3];
   i <<= 8;
   i+= buf[2];
   i <<= 8;
   i+= buf[1];
   i <<= 8;
   i+= buf[0];
   
   return i;
}

/********************************************
Converts an intel endian character sequence to
a WORD
*********************************************/
WORD Chars_2_Word( unsigned char *buf )
{
   WORD i;
   
   i= buf[1];
   i <<= 8;
   i += buf[0];
   
   return i;
}

/********************************************
Converts a DWORD to
an intel endian character sequence 
*********************************************/
void DW_2_Chars( unsigned char *buf, DWORD num )
{
   buf[3] = ( unsigned char ) ((num)>>24)& 0x000000FF;
   buf[2] = ( unsigned char ) ((num)>>16)& 0x000000FF;
   buf[1] = ( unsigned char ) ((num)>>8)& 0x000000FF;
   buf[0] = ( unsigned char ) (num) & 0x000000FF;
}

/********************************************
Converts a WORD to
an intel endian character sequence 
*********************************************/
void Word_2_Chars( unsigned char *buf, WORD num )
{
   buf[1] = ( unsigned char ) (((unsigned)num)>>8) & 0x00FF;
   buf[0] = ( unsigned char ) ((unsigned)num) & 0x00FF;
}
