/*********************************************
**********************************************
This is the main ICQ file. Currently it
logs in and sits in a loop. It can receive
messages and keeps the connection alive.
Use crtl-break to exit.

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 19, 1998
Contributors : Nicolas Sahlqvist April 27, 1998
			   Ulf Hedlund (guru@slideware.com) April 28, 1998
            Michael Ivey May 4, 1998
            Michael Holzt May 5, 1998

Changes :
   4-28-98 support for WIN32 [UH]
   4-20-98 added variable time_delay between keep_alive packets mds
   4-20-98 added instant message from server support mds
   4-21-98 changed so that long ( 250+ characters ) messages work
            new maximum is ~900 which is hopefully big enough.
            When I know more about udp maybe I can come up with
            a general solution. mds I now think ICQ has a max that is
            smaller than this so everything is ok mds I now think that
            the icq client's maximum is arbitrary and can be ignored :)
   4-23-98 Added beginnings of a user interface
   4-26-98 Changed the version to 0.2a :)
   4-27-98 Nicco added feature to use nick names to log in
   5-05-98 Authorization Messages
   5-13-98 Added time stamps for most things.
   6-17-98 Changed condition on which we should send auto_reply message. Fryslan
   6-18-98 Added different auto reply messages for different status types see also ui.c and util.c Fryslan
   6-20-98 Added an alter command to alter your command names online. Fryslan
**********************************************
**********************************************/
#include "micq.h"
#include "datatype.h"
#include "msg_queue.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#ifdef _WIN32
#include <conio.h>
#include <io.h>
#include <winsock2.h>
#include <time.h>
#else
#ifndef STiK
#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 <sys/wait.h>
#endif
#include "mreadline.h"
#endif
#ifndef STiK
#include <fcntl.h>
#endif
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#ifdef STiK
#include <tos.h>

DRV_LIST *drivers = (DRV_LIST *)NULL;
TPL *tpl = (TPL *)NULL;

#endif

BYTE Sound = SOUND_ON; /* Beeps on by default */
BYTE Sound_Str[150];  /* the command to run from the shell to play sound files */
BOOL Russian = FALSE; /* Do we do kio8-r <->Cp1251 codeset translation? */
BOOL Logging = FALSE;  /* Do we log messages to ~/micq_log?  This should probably have different levels */
BOOL Color = TRUE; /* Do we use ANSI color? */
BOOL Quit = FALSE;    /* set when it's time to exit the program */
BOOL Verbose = FALSE;  /* this is displays extra debuging info */
BOOL serv_mess[ 1024 ]; /* used so that we don't get duplicate messages with the same SEQ */
WORD last_cmd[ 1024 ]; /* command issued for the first 1024 SEQ #'s */
/******************** if we have more than 1024 SEQ this will need some hacking */
WORD seq_num = 1;  /* current sequence number */
#ifdef STiK
DWORD our_ip = 0x0100007fL; /* localhost for some reason */
#else
DWORD our_ip = 0x0100007f; /* localhost for some reason */
#endif
DWORD our_port; /* the port to make tcp connections on */
/************ We don't make tcp connections yet though :( */
DWORD UIN; /* current User Id Number */
BOOL Contact_List = FALSE; /* I think we always have a contact list now */
Contact_Member Contacts[ 100 ]; /* no more than 100 contacts max */
int Num_Contacts=0;
DWORD Current_Status=STATUS_OFFLINE;
DWORD last_recv_uin=0;
char passwd[100];
char server[100];
DWORD set_status;
DWORD remote_port;
BOOL Done_Login=FALSE;
BOOL auto_resp=FALSE;
char auto_rep_str_dnd[450] = { "Don't page me, my head is hurting!" };
char auto_rep_str_away[450] = { "I told you I wasn't here!" };
char auto_rep_str_na[450] = { "Working, working always working..." };
char auto_rep_str_occ[450] = { "I am working on opening this beer so I am busy." };
char auto_rep_str_inv[450] = { "So you can see me, so you can't!" };
char message_cmd[16];
char info_cmd[16];
char quit_cmd[16];
char reply_cmd[16];
char again_cmd[16];
char add_cmd[16];

