/*
   main.c -- handles:
   changing nicknames when the desired nick is in use
   flood detection
   signal handling
   telnet code translation
   command line arguments
   connecting to a server (bot and helpbot)
   interpreting server responses (main loop)

   dprintf'ized, 15nov95
 */
/*
   This file is part of the eggdrop source code
   copyright (c) 1997 Robey Pointer
   and is distributed according to the GNU general public license.

   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 2 of the License, 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 w
ith this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author (Robey Pointer) can be reached at:  robey@netcom.com
 */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#ifdef STOP_UAC			/* osf/1 complains a lot */
#include <sys/sysinfo.h>
#define UAC_NOPRINT    0x00000001	/* Don't report unaligned fixups */
#endif
/* some systems have a working sys/wait.h even though configure will */
/* decide it's not bsd compatable.  oh well. */
#include "eggdrop.h"
#include "chan.h"
#include "tclegg.h"
#ifdef MODULES
#include "modules.h"

#else
#ifndef NO_FILE_SYSTEM
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include "../lush.h"
#include "files.h"
#endif
#endif

/* number of seconds to wait between transmitting queued lines to the server */
/* lower this value at your own risk.  ircd is known to start flood control */
/* at 512 bytes/2 seconds */
#define msgrate 0

/* solaris needs this */
#define _POSIX_SOURCE 1

extern char botname[];
extern char origbotname[];
extern int dcc_total;
extern struct dcc_t dcc[];
extern char dccdir[];
extern char dccin[];
extern char admin[];
extern char notefile[];
extern char newserver[];
extern char newserverpass[];
extern int newserverport;
extern int lastsock;
extern int conmask;
extern struct userrec *userlist;
extern int cache_hit, cache_miss;
extern char userfile[];
extern struct chanset_t *chanset;
extern int ban_time;
extern int ignore_time;
extern char botnetnick[];
extern log_t logs[];
extern char ctcp_finger[];
extern char ctcp_userinfo[];
extern char ctcp_version[];
extern Tcl_Interp *interp;
extern int default_port;

#ifndef MODULES
extern char tempdir[];
#include "mod/transfer.mod/transfer.c"
#include "mod/filesys.mod/filedb.c"
#include "mod/filesys.mod/files.c"
#endif
/*
   Please use the PATCH macro instead of directly altering the version
   string from now on (it makes it much easier to maintain patches).
   Also please read the README file regarding your rights to distribute
   modified versions of this bot.

   Note: Loading more than 10 patches could make your patch level "roll
   over" to the next release!  So try not to do that. :)
 */
#ifdef MODULES
char egg_version[1024] = "1.1.6+tPACK 2.3";
#else
char egg_version[1024] = "1.1.6+tPACK 2.3";
#endif
int egg_numver = 1010619;
int min_share = 1010619;	/* minimum version I will share with */

/* socket that the server is on */
int serv = (-1);
/* run in the background? */
#ifdef __CYGWIN32__
int backgrd = 0;
#else
int backgrd = 1;
#endif
/* successful connect yet? */
int online = 0;
/* foreground: constantly display channel stats? */
int con_chan = 0;
/* foreground: use the terminal as a party line? */
int term_z = 0;
/* trying to connect to a server right now? */
time_t trying_server = 0L;
/* how lagged (in seconds) is the server? */
int server_lag = 0;
/* bot's username */
char botuser[21];
/* bot's real name field */
char botrealname[121];
/* our current host */
char bothost[121];
/* our server */
char botserver[121];
/* port # to connect to */
int botserverport = 6667;
/* name of the config file */
char configfile[121] = "egg.log";
/* possible alternate nickname to use */
char altnick[NICKLEN + 1] = "";
/* temporary thing for nick changes */
char newbotname[NICKLEN + 1];
/* current position in server list: */
int curserv = 0;
/* directory of help files (if used) */
char helpdir[121];
/* directory for text files that get dumped */
char textdir[121] = "";
/* MSG flood */
int flood_thr = 5;
int flood_time = 60;
/* PUBLIC flood */
int flood_pub_thr = 20;
int flood_pub_time = 30;
/* JOIN flood */
int flood_join_thr = 10;
int flood_join_time = 30;
/* CTCP flood */
int flood_ctcp_thr = 3;
int flood_ctcp_time = 60;
/* what, if anything, to send to the server on connection */
char initserver[121];
/* never erase logfiles, no matter how old they are? */
int keep_all_logs = 0;
/* context storage for fatal crashes */
#ifdef EBUG
char cx_file[16][30];
int cx_line[16];
int cx_ptr = 0;
#else
char cx_file[30];
int cx_line;
#endif
/* unix-time that the bot loaded up */
time_t online_since;
/* bot's user@host (refreshed whenever the bot joins a channel) */
/* may not be correct user@host BUT it's how the server sees it */
char botuserhost[121];
/* using bot in make-userfile mode?  (first user to 'hello' becomes master) */
int make_userfile = 0;
/* never give up when connecting to servers? */
int never_give_up = 0;
/* permanent owner(s) of the bot */
char owner[121] = "";
/* keep trying to regain my intended nickname? */
int keepnick = 0;
/* set when i unidle myself, cleared when i get the response */
int waiting_for_awake = 0;
/* name of the file for the pid to be stored in */
char pid_file[40];
/* how many minutes past the hour to save the userfile? */
int save_users_at = 0;
/* how many minutes past the hour to notify users of notes? */
int notify_users_at = 0;
/* when (military time) to switch logfiles */
int switch_logfiles_at = 300;
/* version info (long form) */
char version[81];
/* version info (short form) */
char ver[41];
/* patch info */
char egg_xtra[1024];
/* server connection time */
time_t server_online = 0L;
/* send stuff to stderr instead of logfiles? */
int use_stderr = 1;
/* .restart has been called, restart a.s.a.p. */
int do_restart = 0;

