/* Acidblood network routines */
/*
Acidblood IRC Bot
Copyright (C) 1997-2000 Bryan Schwab
bryan@darkice.com

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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#include "acidblood.h"

#include <sys/types.h>
#include <sys/times.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <ctype.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int getline_from_server(
struct botstruct *botinfo,
char *c,
int s)
{
   int x=0,len;
   struct timeval tv;
   fd_set infd;

   extern int errno;

   tv.tv_sec=(60 * (botinfo->timeout));
   tv.tv_usec=0;

   /* Setup the input descriptor for watch */
   FD_ZERO(&infd);
   FD_SET(s,&infd);

   if (globalint) {
	/* we were interrupted, keep going */
        #ifdef DEBUG
           fprintf(stderr, "Resuming interruption. x=%d\n",globalx);
 	#endif
	x=globalx;
   }
   else {
	/* reset pointer */
        c[0]='\0';
   }

   /*
   #ifdef DEBUG
      printf("Selecting...\n");
   #endif
   */

   while(1) {

      len=select(s+1,&infd,NULL,NULL,&tv);

      if(len > 0) {
         if(recv(s, &c[x], 1, 0) < 0) {
	    perror("recv");
	    return(-1);
         }
         /* if(c[x]=='\r' || c[x]=='\n') { */
         /* all strings end in \r\n, so look for the \n */
         if(c[x]=='\n') {
            c[x]='\0';

            #ifdef DEBUG
               fprintf(stderr,"Final value of x=%d\n", x);
            #endif

            break;
         }

	 if (x > 1024) {
		/* hmm, looks like buffer got away somewhere, return an error */
		return(-1);
	 }
         x++;
	 
      } 
      else if (len == 0) {
	 /* timeout */
	 fprintf(stderr,"select error\n");
         perror("select");
         return(-2);
      }
      else if (len == -1){
         /* error */

	 if (errno == EINTR) {
		/* setup the variables to return */
                #ifdef DEBUG
		   fprintf(stderr, "Interrupted! Setting globalx=%d\n",x);
                #endif

		globalx = x;
                globalint++;
	 }
         else {
	    fprintf(stderr,"select error\n");
	    perror("select");
         }

         return(-1);
      }

   }
   totalbytes+=x;

   /*
   #ifdef DEBUG
      printf("\n");
      printf("Bytes=%d\n",x);
      printf("Total Bytes=%d\n",totalbytes);
   #endif
   */

   globalint=0;
   return (x);
}