char list_cmd[16];
char away_cmd[16];
char na_cmd[16];
char dnd_cmd[16];
char online_cmd[16];
char occ_cmd[16];
char ffc_cmd[16];
char inv_cmd[16];
char status_cmd[16];
char auth_cmd[16];
char auto_cmd[16];
char change_cmd[16];
char search_cmd[16];
char save_cmd[16];
char alter_cmd[16];
char msga_cmd[16];
char url_cmd[16];
char update_cmd[16];
char rand_cmd[16];
char color_cmd[16];
char sound_cmd[16];

/*** auto away values ***/
#ifdef STiK
time_t idle_val=0;
#else
int idle_val=0;
#endif
int idle_flag=0;
#define away_time 600

#ifdef STiK
time_t next_resend;
#else
unsigned int next_resend;
#endif

/*/////////////////////////////////////////////
// Connects to hostname on port port
// hostname can be DNS or nnn.nnn.nnn.nnn
// write out messages to the FD aux */

#ifdef STiK
int Connect_Remote( char *hostname, int port, FD_T aux )
{
   int conct, length;
   int sok;
   unsigned long sin;
   CIB *connection;

   if((resolve(hostname, (char **)NULL, &sin,1))<0)
		{
			if (Verbose)
				{
					M_fdprint(aux, "Shakespeare couldn't spell why should I?\n");
					M_fdprint(aux, " Especially something like %s\n",hostname);
				}
			return 0;
		}

	sok = UDP_open(sin,port); /*,0,500);*/

	if ( sok < 0 )/* did we connect ?*/
	{
      if ( Verbose )
      {
   	   M_fdprint( aux, " Conection Refused on port %d at %s\n", port, hostname );
         #ifdef FUNNY_MSGS
            M_fdprint( aux, " D'oh!\n" );
         #endif
   	   perror( "connect" );
      }
	   return sok;
	}

   connection = (CIB *)CNgetinfo(sok);

   our_ip = connection->lhost;
   our_port = sok;

   if (Verbose )
   {
      #ifdef FUNNY_MSGS
         M_fdprint( aux, "Our port is %d, take her to sea Mr. Mordoch.\n", ntohs( sin.sin_port ) );
      #else
         M_fdprint( aux, "The port that will be used for tcp ( not yet implemented ) is %d\n", our_port );
      #endif
   }

   if ( Verbose )
   {
      M_fdprint( aux, "Connected to %s, waiting for response\n", hostname );
   }
   return sok;
}
#else
int Connect_Remote( char *hostname, int port, FD_T aux )
{

   int conct, length;
   int sok;
   
   struct sockaddr_in sin;  /* used to store inet addr stuff */
   struct hostent *host_struct; /* used in DNS lookup */


#if 1
	sin.sin_addr.s_addr = inet_addr( hostname ); 
  	if ( sin.sin_addr.s_addr  == -1 ) /* name isn't n.n.n.n so must be DNS */
#else

	if ( inet_aton( hostname, &sin.sin_addr )  == 0 ) /* checks for n.n.n.n notation */
#endif
	{
	   host_struct = gethostbyname( hostname );/* name isn't n.n.n.n so must be DNS */
      if ( host_struct == NULL )
      {
         if ( Verbose )
         {
            M_fdprint( aux, "Shakespeare couldn't spell why should I?\n" );
            M_fdprint( aux, " Especially something like %s\n", hostname );
            /*herror( "Can't find hostname" );*/
         }
         return 0;
      }
	   sin.sin_addr = *((struct in_addr *)host_struct->h_addr);
   }
	sin.sin_family = AF_INET; /* we're using the inet not appletalk*/
	sin.sin_port = htons( port );	/* port */
	sok = socket( AF_INET, SOCK_DGRAM, 0 );/* create the unconnected socket*/
   if ( sok == -1 ) 
   {
      perror( "Socket creation failed" );
      exit( 1 );
   }   
   if ( Verbose )
   {
      M_fdprint( aux, "Socket created attempting to connect\n" );
   }
	conct = connect( sok, (struct sockaddr *) &sin, sizeof( sin ) );

	if ( conct == -1 )/* did we connect ?*/
	{
      if ( Verbose )
      {
   	   M_fdprint( aux, " Conection Refused on port %d at %s\n", port, hostname );
         #ifdef FUNNY_MSGS
            M_fdprint( aux, " D'oh!\n" );
         #endif
   	   perror( "connect" );
      }
	   return 0;
	}
   length = sizeof( sin ) ;
   getsockname( sok, (struct sockaddr *) &sin, &length );
   our_ip = sin.sin_addr.s_addr;
   our_port = sin.sin_port;

   if (Verbose )
   {
      #ifdef FUNNY_MSGS
         M_fdprint( aux, "Our port is %d, take her to sea Mr. Mordoch.\n", ntohs( sin.sin_port ) );
      #else
         M_fdprint( aux, "The port that will be used for tcp ( not yet implemented ) is %d\n", ntohs( sin.sin_port ) );
      #endif
   }
   if ( Verbose )
   {
      M_fdprint( aux, "Connected to %s, waiting for response\n", hostname );
   }
   return sok;
}
#endif 