void fatal PROTO2(char *, s, int, recoverable)
{
   int i;
   putlog(LOG_MISC, "*", "* %s", s);
   flushlogs();
   if (serv >= 0)
      killsock(serv);
   for (i = 0; i < dcc_total; i++)
      killsock(dcc[i].sock);
   unlink(pid_file);
   exit(1);
}

/* for mem.c : calculate memory we SHOULD be using */
int expected_memory()
{
   int tot;
   context;
   tot = expmem_chan() + expmem_chanprog() + expmem_misc() + expmem_users() +
       expmem_dccutil() + expmem_botnet() + expmem_tcl() + expmem_tclhash() +
       expmem_net();
#ifndef MODULES
   tot += expmem_assoc() + expmem_blowfish();
#ifndef NO_FILE_SYSTEM
   tot += expmem_fileq();
#endif
#else
   tot += expmem_modules(0);
#endif
   return tot;
}

/* fix the last parameter... if it starts with ':' then accept all of it,
   otherwise return only the first word */
void fixcolon PROTO1(char *, s)
{
   if (s[0] == ':')
      strcpy(s, &s[1]);
   else
      split(s, s);
}

#ifndef NO_IRC

/* given <in> (raw stuff from server), pull off who it's from & the code */
void parsemsg PROTO4(char *, in, char *, from, char *, code, char *, params)
{
   char *p;
   from[0] = 0;
   if (in[0] == ':') {
      strcpy(in, &in[1]);
      p = strchr(in, ' ');
      if (p == NULL) {
	 from[0] = params[0] = 0;
	 strcpy(code, in);
	 return;
      }
      strcpy(params, p + 1);
      *p = 0;
      strcpy(from, in);
      *p = ' ';
      p++;
      strcpy(in, p);
   }
   p = strchr(in, ' ');
   if (p == NULL) {
      strcpy(code, in);
      params[0] = 0;
      return;
   }
   *p = 0;
   strcpy(code, in);
   *p = ' ';
   strcpy(params, p + 1);
}

/* ping from server */
void gotpong PROTO2(char *, from, char *, msg)
{
   split(NULL, msg);
   fixcolon(msg);		/* scrap server name */
   waiting_for_awake = 0;
   server_lag = time(NULL) - my_atoul(msg);
   if (server_lag > 99999) {
      /* bogus */
      server_lag = (-1);
   }
}

/* 302 : USERHOST to be used at a later date in a tcl command mebbe */
void got302 PROTO2(char *, from, char *, msg)
{
/*  char userhost[UHOSTLEN],nick[NICKLEN];
   int i,oper=0,away=0;
   context;
   split(NULL,msg); fixcolon(msg); strcpy(s,msg);
   * now we have to interpret this shit *
   * <nick>['*']'='<'+'|'-'><hostname> *
   for (i=0; i<strlen(s); i++) {
   if (s[i]==61) s[i]=' ';
   if (s[i]==42) oper=1;
   if (s[i]==43) away=1;
   }
   split(nick,s); strcpy(userhost,&s[1]);
   if (oper) nick[strlen(nick)-1]=0;

   I had specific uses for what I put here but I 
   thought I would throw the above in for starters
 */
}

/* trace failed! meaning my nick is not in use!
   206 (undernet)
   401 (other non-efnet)
   402 (Efnet)
 */
void trace_fail PROTO2(char *, from, char *, msg)
{
//   debug1("%s\n", msg);
//   if (strcasecmp(botname, origbotname) == 0)
//      return;
//   putlog(LOG_MISC, "*", "Switching back to nick %s", origbotname);
//   strcpy(newbotname, botname);	/* save, just in case */
//   strcpy(botname, origbotname);
//   tprintf(serv, "NICK %s\n", botname);
}

/* 432 : bad nickname */
void got432 PROTO2(char *, from, char *, msg)
{
   putlog(LOG_MISC, "*", "Server says my nickname is invalid.");
   /* make random nick. */
   if (!newbotname[0]) {	/* if it's due to an attempt to change nicks .. */
      strcpy(newbotname, botname);	/* store it, just in case it's raist playing */
      makepass(botname);
      botname[NICKLEN] = 0;	/* raist sux :P */
      tprintf(serv, "NICK %s\n", botname);
   } else {			/* go back to old nick */
      strcpy(botname, newbotname);
      newbotname[0] = 0;
   }
}

