
/* This is a simple irc program.  It's not pretty, but it's free.  :-)    */
/* See the beginning of the program for the version number (date really). */

/* Alton O. Moore III       Please leave my little advertisement here,  */
/* alton@alton-moore.net    as I might get some work out of it some     */
/* www.alton-moore.net      day (a job programming over the internet?). */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>

extern int errno;  /* Error codes are returned here. */

struct hostent *hostent;
struct sockaddr_in sockaddr;
int sock,status,hostaddr,irc_port=6667,socket_option=0;
char buffer[4096];
fd_set socket_read_status;
struct timeval socket_time_structure;

#define SERVER_PING_INTERVAL 30

time_t current_time,start_time,last_message_from_server_time,
 last_ping_to_server_time;
char local_host_name[64];
char irc_server[128],irc_nickname[16],irc_channel[32];
char temp_string[128],form_data_string[255];
int temp_int,temp_string_pointer;
char socket_input_buffer[1024],terminal_input_buffer[1024];
int socket_input_buffer_counter,terminal_input_buffer_counter,result_code;
char field_1[256],field_2[256],field_3[256];
int socket_input_buffer_pointer,output_pointer;


/*--------------------------- Subroutines ------------------------------------*/

void parse_server_message(void)  /* Take 1st 3 fields out of message. */
{
socket_input_buffer_pointer = 0;
output_pointer= 0;
while ((socket_input_buffer[socket_input_buffer_pointer] != ' ') &&
 (socket_input_buffer[socket_input_buffer_pointer] != 0))
  field_1[output_pointer++] = socket_input_buffer[socket_input_buffer_pointer++];
field_1[output_pointer] = 0;
field_2[0] = 0;
if (socket_input_buffer[socket_input_buffer_pointer])  /* Not null? */
  {
  socket_input_buffer_pointer++;  /* Skip the space character. */
  output_pointer = 0;
  while ((socket_input_buffer[socket_input_buffer_pointer] != ' ') &&
   (socket_input_buffer[socket_input_buffer_pointer] != 0))
    field_2[output_pointer++] = socket_input_buffer[socket_input_buffer_pointer++];
  field_2[output_pointer] = 0;
  }
field_3[0] = 0;
if (socket_input_buffer[socket_input_buffer_pointer])  /* Not null? */
  {
  socket_input_buffer_pointer++;
  output_pointer = 0;
  while ((socket_input_buffer[socket_input_buffer_pointer] != ' ') &&
   (socket_input_buffer[socket_input_buffer_pointer] != 0))
    field_3[output_pointer++] = socket_input_buffer[socket_input_buffer_pointer++];
  field_3[output_pointer] = 0;
  }
}