/******************************************
Handles packets that the server sends to us.
*******************************************/
void Handle_Server_Response( SOK_T sok )
{
   srv_net_icq_pak pak;
   static DWORD last_seq=-1;
#ifdef STiK
	/*int16 s;*/
	DWORD s;
	NDB *our_packet;
#else
   int s;
#endif

# ifdef STiK 
   our_packet = SOCKREAD( sok );
   memcpy(&pak.head.ver,our_packet->ndata,our_packet->len);

   s = our_packet->len;
   
   if (our_packet->ptr)           /* blk->ptr will never be NULL, though */
	  KRfree(our_packet->ptr);

   KRfree((char *)our_packet);

#else
   s = SOCKREAD( sok, &pak.head.ver, sizeof( pak ) - 2  );

   if ( s < 0 )
   {
   		printf("failure on sockread\r\n");
	   	return;
	}
#endif

      
#if 0
   M_print( "\nCmd : %04X\t",Chars_2_Word( pak.head.cmd ) );
   M_print( "Ver : %04X\t",Chars_2_Word( pak.head.ver ) );
   M_print( "Seq : %08X\t",Chars_2_DW( pak.head.seq ) );
   M_print( "Ses : %08X\n",Chars_2_DW( pak.head.session ) );
#endif

#if ICQ_VER == 0x0005
  if ( Chars_2_DW( pak.head.session ) != our_session ) {
     if ( Verbose ) {
       	R_undraw();
        M_print( "Got a bad session ID %08X with CMD %04X ignored.\n", 
            Chars_2_DW( pak.head.session ), Chars_2_Word( pak.head.cmd ) );
	R_redraw();
     }
     return;
  }
#endif
  /* !!! TODO make a check checksum routine to verify the packet further */
/*   if ( ( serv_mess[ Chars_2_Word( pak.head.seq2 ) ] ) && 
      ( Chars_2_Word( pak.head.cmd ) != SRV_NEW_UIN ) )*/
   if ( ( last_seq == Chars_2_DW( pak.head.seq ) ) && 
      ( Chars_2_Word( pak.head.cmd ) != SRV_NEW_UIN ) )
    {
      if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK ) /* ACKs don't matter */
      {
         if ( Verbose ) {
       	    R_undraw();
            M_print( "\nIgnored a message cmd  %04x\n", Chars_2_Word( pak.head.cmd )  );
	    R_redraw();
	 }
         ack_srv( sok, Chars_2_DW( pak.head.seq ) ); /* LAGGGGG!! */
         return;
      }
   }
   if ( Chars_2_Word( pak.head.cmd ) != SRV_ACK )
   {
      serv_mess[ Chars_2_Word( pak.head.seq2 ) ] = TRUE;
      last_seq = Chars_2_DW( pak.head.seq );
      ack_srv( sok, Chars_2_DW( pak.head.seq ) );
   }

#ifdef STiK
   /* Don't ask me why it has to be this way.  Thank PureC I guess */
   Server_Response( sok, pak.head.check + 4, s - ( sizeof( pak.head ) - 2 ), 
#else
   Server_Response( sok, pak.data, s - ( sizeof( pak.head ) - 2 ), 
#endif
      Chars_2_Word( pak.head.cmd ), Chars_2_Word( pak.head.ver ),
      Chars_2_DW( pak.head.seq ), Chars_2_DW( pak.head.UIN ) );
}