/* connect to the server */
/* at this point, everything is put into log files */
int
connect_to_server(
struct botstruct *botinfo,
FILE **fp_socket,
FILE **fp_log,
int *s)
{
   struct sockaddr_in sin;
   struct hostent *hp;
   /* defined globally now */
   /* char *input; */
   char output[80];
   char temp[50];
   char key[50];
   char *tmpptr;
   char *codeptr;
   int code;
   char done=0;
   
   
   if ((input=malloc(1000))==NULL) {
      fprintf(stderr, "connect_to_server: Malloc error!\n");
      return(-1);
   }

   if ((hp=gethostbyname(botinfo->server)) == NULL) {
      fprintf(*fp_log, "Network Error: Unknown host %s\n",botinfo->server);
      return(-1);
   }

   if ((*s=socket(AF_INET,SOCK_STREAM,0)) < 0) {
      fprintf(*fp_log, "Network Error: Cannot create socket!\n");
      return(-1);
   }

   sin.sin_family=AF_INET;
   sin.sin_port=htons(botinfo->port);
   bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);

   /* timeout in 30 seconds */
   alarm(30);

   if (connect(*s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
      fprintf(*fp_log, "Network Error: Cannot connect to port.\n");
      return(-1);
   }
  
   /* reset alarm */
   alarm(0);

   /* this could be removed */
   if ((*fp_socket=fdopen(*s,"r"))==NULL) {
      return(-1);
   }

   printf("Registering...\n");
   
   snprintf(output,80,"USER %s %s +iw :%s\n",botinfo->user,botinfo->server,botinfo->fname);
   if ((send(*s,output,strlen(output),0))==-1) {
      return(-1);
   }
   snprintf(output,80,"NICK %s\n",botinfo->nick);
   if ((send(*s,output,strlen(output),0))==-1) {
      return(-1);
   }

   
   /* Now we have told the server all we should have to */
   done=0;
   while(1)
   {
      getline_from_server(botinfo, input, *s);

    
   #ifdef DEBUG
      printf("connect> %s\n",input);
   #endif
     
     /* if line is null, crashes, FIX */

      /* Set codeptr to point to the first character after the first space */
      codeptr=strchr(input, ' ')+1;

       if(isdigit(codeptr[0])) {
       /* if(codeptr[0] >= '0' && codeptr[0] <= '9') { */
         sscanf(codeptr,"%d",&code);
       }
       else {
         code=-1;
       }
       

      if(input[0]==':') { /* Message from server or user */
      
         if(code > 0) { /* Numeric code from server */
            switch(code) {
               case   1: /* Welcome */
                  /* We could pull the irc server name from this.. just so we have it */
                  break;
               case 433: /* Nick already in use */
                  if(strncmp(botinfo->nick, botinfo->altnick, strlen(botinfo->altnick)+1)==0) {
                     fprintf(stderr,"Regular and alternate nick names are in use. Exiting.\n");
                     fprintf(*fp_log,"Regular and alternate nick names are in use. Exiting.\n");
                     /* This exit is a bit dangerous... nothin is free()ed */
                     exit(0);
                  }
                  fprintf(*fp_log,"Nickname is already in use, using alternate.\n");
                  snprintf(output,80,"NICK %s\n",botinfo->altnick);
                  if ((send(*s,output,strlen(output),0))==-1)
                     return(-1);

                  /* set new bot nick pointer */
                  if (botinfo->nick!=NULL) {
                     free(botinfo->nick);
                  }
                  botinfo->nick=malloc(strlen(botinfo->altnick)+1);
                  strncpy(botinfo->nick, botinfo->altnick, strlen(botinfo->altnick)+1);

                  break;
                  
               case 372: /* Motd body */
                  break;
               case 376: /* Motd end, this is where we stop trying to conenct to a server  */
                  done=1;
                  break;
               case 422: /* Motd missing */
		  done=1;
                  break;
            }
         } else { /* PRIVMSG, etc, etc */
            /* We aren't concerned about anything like that in here */
            ;
         
         }
         
      
      } else if(memcmp(input,"PING",4)==0) {
         tmpptr=strtok(input,":");
         tmpptr=strtok(NULL,"\n");

         snprintf(output,80,"PONG :%s\n",tmpptr);
         if ((send(*s,output,strlen(output),0))==-1) {
            fprintf(*fp_log,"Error sending to server.\n");
            exit(-1);
         }
         /* hmmmm, not good if we get an error! */
         /* done=1; */
         
      } else if(memcmp(input,"NOTICE",6)==0) {
         /* Ident, and other stuff in here.  on efnet servers, 
             this is an excellent place to put USER and NICK */
      } else if (memcmp(input,"ERROR",5)==0) {
         fprintf(*fp_log,"%s",input);
         fflush(*fp_log);
	 printf("%s",input);
         if ((strstr(input,"No Authorization"))!=NULL) {
            return(-2);
         }
         if ((strstr(input,"No more connections"))!=NULL) {
            return(-3);
         }
	 if ((strstr(input, "Identd Required"))!=NULL) {
            return(-4);
         }
	 if ((strstr(input, "Closing Link"))!=NULL) {
	    return(-5);
         }
      }
      if(done) break;
   }
      
   curr3=top3;
   prev3=top3;

   if (botinfo->ns==1) {}
   else {
    while (get_channel(channeldata,temp,key) > 0) {
      snprintf(output,80,"JOIN %s %s\n",temp,key);
      if ((send(*s,output,strlen(output),0))==-1) {
         fprintf(*fp_log,"Error sending to server.\n");
         return(-1);
      }
    }
   }

 /* if an away message exists, set away */
 if (botinfo->awaymsg != NULL) {
   snprintf(output,80,"AWAY :%s\n",botinfo->awaymsg);
   if ((send(*s,output,strlen(output),0))==-1) {
      fprintf(*fp_log,"Error sending to server.\n");
      return(-1);
   }
 }

   return(0);
}