/* 433 : nickname in use */
/* change nicks till we're acceptable or we give up */
void got433 PROTO2(char *, from, char *, msg)
{
   char c, *oknicks = "^-_\\[]`", *p;
   /* could be futile attempt to regain nick: */
   if (newbotname[0]) {
      tprintf(serv, "NICK %s\n", newbotname);
      strcpy(botname, newbotname);
      newbotname[0] = 0;
      return;
   }
   /* alternate nickname defined? */
   if ((altnick[0]) && (strcasecmp(altnick, botname) != 0)) {
      strcpy(botname, altnick);
   }
   /* if alt nickname failed, drop thru to here */
   else {
      c = botname[strlen(botname) - 1];
      p = strchr(oknicks, c);
      if (((c >= '0') && (c <= '9')) || (p != NULL)) {
	 if (p == NULL) {
	    if (c == '9')
	       botname[strlen(botname) - 1] = oknicks[0];
	    else
	       botname[strlen(botname) - 1] = c + 1;
	 } else {
	    p++;
	    if (!*p)
	       botname[strlen(botname) - 1] = 'a' + random() % 26;
	    else
	       botname[strlen(botname) - 1] = (*p);
	 }
      } else {
	 if (strlen(botname) == NICKLEN)
	    botname[strlen(botname) - 1] = '0';
	 else {
	    botname[strlen(botname) + 1] = 0;
	    botname[strlen(botname)] = '0';
	 }
      }
   }
   putlog(LOG_MISC, "*", "NICK IN USE: Trying '%s'", botname);
   tprintf(serv, "NICK %s\n", botname);
}

/* 437 : nickname juped (Euronet) */
void got437 PROTO2(char *, from, char *, msg)
{
   char s[512];
   struct chanset_t *chan;
   split(NULL, msg);
   split(s, msg);
   if (strchr("#&+", s[0]) != NULL) {
      chan = findchan(s);
      if (chan != NULL) {
	 if (chan->stat & CHANACTIVE) {
	    putlog(LOG_MISC, "*", "Can't change nickname on %s.  Is my nickname banned?", s);
	    got433(from, NULL);
	 } else {
	    putlog(LOG_MISC, "*", "Channel %s is juped. :(", s);
	 }
      }
   } else {
      putlog(LOG_MISC, "*", "Nickname has been juped.");
      got433(from, NULL);
   }
}

/* 451 : Not registered */
void got451 PROTO2(char *, from, char *, msg)
{
/* usually if we get this then we really fucked up somewhere
   or this is a non-standard server, so we log it and kill the socket
   hoping the next server will work :) -poptix */
   putlog(LOG_MISC, "*", "%s says I'm not registered, trying next one.", from);
   tprintf(serv, "QUIT :You have a fucked up server.\<#  =#                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          tch_file PROTO2(register unsigned char *, m, register unsigned char *, n)
{
   unsigned char *ma = m, *lsm = 0, *lsn = 0;
   int match = 1;
   register unsigned int sofar = 0;

   /* take care of null strings (should never match) */
   if ((m == 0) || (n == 0) || (!*n))
      return NOMATCH;
   /* (!*m) test used to be here, too, but I got rid of it.  After all,
      If (!*n) was false, there must be a character in the name (the
      second string), so if the mask is empty it is a non-match.  Since
      the algorithm handles this correctly without testing for it here
      and this shouldn't be called with null masks anyway, it should be
      a bit faster this way */

   while (*n) {
      /* Used to test for (!*m) here, but this scheme seems to work better */
      switch (*m) {
      case 0:
	 do
	    m--;		/* Search backwards      */
	 while ((m > ma) && (*m == '?'));	/* For first non-? char  */
	 if ((m > ma) ? ((*m == '*') && (m[-1] != QUOTE)) : (*m == '*'))
	    return MATCH;	/* nonquoted * = match   */
	 break;
      case WILDS:
	 do
	    m++;
	 while (*m == WILDS);	/* Zap redundant wilds   */
	 lsm = m;
	 lsn = n;		/* Save * fallback spot  */
	 match += sofar;
	 sofar = 0;
	 continue;		/* Save tally count      */
      case WILDQ:
	 m++;
	 n++;
	 continue;		/* Match one char        */
      case QUOTE:
	 m++;			/* Handle quoting        */
      }
      if (*m == *n) {		/* If matching           */
	 m++;
	 n++;
	 sofar++;
	 continue;		/* Tally the match       */
      }
      if (lsm) {		/* Try to fallback on *  */
	 n = ++lsn;
	 m = lsm;		/* Restore position      */
	 /* Used to test for (!*n) here but it wasn't necessary so it's gone */
	 sofar = 0;
	 continue;		/* Next char, please     */
      }
      return NOMATCH;		/* No fallbacks=No match */
   }
   while (*m == WILDS)
      m++;			/* Zap leftover *s       */
   return (*m) ? NOMATCH : MATCH;	/* End of both = match   */
}

#endif