/**********************************************
Verifies that we are in the correct endian
***********************************************/
void Check_Endian( void )
{
   int i;
   char passwd[10];
   
   passwd[0] = 1;
   passwd[1] = 0;
   passwd[2] = 0;
   passwd[3] = 0;
   passwd[4] = 0;
   passwd[5] = 0;
   passwd[6] = 0;
   passwd[7] = 0;
   passwd[8] = 0;
   passwd[9] = 0;
   i = *  ( DWORD *) passwd;
   if ( i == 1 )
   {
      M_print( "Using intel byte ordering." );
   }
   else
   {
      M_print( "Using motorola byte ordering." );
   }
}

/******************************
Idle checking function
added by Warn Kitchen 1/23/99
******************************/
void Idle_Check( SOK_T sok )
{
#ifdef STiK
	time_t tm;
#else
   int tm;
#endif
   tm = ( time( NULL ) - idle_val );
   if ( ( Current_Status == STATUS_AWAY || Current_Status == STATUS_NA )
           && tm < away_time && idle_flag == 1) {
      icq_change_status(sok,STATUS_ONLINE);
      R_undraw ();
      M_print( "\nAuto-Changed status to " );
      Print_Status(Current_Status);
      M_print( " " );
      Time_Stamp();
      M_print( "\n" );
      R_redraw ();
      idle_flag=0;
      return;
   }
   if ( (Current_Status == STATUS_AWAY) && (tm >= (away_time*2)) && (idle_flag == 1) ) {
      icq_change_status(sok,STATUS_NA);
      R_undraw ();
      M_print( "\nAuto-Changed status to " );
      Print_Status(Current_Status);
      M_print( " " );
      Time_Stamp();
      M_print( "\n" );
      R_redraw ();
      return;
   }
   if ( Current_Status != STATUS_ONLINE && Current_Status != STATUS_FREE_CHAT ) {
      return;
   }
   if(tm>=away_time) {
      icq_change_status(sok,STATUS_AWAY); 
      R_undraw ();
      M_print( "\nAuto-Changed status to " );
      Print_Status(Current_Status);
      M_print( " " );
      Time_Stamp();
      M_print( "\n" );
      R_redraw ();
      idle_flag=1;
   }
   return;
}

#ifdef STiK
#define CON 2

/* Put 'STIK' cookie value into drivers */

typedef struct {
    long cktag;
    long ckvalue;
} ck_entry;

static long init_drivers(void)
{
	long i = 0;
	ck_entry *jar = *((ck_entry **) 0x5a0);

	while (jar[i].cktag) {
		if (!strncmp((char *)&jar[i].cktag, CJTAG, 4)) {
			drivers = (DRV_LIST *)jar[i].ckvalue;
			return (0);
		}
		++i;
	}
	return (0);	/* Pointless return value...	*/
}
static int initialise(void)
{
	static long init_drivers(void);

	Supexec(init_drivers);

	/* See if we got a value	*/

	if (drivers == (DRV_LIST *)NULL) {
		Cconws("STiK is not loaded\r\n");
		return (FALSE);
	}

	/* Check Magic number	*/

	if (strcmp(MAGIC, drivers->magic)) {
		Cconws("Magic string doesn't match\r\n");
		return (FALSE);
	}

	/* OK, now we can get the address of the "TRANSPORT" layer
	 * driver.  If this seems unnecessarily complicated, it's
	 * because I tried to create today, what I would like to
	 * use later on.  In future, there will be multiple
	 * drivers accessible via this method.  With luck, your
	 * code will still work with future versions of my software.
	 */

	tpl = (TPL *)get_dftab(TRANSPORT_DRIVER);

	if (tpl == (TPL *)NULL) 
		{
			Cconws("Transport layer *not* loaded\r\n");
			return (FALSE);
		}

	return (TRUE);
}

/* Issue a prompt and read a line from the user */

static int16
getline(SOK_T sok, char *prompt, char *buf, int n)
{
	/* If there's something already there, don't issue prompt */
	/* I'm not tracking this, so remove the test,
	 * probably a good idea though
	 */

	if(CNbyte_count(sok) > 0) /* Interupt if we get a packet */
		return(1);
		
	Cconws(prompt);

	return scr_gets(buf,n);
}