/*----------------------------- Main Program ---------------------------------*/
int main(int argc,char *argv[])
{
int argument_count;
argument_count = argc - 1;

printf("EZ-IRC -- Version 10/21/1999 -- Alton Moore (www.alton-moore.net)\n");
printf("\n");

if (argument_count < 3)             /* Server address not supplied? */
  {
  printf("To automate, invoke with the following three arguments:\n");
  printf("  ez-irc (server_address) (nickname) (channel_name)\n");
  printf("\n");
  printf("Server address:   ");
  gets(irc_server);
  printf("Nickname:         ");
  gets(irc_nickname);
  printf("Channel (with #): ");
  gets(irc_channel);
  }
else
  {
  strcpy(irc_server,argv[1]);
  strcpy(irc_nickname,argv[2]);
  strcpy(irc_channel,argv[3]);
  }

gethostname(local_host_name,sizeof(local_host_name));
/* printf("Local host name: %s\n",local_host_name); */

printf("(Connecting to irc server %s with nickname %s now.)\n",irc_server,
 irc_nickname);
strcpy(temp_string,irc_server);
hostent = gethostbyname(temp_string);  /* Do DNS lookup. */
if (hostent == NULL)
  {
  printf("Couldn't resolve IRC server address; quitting.\n");
  return(1);
  }
sock = socket(AF_INET,SOCK_STREAM,0);  /* Allocate a socket. */
if (sock < 0)
  {
  printf("Unable to allocate a socket; quitting.\n");
  return(1);
    }
memset(&sockaddr,0,sizeof(sockaddr));  /* Initialize sockaddr fields. */
sockaddr.sin_family = AF_INET;
memcpy((char *)&(sockaddr.sin_addr.s_addr),(char *)hostent->h_addr_list[0],
 (size_t)hostent->h_length);
sockaddr.sin_port = htons(irc_port);

/* Connect to remote port now. */
if (connect(sock,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) < 0)
  {
  printf("Unable to connect to irc server; quitting.\n");
  close(sock);
  return(1);
  }
fcntl(sock,F_SETFL,O_NONBLOCK);  /* Set socket to non-blocking. */
fcntl(1,F_SETFL,O_NONBLOCK);     /* Set keyboard too. */


/*--------------------------------------------------------------*/
/* Now we're ready to talk with the server, with text messages. */

strcpy(buffer,"NICK ");
strcat(buffer,irc_nickname);
strcat(buffer,"\r\n");
write(sock,buffer,strlen(buffer));

strcpy(buffer,"USER ");
strcat(buffer,irc_nickname);
strcat(buffer," ");
strcat(buffer,local_host_name);
strcat(buffer," ");
strcat(buffer,irc_server);
strcat(buffer," :EZ-IRC Program\r\n");
write(sock,buffer,strlen(buffer));

sleep(5);  /* Wait for server registration.  Check results instead, soon. */

socket_input_buffer_counter   = 0;
terminal_input_buffer_counter = 0;

start_time = time((time_t *) 0);  /* So we know when to join channel (temporary). */
last_message_from_server_time = time((time_t *) 0);  /* Initialize. */


/*-----------------------------------------------*/
event_loop:

result_code = read(sock,buffer,1);  /* Try to read one char. from server. */
if (result_code == 1)  /* Something received? */
  {
  if (buffer[0] == 13)   /* CR? */
    buffer[0] = 13;      /* Do nothing. */
  if (buffer[0] != 10)   /* Anything other than LF?  Add to string. */
    socket_input_buffer[socket_input_buffer_counter++] = buffer[0];
  if (buffer[0] == 10)   /* LF? */
    {
    socket_input_buffer[socket_input_buffer_counter] = 0;  /* Terminate. */
    socket_input_buffer_counter = 0;  /* Reset this string's counter. */
    last_message_from_server_time = time((time_t *) 0);
    /* Now decide what to do with the received line. */
    parse_server_message();
    if (!strncmp(socket_input_buffer,"PING",4))  /* Ping received? */
      {
/*      printf("(Responding to ping from server.)\n"); */
      strcpy(buffer,socket_input_buffer);
      buffer[1] = 'O';
      strcat(buffer,"\r\n");
      write(sock,buffer,strlen(buffer));
      goto event_loop;
      }
    if (!strcmp(field_2,"PONG"))  /* Have we been pinging the server? */
      goto event_loop;  /* We've already noted the time, so that's it. */
    if (!strcmp(field_2,"PRIVMSG"))  /* A message? */
      {
      printf("<");
      temp_int = 1;
      while (field_1[temp_int] != '!')
        printf("%c",field_1[temp_int++]);
      printf("> ");
      while (socket_input_buffer[socket_input_buffer_pointer++] != ':')
        { };
      while (socket_input_buffer[socket_input_buffer_pointer] != 0)
        printf("%c",socket_input_buffer[socket_input_buffer_pointer++]);
      printf("\n");
      goto event_loop;
      }
    if (!strcmp(field_2,"001"))  /* Registered ok? */
      {
      printf("(Joining channel %s now.)\n",irc_channel);
      strcpy(buffer,"JOIN ");
      strcat(buffer,irc_channel);
      strcat(buffer,"\r\n");
      write(sock,buffer,strlen(buffer));
      }
    printf("%s\n",socket_input_buffer);  /* Print the line then.... */
    }
  goto event_loop;
  }

result_code = fgetc(stdin);  /* Try to read one char. from keyboard. */
if (result_code != -1)  /* Something received? */
  {
  if (result_code == 13)   /* CR? */
    result_code = 13;      /* Do nothing. */
  if (result_code != 10)   /* Anything other than LF?  Add to string. */
    terminal_input_buffer[terminal_input_buffer_counter++] = result_code;
  if (result_code == 10)   /* LF? */
    {
    terminal_input_buffer[terminal_input_buffer_counter] = 0;  /* Terminate. */
    terminal_input_buffer_counter = 0;  /* Reset this string's counter. */
    /* Now decide what to do with the entered line. */
    if (!strncmp(terminal_input_buffer,"/quit",5))
      goto exit_point;
    if (terminal_input_buffer[0] == '/')  /* Some other sort of command? */
      {
      strcpy(buffer,terminal_input_buffer+1);
      strcat(buffer,"\r\n");
      write(sock,buffer,strlen(buffer));
      goto event_loop;
      }
    strcpy(buffer,"PRIVMSG ");  /* Build the PRIVMSG line. */
    strcat(buffer,irc_channel);
    strcat(buffer," :");
    strcat(buffer,terminal_input_buffer);
    strcat(buffer,"\r\n");
    write(sock,buffer,strlen(buffer));
    }
  goto event_loop;
  }

current_time = time((time_t *) 0);  /* Too long since hearing from server? */
if (current_time - last_message_from_server_time > 70)
  {
  printf("(Connection to server died, or server doesn't respond to pings.)\n");
  goto exit_point;
  }
if ((current_time - last_message_from_server_time > SERVER_PING_INTERVAL) &&
 (current_time - last_ping_to_server_time > SERVER_PING_INTERVAL))
  {
/*  printf("(No activity from server for %d seconds; pinging server.)\n",  */
/*   SERVER_TIMEOUT_INTERVAL);  */
  strcpy(buffer,"PING ");
  strcat(buffer,local_host_name);
  strcat(buffer,"\r\n");
  write(sock,buffer,strlen(buffer));
  last_ping_to_server_time = time((time_t*) 0);
  }
 
sleep(1);
goto event_loop;


/*--------------------------------------------------------*/
exit_point:
printf("(Quitting now.)\n");
strcpy(buffer,"QUIT\r\n");  /* Log out from IRC server. */
write(sock,buffer,strlen(buffer));
shutdown(sock,2);           /* Close socket connection. */
close(sock);
return(0);
}