/* an unelegant way to get input from the user */

/*  Keyboard routines
 *
 *  These are necessary so that character entry with echo properly records
 *  cursor position.
 */

long scr_conin(SOK_T sok, int16 echo_flag)
{
    register long key;

	while(TRUE)
		{
			if (Bconstat(CON))
				{
				    key = Bconin(CON);

				    if (echo_flag) 
				    	{
				    	    if ((key & 0xff) == '\r' || ((key & 0xff) == '\n')) 
				    	    	Cconws("\r\n");
				        	else 
					            Cconout((int)(key & 0xff));
					    }
					    
					break;
				}
/* The following bit will interupt what you ar typing if you get a packet
  It was just too annoying fr my tastes in this application

			if(CNbyte_count(sok) > 0) 
				return(-1);
*/
		}
		
    return (key);
}


int16 scr_gets(SOK_T sok, char *s, int16 max_len)
{
    register int16 len = 0;
    register long key;

    while (len < max_len) {

/*		The following bit will interupt what you are typing if
	you get a packet.  It was just too annoying for my tastes
	in this application

		if(CNbyte_count(sok) > 0) 
			return(-1);
*/		
        key = scr_conin(sok,FALSE);

		if (key == -1)
			return(-1);

        if ((key & 0xff) == '\r' || ((key & 0xff) == '\n'))
            break;

        if ((key & 0xff) == '\b') {
            if (len > 0) {
                Cconws("\b \b");
                len -= 1;
            }
        }
        else if ((key & 0xff) == '\t')
            key = ' ';

        else if ((key & 0xff) != 0) {
            Cconout((int)(key &0xff));
            s[len] = key & 0xff;
            len += 1;
        }
    }
    s[len] = '\0';

	Cconws("\r\n");

    return (len);
}

/******************************
Main function connects gets UIN
and passwd and logins in and sits
in a loop waiting for server responses.
******************************/
int main( int argc, char *argv[] )
{
   int16 sok;
   int i;
   time_t next;
   time_t time_delay = 120;
   struct timeval tv;
/*   fd_set readfds; */

   M_print( SERVCOL "Matt's ICQ clone " NOCOL "compiled on %s %s\n" SERVCOL " Version " MICQ_VERSION NOCOL "\n", __TIME__, __DATE__ );
#ifdef FUNNY_MSGS
   M_print( "No Mirabilis client was maimed, hacked, tortured, sodomized or otherwise harmed\nin the making of this utility.\n" );
#else
   M_print( "This program was made without any help from Mirabilis or their consent.\n" );
   M_print( "No reverse engineering or decompilation of any Mirabilis code took place\nto make this program.\n" );
#endif

	if(!initialise())
		return -1;		

   Get_Unix_Config_Info();

   srand( time( NULL ) );
   if ( !strcmp( passwd,"" ) )
   {
      M_print( "Enter password : " );
      Echo_Off();
      M_fdnreadln(STDIN, passwd, sizeof(passwd));
      Echo_On();
   }
   memset( serv_mess, FALSE, 1024 );
   Initialize_Msg_Queue();
   if (argc > 1 )
	   {
	      for ( i=1; i< argc; i++ )
	      {
	         if ( argv[i][0] != '-' )
		         ;
	         else if ( (argv[i][1] == 'v' ) || (argv[i][1] == 'V' ) )
	         {
	            Verbose++;
	         }
	      }
	   }
	   
   Check_Endian();

   sok = Connect_Remote( server, remote_port, STDERR );
   if ( sok < 0 ) 
   {
   	M_print( "Couldn't establish connection\n" );
   	exit( 1 );
   }
   Login( sok, UIN, &passwd[0], our_ip, our_port, set_status );
   next = time( NULL );
   idle_val = time( NULL );
   next += 120;
   next_resend = 10;
   R_init ();
   M_print ("\n");
   Prompt();
   for ( ; !Quit; )
   {
      Idle_Check( sok );

    if (CNbyte_count(sok)>0)
         Handle_Server_Response( sok );

	if (Bconstat(2)) /* should be CON it's really ugly now
						 sorry, this is a bit ugly...   [UH]*/
      {
         idle_val = time( NULL );
/*	 M_print( "%04X\n", sok );*/
	 if (R_process_input ())
		Get_Input( sok );

		
      }

      if ( time( NULL ) > next )
      {
         next = time( NULL ) + time_delay;
         Keep_Alive( sok );
      }

      if ( time( NULL ) > next_resend )
      {
        Do_Resend( sok );
      }
   }
   Quit_ICQ( sok );
   return 0;
}
#else
int main( int argc, char *argv[] )
{
   int sok;
   int i;
   int next;
   int time_delay = 120;
   struct timeval tv;
   fd_set readfds;
#ifdef _WIN32
   WSADATA wsaData;
#endif

   setbuf (stdout, NULL); /* Don't buffer stdout */
   M_print( SERVCOL "Matt's ICQ clone " NOCOL "compiled on %s %s\n" SERVCOL " Version " MICQ_VERSION NOCOL "\n", __TIME__, __DATE__ );
#ifdef FUNNY_MSGS
   M_print( "No Mirabilis client was maimed, hacked, tortured, sodomized or otherwise harmed\nin the making of this utility.\n" );
#else
   M_print( "This program was made without any help from Mirabilis or their consent.\n" );
   M_print( "No reverse engineering or decompilation of any Mirabilis code took place\nto make this program.\n" );
#endif
   Get_Config_Info();
   srand( time( NULL ) );
   if ( !strcmp( passwd,"" ) )
   {
      M_print( "Enter password : " );
      Echo_Off();
      M_fdnreadln(STDIN, passwd, sizeof(passwd));
      Echo_On();
   }
   memset( serv_mess, FALSE, 1024 );
   Initialize_Msg_Queue();
   if (argc > 1 )
   {
      for ( i=1; i< argc; i++ )
      {
         if ( argv[i][0] != '-' )
         ;
         else if ( (argv[i][1] == 'v' ) || (argv[i][1] == 'V' ) )
         {
            Verbose++;
         }
      }
   }
   Check_Endian();
#ifdef _WIN32
   i = WSAStartup( 0x0101, &wsaData );
   if ( i != 0 ) {
#ifdef FUNNY_MSGS
		perror("Windows Sockets broken blame Bill -");
#else
		perror("Sorry, can't initialize Windows Sockets...");
#endif
	    exit(1);
   }
#endif
   sok = Connect_Remote( server, remote_port, STDERR );
   if ( ( sok == -1 ) || ( sok == 0 ) ) 
   {
   	M_print( "Couldn't establish connection\n" );
   	exit( 1 );
   }
   Login( sok, UIN, &passwd[0], our_ip, our_port, set_status );
   next = time( NULL );
   idle_val = time( NULL );
   next += 120;
   next_resend = 10;
   R_init ();
   M_print ("\n");
   Prompt();
   for ( ; !Quit; )
   {
      Idle_Check( sok );
#ifdef UNIX
      tv.tv_sec = 2;
      tv.tv_usec = 500000;
#else
	  tv.tv_sec = 0;
      tv.tv_usec = 100000;
#endif

      FD_ZERO(&readfds);
      FD_SET(sok, &readfds);
#ifndef _WIN32
      FD_SET(STDIN, &readfds);
#endif

      /* don't care about writefds and exceptfds: */
      select(sok+1, &readfds, NULL, NULL, &tv);

      if (FD_ISSET(sok, &readfds))
          Handle_Server_Response( sok );
#if 0
      else
          M_print( "\a" );
#endif      
#if _WIN32
	  if (_kbhit())		/* sorry, this is a bit ugly...   [UH]*/
#else      
	  if (FD_ISSET( STDIN, &readfds ) )
#endif
      {
         idle_val = time( NULL );
/*	 M_print( "%04X\n", sok );*/
	 if (R_process_input ())
		Get_Input( sok );
      }

      if ( time( NULL ) > next )
      {
         next = time( NULL ) + time_delay;
         Keep_Alive( sok );
      }

      if ( time( NULL ) > next_resend )
      {
        Do_Resend( sok );
      }
#ifdef UNIX
      while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */
#endif
   }
   Quit_ICQ( sok );
   return 0;
}
#endif /* STiK */

  
