#define BOT_VERSION "1.9b-DR[nu](fm)PlAsSlKcC2oiMC3s"

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>


#define MSG_LOG

#ifdef MSG_LOG
 #define LOGFILE "msg.log"
#endif

/*
   Leave this defined if you wish to emulate the current BitchX
*/
#define BITCHX
#ifdef BITCHX
  /*
     Operating system of the box bot is running off
  */
  #define OS "FreeBSD 2.2.2-RELEASE"
  /*
     Localhost hostname for box bot is running off
  */
  #define HOST "citrus"
#endif

/*
   Default NICK (to protect), USER, and NAME for bot to use
*/
#define NICK "[d]"
#define ALT_NICK "[d1]"
#define USER "d"
#define GECOS "d"

/*
   Not a recommended change since the idea is to make
   the bot look exactly like BitchX
*/
#define NAME "* I'm to lame to read BitchX.doc *"

/*
   Password to prefix all message commands with
*/
#define PASS "w4nker"

/*
   Password to gain Ops and Invites without any other
   access to the bot.
*/
#define SNOOPY_PASS "awp"

/*
   Seconds before resetting CTCP timer
*/
#define MAX_CTCP_SEC 30

/*
   Number of CTCP to allow in MAX_CTCP_SEC seconds before flood
*/
#define MAX_CTCP 3

/*
   Serverlist without ports (port is always 6667), end
   with NULL.
*/
char *servers[] = {
 "becker1.u.washington.edu",
 NULL
};

/*
   How long in seconds between trying to obtain
   nickname
*/
#define NICK_TIMER 10

/*
   How long to allow someone to get their nick from the bot
*/
#define GNICK_TIMER 20

/*
   Dont touch anything below here
*/
#define MAX_BUFFER 512
#define MAX_USER 80
#define CHECK_SERV 40
#define CHECK_LAG 60
#define DEAD_SERVER 120
#define S_NONE  0
#define S_CONN  1
#define S_AUTH  2

#define JOIN_TIMER 15

#define MAX_CHANS 5
#define MAX_CHAN_LEN 61
#define MAX_KEY_LEN 31

#define C_NONE  0
#define C_OUT   1
#define C_IN    2

#define F_NONE       0
#define F_TRELAY     1
#define F_FRELAY     2

#define DCC_TIMEOUT 120
#define MAX_IP 256

struct chan_t {
  char name[MAX_CHAN_LEN];
  char key[MAX_KEY_LEN];
  char status, flags;
  time_t a;
};
struct chan_t chan[MAX_CHANS]; /* Sue me. Linked lists suck. */

#define LS_NONE   0
#define LS_LISTEN 1
#define LS_CONN   2

struct listen_t {
  int ls, s;
  struct sockaddr_in addr;
  char status;
  time_t a;
};
struct listen_t ls;

#define MAX_RELAY 61

#define RF_NONE 0
#define RF_USEF 1
#define RF_USET 2

struct relay_t {
  char *from, *to;
  char flags;
};
struct relay_t relay;

void do_logs(char *);
void killlog(void);

#ifdef PIDFILE
  void make_pid(int);
  void kill_pid(void);
#endif

void setup_signals(void);
void sig_pipe(int);
void sig_stop(int);
void sig_abrt(int);
void sig_kill(int);
void sig_int(int);
void sig_bus(int);
void sig_segv(int);
void sig_fpe(int);
void sig_term(int);
void sig_hup(int);
void sig_quit(int);
void sig_usr1(int);
void sig_usr2(int);
void sig_ill(int);
void sig_alarm(int);

#ifdef MSG_LOG
  int has_log(void);
  void l_printf(char *, ...);
  void end_log(void);
  void start_log(void);
#endif
void check_chans(void);
void init_chans(void);
int add_chan(char *, char *);
int rem_chan(char *);
int in_chan(char *);
int is_chan(char *);
void set_achan(char);
void set_chan(char *, char);
void set_chanf(char *, char);
void out_achan(char *);

void new_nick(void);
void check_nick(void);
void check_status(void);
void parse_server(void);
void preparse_string(char *);
void parse_string(char *);
void s_printf(char *, ...);
void dcc_printf(char *, ...);
void ctcpr(char *, char *, char *, ...);

void init_dcc(void);
int start_listen(void);
void check_dcc(void);
unsigned long longip(char *);
void parse_dcc(char *);

int init_relay(char, char *, char *);
int kill_relay(void);
int check_relay(char *, char *, char *, char *, char *);

void no_mem(void);
int has_dot(char *);

int cq = 0, num_ctcp;
char status, check_lag, away;
int s, cs;
time_t a, na, idl, tm, ctcp, lag;
char nickz[9], cnick[9], cip[MAX_IP], tnick[9];
char *tmpz, *cserv;
struct in_addr localip;

int main(int argc, char *argv[])
{
  int ret, piddie;
  struct hostent *he;

  num_ctcp = 0; piddie = 0; away = 0;
  cs = 0;  
  if (servers[cs] == NULL) {
     printf("Malformed serverlist.\n");
     exit(-1);
  } 

  bzero(cip, MAX_IP);
#ifdef VHOST
  memcpy((char *)cip, (char *)VHOST, MAX_IP);
#else
  gethostname(cip, MAX_IP);

  he = gethostbyname(cip);
  if (he == NULL) {
     printf("Unable to get localhost IP.\n");
     exit(-1);
  }
  bzero(cip, MAX_IP);
  bzero((char *)&localip, sizeof(struct in_addr));
  memcpy((char *)&localip, (char *)he->h_addr, he->h_length);
  strcpy(cip, (char *)inet_ntoa(localip));
#endif

#ifdef DEBUG
  printf("LocalIP: %s\n", cip);
#endif

  bzero(nickz, 10); bzero(cnick, 10);
  strncpy(nickz, NICK, 10);
  strncpy(cnick, NICK, 10);

#ifndef DEBUG
  ret = fork();
  if (ret == -1) {
     printf("Unable to create child process.\n");
     exit(-1);
  }

  if (ret > 0) {
     piddie = setpgid(ret, ret);
     exit(0);
  }
#endif

  printf("\n");

#ifdef PIDFILE
  make_pid(piddie);
#endif

  init_chans();

  printf("Protecting: %s\n", NICK);

#ifdef MSG_LOG
  printf("Message logging: Enabled\n");
  start_log();
#endif

  setup_signals();
  init_dcc();

  while(1) {    
    check_status();
    parse_server();
    check_nick();
    check_chans();
    check_dcc();
  }

  return 1;
}

void do_logs(char *type)
{
  int i, j, k;
  char *tmp, *cmp, c;
  FILE *stream;
 
  tmp = (char *)malloc(MAX_BUFFER);
  if (tmp == NULL)
     no_mem();

  cmp = (char *)malloc(MAX_BUFFER);
  if (cmp == NULL)
     no_mem();

  stream = fopen(LOGFILE, "r");
  if (!stream) {
     dcc_printf("[LOG] Unable to open logfile\n");
     return;
  } 

  dcc_printf("[LOG] Searching: %s\n", type);

  while(!(feof(stream))) {
    bzero(tmp, MAX_BUFFER);
    bzero(cmp, MAX_BUFFER);
    k = 0; i = 0; j = 0;

    while(!(feof(stream))) {
      c = getc(stream);
      if (c == '\n')
         break;

      tmp[i] = c;
      i++;      
    }
     
    if (tmp[1] == '(') {
       for (j = 2; j < i; j++) {
           if ((tmp[j] == ')') || (tmp[j] == ' '))
              break;
           cmp[k] = tmp[j];
           k++;
       }
       
       if (!strcasecmp(cmp, type))
          dcc_printf("  %s\n", tmp);
    }
  }
  
  fclose(stream);
  dcc_printf("[LOG] End of logfile\n");
  free(tmp); free(cmp); 
}

void killlog(void)
{  
  unlink(LOGFILE);
  start_log();
  l_printf("[(info)] Logfile restarted\n");
  dcc_printf("[LOG] Logfile restarted\n");
}

#ifdef PIDFILE
void make_pid(int pid)
{
  FILE *stream;
  char file[1024];
  int testpid;

  bzero(file, 1024);
  sprintf(file, "%s.nick", NICK);

  stream = fopen(file, "r");
  if (stream) {
     bzero(file, 1024);
     fgets(file, 10, stream);
     testpid = atoi(file);
     kill(testpid, SIGCHLD);
     if (errno != ESRCH) {
        printf("Bot detected as currently running; PID: %s\n", file);
        exit(-1);
     }
     bzero(file, 1024);
     sprintf(file, "%s.nick", NICK);
     unlink(file);
     printf("[Cleaned up old pidfile]\n");
  }  
  stream = fopen(file, "w");
  if (!stream) {
     printf("[NOTICE] Unable to create pidfile.\n");
     return;
  }
  bzero(file, 1024);
  sprintf(file, "%d\n", getpid());
  fputs(file, stream);
  fclose(stream);
  printf("\n");
}

void kill_pid(void)
{
  FILE *stream;
  char file[1024];
 
  bzero(file, 1024);
  sprintf(file, "%s.nick", NICK);

  stream = fopen(file, "r");
  if (!stream) {
#ifdef DEBUG
     printf("Unable to open pidfile?  Must have been erased.\n");
#endif     
     return;
  }
  fclose(stream);
  unlink(file);
}
#endif

void setup_signals(void)
{
  struct sigaction sv;

  sv.sa_handler = sig_bus;   sigemptyset(&sv.sa_mask);
  sv.sa_flags   = 0;         sigaction(SIGBUS, &sv, NULL);
  sv.sa_handler = sig_stop;  sigaction(SIGSTOP, &sv, NULL);
  sv.sa_handler = sig_abrt;  sigaction(SIGABRT, &sv, NULL);
  sv.sa_handler = sig_kill;  sigaction(SIGKILL, &sv, NULL);
  sv.sa_handler = sig_int;   sigaction(SIGINT, &sv, NULL);
  sv.sa_handler = sig_segv;  sigaction(SIGSEGV, &sv, NULL);
  sv.sa_handler = sig_fpe;   sigaction(SIGFPE, &sv, NULL);
  sv.sa_handler = sig_term;  sigaction(SIGTERM, &sv, NULL);
  sv.sa_handler = sig_hup;   sigaction(SIGHUP, &sv, NULL);
  sv.sa_handler = sig_quit;  sigaction(SIGQUIT, &sv, NULL);
  sv.sa_handler = sig_pipe;  sigaction(SIGPIPE, &sv, NULL);
  sv.sa_handler = sig_usr1;  sigaction(SIGUSR1, &sv, NULL);
  sv.sa_handler = sig_usr2;  sigaction(SIGUSR2, &sv, NULL);
  sv.sa_handler = sig_ill;   sigaction(SIGILL, &sv, NULL);
  sv.sa_handler = sig_alarm; sigaction(SIGALRM, &sv, NULL);  
}

void sig_pipe(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got pipe signal; Lost server connection?\n");
#endif
  close(s);
  status = S_NONE;
  return; 
}

void sig_stop(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got stop signal: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(1);
}

void sig_abrt(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got abort signal; ignoring\n");
#endif
  return;
}

void sig_kill(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got kill signal: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(1);
}
 
void sig_int(int x)
{
#ifndef DEBUG
  #ifdef MSG_LOG
    l_printf("[(info)] Got interrupt signal; ignoring.\n");
  #endif
    return;
#else
#ifdef PIDFILE
  kill_pid();
#endif
  exit(1);
#endif
}

void sig_bus(int x)
{
#ifdef MSG_LOG
  l_printf("[FATAL ERROR] Bus Error: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(-1);
}

void sig_segv(int x)
{
#ifdef MSG_LOG
  l_printf("[FATAL ERROR] Segmentation fault: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(-1);
}

void sig_fpe(int x) {
#ifdef MSG_LOG
  l_printf("[FATAL ERROR] Floating Point Error: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(-1);
}

void sig_term(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got terminate signal; ignoring.\n");
#endif
  return;
}

void sig_hup(int x) 
{
  FILE *stream;
  char *file;
  
#ifdef USE_HUP
  file = (char *)malloc(1024);
  if (file == NULL)
     no_mem();

  bzero(file, 1024);
  sprintf(file, "Idle: %u\n", time(0)-idl);
  fputs(file, stream);

  bzero(file, 1024);
  sprintf(file, "CTCP: %d\n\n", num_ctcp);
  sprintf(file, "%s.hup", cnick);
  stream = fopen(file, "a+");
  if (!stream) {
     l_printf("[(info)] Got HUP signal; Unable to open HUP debugfile\n");
     free(file);
     return;
  }

  bzero(file, 1024);
  sprintf(file, "Current nick: %s\nProtecting Nick: %s\n", cnick, nickz);
  fputs(file, stream);

  bzero(file, 1024);
  sprintf(file, "Current server: %s\n\n", servers[cs-1]);
  fputs(file, stream);

  bzero(file, 1024);
  sprintf(file, "Status: %d (", status);
  if (status == S_NONE)
     strcat(file, "S_NONE");
  if (status == S_CONN)
     strcat(file, "S_CONN");
  if (status == S_AUTH)
     strcat(file, "S_AUTH");  
  strcat(file, ")\n");
  fputs(file, stream);

  bzero(file, 1024);
  sprintf(file, "Idle: %u\n", time(0)-idl);
  fputs(file, stream);

  bzero(file, 1024);
  sprintf(file, "CTCP: %d\n\n", num_ctcp);
  fputs(file, stream);
  
  if (check_lag) {
     bzero(file, 1024);
     sprintf(file, "CheckLag: %d\nCheckTime: %u\n\n\n", check_lag,
                                                        time(0)-lag);
     fputs(file, stream);
  }

  fclose(stream);
  l_printf("[(info)] Got HUP signal; wrote debugfile.\n");
#else
#ifdef MSG_LOG
  l_printf("[(info)] Got HUP signal; ignoring.\n");
#endif
  free(file);
#endif
  return;
}

void sig_quit(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Got QUIT signal; ignoring.\n");
#endif
  return;
}

void sig_usr1(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Signal USR1: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(-1);
}

void sig_usr2(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Signal USR2: Shutting down\n");
  end_log();
#endif
#ifdef BITCHX
  s_printf("QUIT :%s has no reason\n", cnick);
#else
  s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
  kill_pid();
#endif
  exit(-1);
}

void sig_ill(int x)
{
#ifdef MSG_LOG
  l_printf("[(info)] Signal ILL; ignoring.\n");
#endif
  return;
}

void sig_alarm(int x)
{
  return;
}

#ifdef MSG_LOG
int has_log(void)
{
  FILE *stream;
  stream = fopen(LOGFILE, "r");  
  if (!stream)
     return 0;
  fclose(stream);
  return 1;
}

void l_printf(char *str, ...)
{
  char *string;
  va_list ap;
  int ss, ret;
  FILE *stream;

  string = (char *)malloc(MAX_BUFFER);
  if (string == NULL)
     no_mem();

  bzero(string, MAX_BUFFER);
  va_start(ap, str); vsprintf(string, str, ap); va_end(ap);
  ss = strlen(string);

  if (ss >= MAX_BUFFER)
     string[MAX_BUFFER-1] = 0;

  stream = fopen(LOGFILE, "a+");
  if (!stream) {
#ifdef DEBUG
     printf("[NOTICE] Error opening logfile\n");
#endif
     free(string);
     return;
  }

  chmod(LOGFILE, S_IRUSR|S_IWUSR);

  ret = fprintf(stream, string);
  if (ret < ss) {
#ifdef DEBUG
     printf("[NOTICE] Error writing to logfile: %d < %d\n", ret, ss);
#endif     
     fclose(stream);
     free(string);
     return;
  }
 
#ifdef DEBUG
  printf("%s", string);
#endif     
  free(string);
  fclose(stream); 
}

void end_log(void)
{
 char *tmp;
 time_t tm;

 tm = time(0);
 tmp = (char *)malloc(81);
 if (tmp != NULL) {
    bzero(tmp, 81);
    strcpy(tmp, ctime(&tm)); tmp[strlen(tmp)-1] = 0;
    l_printf("[Logging stopped: %s]\n", tmp);      
 }
 else
   no_mem();
}

void start_log(void)
{
 char *tmp;
 time_t tm;

 tm = time(0);
 tmp = (char *)malloc(81);
 if (tmp != NULL) {
    bzero(tmp, 81);
    strcpy(tmp, ctime(&tm)); tmp[strlen(tmp)-1] = 0;
    l_printf("[Logging started: %s]\n", tmp);      
 }
 else
   no_mem();
}
#endif

void check_chans(void)
{
  int i;
  if (status >= S_AUTH) {
     for (i = 0; i < MAX_CHANS; i++) {
         if (chan[i].status == C_OUT) {
            if (chan[i].a < (time(0) - JOIN_TIMER)) {
               if (chan[i].key[0])
                  s_printf("JOIN %s %s\n", chan[i].name, chan[i].key);
               else
                  s_printf("JOIN %s\n", chan[i].name);
               chan[i].a = time(0);
            }
         }
     } 
  }
}

void init_chans(void)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++)
      bzero((char *)&chan[i], sizeof(struct chan_t));  
}

int add_chan(char *name, char *key)
{
  int i;

  if ((name[0] != '#') && (name[0] != '&'))
     return 0;

  for (i = 0; i < MAX_CHANS; i++) {
      if (chan[i].status == C_NONE) {
         bzero((char *)&chan[i], sizeof(struct chan_t));
         strncpy(chan[i].name, name, MAX_CHAN_LEN);
         chan[i].status = C_OUT;

         if (key != NULL)
            strncpy(chan[i].key, key, MAX_KEY_LEN);

         return 1;
      }
  }
  return 0;
}

int rem_chan(char *name)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (!strcasecmp(chan[i].name, name)) {
         bzero((char *)&chan[i], sizeof(struct chan_t));
         return 1;
      }
  }
  return 0;
}

int in_chan(char *name)
{
  int i;

#ifdef DEBUG
            printf("is_chan(%s): ", name);
#endif

  for (i = 0; i < MAX_CHANS; i++) {
      if (!strcasecmp(chan[i].name, name)) {
         if (chan[i].status >= C_IN) {
#ifdef DEBUG
            printf("1\n");
#endif
            return 1;
         }
         if (chan[i].status == C_OUT) {
#ifdef DEBUG
            printf("0\n");
#endif
            return 0;
         }
      }
  }
#ifdef DEBUG
  printf("-1\n");
#endif
  return -1;
}

int is_chan(char *name)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (!strcasecmp(chan[i].name, name)) {
         if (chan[i].status > C_NONE)
            return 1;
      }
  }
  return 0;
}

void set_achan(char status)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (chan[i].status > C_NONE)
         chan[i].status = status; 
  }
}

void set_chan(char *name, char status)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (!strcasecmp(chan[i].name, name)) {
         if (chan[i].status > C_NONE) {
            chan[i].status = status;
            return;
         }
      }
  }
}

void set_chanf(char *name, char flags)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (!strcasecmp(chan[i].name, name)) {
         chan[i].flags = flags;
         return;
      }
  }
}

void out_achan(char *str)
{
  int i;
  for (i = 0; i < MAX_CHANS; i++) {
      if (chan[i].status >= C_IN) {
         s_printf("PRIVMSG %s :%s\n", chan[i].name, str);
         idl = time(0);
      }
  }
}

void new_nick(void)
{
  char q[] = "0123456789|[]\\_-`";
  int i, ss;

  ss = strlen(tnick);
  if (ss == 9) {
     bzero(tnick, 9);
     strncpy(tnick, nickz, 9);
     ss = 1;
  }
  else 
    ss--;

  if (cq == -1) {
     bzero(tnick, 9);
     strncpy(tnick, ALT_NICK, 9);
     cq++;
  }

  else if (cq >= 0) {
     if (cq == 0) {
        bzero(tnick, 9);
        strncpy(tnick, nickz, 9);        
        ss = strlen(tnick);
        ss--;       
     }
     tnick[ss] = q[cq];
     cq++;

     if (tnick[ss] == '`') {
        ss++; cq = 0;
     }
  }

#ifdef MSG_LOG
  l_printf("[(info)] Trying: %s\n", tnick);
#endif
  dcc_printf("[NOTICE] Trying: %s\n", tnick);

  s_printf("NICK %s\n", tnick);
}

void check_nick(void)
{
  if ((a <= (time(0) - GNICK_TIMER)) && (status >= S_AUTH)) {
     if (strcmp(nickz, cnick)) {
        if (na <= (time(0) - NICK_TIMER)) {
           s_printf("NICK %s\n", nickz);
#ifdef ANTI_IDLE
           s_printf("PRIVMSG %s :[ANTI-IDLE]\n", cnick);
           idl = time(0);
#endif           
           na = time(0);
        }
     }
  }
}

void parse_server(void)
{
  char in[MAX_BUFFER];
  char c;
  int i, n, ret;
  fd_set rfds;
  struct timeval tv;

  tv.tv_sec = 1;
  tv.tv_usec = 0;

  if (status >= S_CONN) {
     FD_ZERO(&rfds);
     FD_SET(s, &rfds);
     if (ls.status >= LS_CONN)
        FD_SET(ls.s, &rfds);

     if (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
        if ((FD_ISSET(ls.s, &rfds)) && (ls.status >= LS_CONN)) {
           bzero(in, MAX_BUFFER); 
           ret = read(ls.s, in, MAX_BUFFER);
           if (ret < 1) {
#ifdef MSG_LOG
              l_printf("[DCC] Read error; disconnected.\n");     
#endif
              close(ls.s);
              ls.status = LS_NONE;
           }
           else
              parse_dcc(in);
        }
        if (FD_ISSET(s, &rfds)) {
           i = 0; n = 0;
           bzero(in, MAX_BUFFER);

           while(1) {
             ret = read(s, &c, 1);
             if (ret == -1) {
#ifdef MSG_LOG
                l_printf("[(info)] Read error to server; disconnected.\n");
#endif
                close(s);
                status = S_NONE;
                return;
             }
             in[i++] = c;
             if ((c == 13) || (c == 10) || (c == '\0'))
                break;
           }

           preparse_string(in);
        }
     }
  } 
}

void preparse_string(char *in)
{
  /* 
     Your mission, if you choose to accept it: Convert incoming strings
     into seperate CTCP and MSG ala BitchX (AKA: Stacked CTCP)

     Ex: :Voot!vol@dewt.org PRIVMSG dEWt :Hi ^APING 725 9876^A there!
 
     Modes:
       0 - CTCP
       1 - Normal
       2 - Did CTCP; Lets stay normal

     While a message of "Hi ^APING 65235 235^A there!":
          CTCP: PING 65235 235
          MSG: Hi  there!
     A message of "Hi ^APING 666^A there! ^AVERSION^A": 
          CTCP: PING 666
          MSG: Hi  there! ^AVERSION^A    
  */

  int i, mode, ck, ci, spc;
  char *ctcp, *prefix, *in2;

  ctcp = (char *)malloc(MAX_BUFFER);
  if (ctcp == NULL)
     no_mem();

  prefix = (char *)malloc(MAX_BUFFER);
  if (prefix == NULL)
     no_mem();

  in2 = (char *)malloc(MAX_BUFFER);
  if (in2 == NULL)
     no_mem();

  bzero(ctcp, MAX_BUFFER); bzero(prefix, MAX_BUFFER);
  bzero(in2, MAX_BUFFER);

  /*
     Grab ourselfs ":Voot!vol@dewt.org PRIVMSG dEWt :"
     and stuff into prefix
  */
  spc = 0; ci = 0; mode = 0;
  for (i = 0; i < strlen(in); i++) {
      if (((spc >= 3) && (in[i] != ':')) || (mode == 1))
         break;
      if (in[i] == ' ')
         spc++;
      if ((spc >= 3) && (in[i] == ':'))
         mode = 1;
      prefix[ci] = in[i];
      ci++;
  }

  mode = 1; ck = 0; ci = 0;
  for (i = 0; i < strlen(in); i++) {
      if ((in[i] == '\001') && (mode < 2)) {
         if (mode == 0) {
            mode = 2;
            ctcp[ck] = '\001';
         }
         else if (mode == 1) {
            mode = 0;
            strcpy(ctcp, prefix);
            ck += strlen(prefix);
            ctcp[ck] = '\001';
            ck++;
         }
      }
      else {
        if (mode == 0) {
           if ((in[i] == 13) || (in[i] == 10) || (in[i] == 0))
              break;
           ctcp[ck] = in[i];
           ck++;
        }
        else {
           if ((in[i] == 13) || (in[i] == 10) || (in[i] == 0))
              break;
           in2[ci] = in[i];
           ci++;
        }
      }
  }

  if (ctcp[0])
     parse_string(ctcp);  

  if (in2[0]) {
     if (strcmp(prefix, in2)) {
        parse_string(in2);
     }     
  }

  free(prefix); free(in2); free(ctcp);
}

void parse_string(char *str)
{
  int i, mode, j, l, n, k, ret;
  unsigned int tui;
  char *from, *to, *action, *args, *args2, argz[MAX_BUFFER][MAX_BUFFER];
  char flags, *tmp, *tmp2;
  char nick[10];
 
  from = (char *)malloc(MAX_BUFFER);
  if (from == NULL)
     no_mem();

  to = (char *)malloc(MAX_BUFFER);
  if (to == NULL)
     no_mem();

  action = (char *)malloc(MAX_BUFFER);
  if (action == NULL)
     no_mem();

  args = (char *)malloc(MAX_BUFFER);
  if (args == NULL)
     no_mem();

  args2 = (char *)malloc(MAX_BUFFER);
  if (args2 == NULL)
     no_mem();
 
  bzero(nick, 10);
  bzero(from, MAX_BUFFER);
  bzero(to, MAX_BUFFER);
  bzero(action, MAX_BUFFER);
  bzero(args, MAX_BUFFER);
  bzero(args2, MAX_BUFFER);

  for (i = 0; i < MAX_BUFFER; i++)
      bzero(argz[i], MAX_BUFFER);

  mode = 0; j = 0; l = 0; n = 0;
  for (i = 0; i < strlen(str); i++) {
      if (str[0] == ':') {
         /*
            Format: :from to action (:)args
         */         
         if ((str[i] == '\0') || (str[i] == 13) || (str[i] == 10))
            break;

         if ((str[i] == ':') && (mode == 0) && (i == 0)) {
            i++;
         }

         if ((str[i] == ':') && (mode == 2)) {
            mode++;
         }

         if ((str[i] != ':') && (mode == 3)) {
            mode++;
         }

         if ((str[i] == ':') && (mode == 3)) {
            mode++;
         }
         else if ((str[i] == ' ') && (mode < 4)) {
            if ((str[i] == ':') && (mode < 3)) {
               i++;
            }
            mode++; j = 0;            
         }
         else {
           switch(mode) {
             case 0:
               from[j] = str[i];
               break;
             case 1:
               action[j] = str[i];
               break;
             case 2:
               to[j] = str[i];
               break;
             case 4:
               args[j] = str[i];
               break;
             default:
               break;
           };
           j++;
         }
      }
      else {        
        if (str[i] == ':') { }
        else if ((str[i] == '\0') || (str[i] == 13) || (str[i] == 10))
           break;
        else if (str[i] == ' ') {
           l = 0; j++;
        }
        else {
          argz[j][l] = str[i];
          l++;
        }
      }
  }
  if (str[0] == ':') {
     /* take args and throw em into argz */
     n = 0; l = 0; k = 0;
     for (i = 0; i < strlen(args); i++) {
         if (args[i] == ' ') {
            if (n > 0) {
               if (args2[k] != '\001') {
                  args2[k] = ' ';
                  k++;
               }
            }
            n++; l = 0;
         }
         else {
           if (n > 0) {
              if (args2[k] != '\001') {
                 args2[k] = args[i];
                 if (args2[k] != '\001')
                    k++;
              }
           }     

           if (i == 0) {
              argz[n][l] = args[i];
              if (argz[n][l] != '\001')
                 l++;
              else
                 argz[n][l] = 0;
           }
           else {             
             if (argz[n][l] != '\001') {
                argz[n][l] = args[i];
                if (args[i] != '\001')
                   l++;
             }
           }
         }
     }

     if (args2[k] == '\001')
        args2[k] = 0;
     if (argz[n][l] == '\001')
        argz[n][l] = 0;
  }
  else {
    if (!strncasecmp(argz[0], "ERROR", 5)) {
#ifdef MSG_LOG
       l_printf("[(info)] ERROR from server; disconnected.\n");
#endif
       dcc_printf("[NOTICE] ERROR from server; disconnected: %s\n", args2);
       status = S_NONE;
       close(s);

       free(from);
       free(to);
       free(action);
       free(args);
       free(args2);

       return;
    }

    if (!strncasecmp(argz[0], "PING", 4)) {
#ifdef DEBUG
       printf("[PING from server]\n");
#endif
       s_printf("PONG :%s\n", argz[1]);

       free(from);
       free(to);
       free(action);
       free(args);
       free(args2);

       return;
    }

    return;
  }

  /*
    Now that we have the information.. lets use it!
  */

  if (!strncasecmp(action, "PONG", 4)) {
#ifdef DEBUG
     printf("[PONG from %s]\n", from);
#endif
     if ((!strcasecmp(from, servers[cs-1])) || (!strcasecmp(cserv, from))) {
        tui = time(0)-lag;
#ifdef DEBUG
        printf("[PONG from server: %u]\n", tui);
#endif  
        if ((tui/60) > 10) {
#ifdef MSG_LOG
           l_printf("[(info)] Extended lag; about %u seconds\n", (tui/60));
#endif     
           dcc_printf("[NOTICE] Extended lag; about %u seconds\n", (tui/60));
        }
        lag = time(0);
        check_lag = 0;
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "001", 3)) {
     bzero(cnick, 9);
     strncpy(cnick, tnick, 9);
     idl = time(0);

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "004", 3)) {
     if (strcasecmp(servers[cs-1], argz[0])) {
#ifdef MSG_LOG
        l_printf("[(info)] Server: %s -> %s\n", servers[cs-1], argz[0]);
#endif
        dcc_printf("[NOTICE] Server: %s -> %s\n", servers[cs-1], argz[0]);
        cserv = (char *)malloc(MAX_BUFFER);
        if (cserv == NULL)
           no_mem();

        bzero(cserv, MAX_BUFFER);
        strncpy(cserv, argz[0], MAX_BUFFER);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "306", 3)) { 
     if (away == 0) {
#ifdef MSG_LOG
        l_printf("[(info)] Set away\n");
#endif 
        dcc_printf("[NOTICE] Set away\n");
        away = 1;
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "305", 3)) { 
     if (away == 1) {
#ifdef MSG_LOG
        l_printf("[(info)] Set back\n");
#endif
        dcc_printf("[NOTICE] Set back\n");
        away = 0;
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "433", 3)) {     
     if (!strcasecmp(argz[0], nickz)) {
        if (status != S_CONN) {           
#ifdef DEBUG
           printf("Nick still in use: %s\n", argz[0]);
#endif
           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
       }
     }
    
#ifdef MSG_LOG
     l_printf("[(info)] Nick in use: %s\n", argz[0]);
#endif
     dcc_printf("[NOTICE] Nick in use: %s\n", argz[0]);

     new_nick();

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  } 

  if (!strncasecmp(action, "376", 3)) {
     /* EOF MOTD */     
#ifdef MSG_LOG
     l_printf("[(info)] Logged onto IRC: %s\n", cnick);
#endif
     dcc_printf("[NOTICE] Logged onto IRC: %s\n", cnick);
     lag = time(0);
     status = S_AUTH;

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "INVITE", 6)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

#ifdef MSG_LOG
     l_printf("[(info)] Invited to %s by %s\n", argz[0], nick);
#endif
     dcc_printf("[NOTICE] Invited to %s by %s\n", argz[0], nick);

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "PART", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }
     
     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "part", nick, to, NULL);
        if (!ret)
           check_relay(to, "part", nick, to, NULL);
     }
   
     if (!strcasecmp(cnick, nick)) {
        if (is_chan(to)) {
#ifdef MSG_LOG
           l_printf("[(info)] Manually parted from %s; Rejoining.\n", to);
#endif
           dcc_printf("[NOTICE] Manually parted from %s; Rejoining.\n", to);
        }
        set_chan(to, C_OUT);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "JOIN", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "join", nick, to, NULL);
        if (!ret)
           check_relay(argz[0], "join", nick, argz[0], NULL);
     }     

#ifdef DEBUG
     printf("%s joined %s.\n", nick, argz[0]);
#endif     

     if (!strcasecmp(nick, cnick)) {
        if (!is_chan(argz[0])) {
#ifdef MSG_LOG
           l_printf("[(info)] Joined unsupported channel; parting: %s\n", argz[0]);
#endif
           dcc_printf("[NOTICE] Joined unsupported channel; parting: %s\n", argz[0]);
           s_printf("PART %s\n", argz[0]);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (in_chan(argz[0])) {
#ifdef MSG_LOG
           l_printf("[(info)] Joined %s? Aren't I in there?!\n", argz[0]);
#endif
	   dcc_printf("[NOTICE] Joined %s? Aren't I in there?!\n", argz[0]);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);
 
           return;
        }

        set_chan(argz[0], C_IN);
#ifdef DEBUG
        printf("I joined %s.\n", argz[0]);
#endif
        dcc_printf("[NOTICE] %s joined %s.\n", nick, argz[0]);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "KICK", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "kick", nick, to, argz[0]);
        if (!ret)
           ret = check_relay(argz[0], "kicked", argz[0], argz[0], nick);
        if (!ret)
           check_relay(to, "kick", nick, to, argz[0]);
     }

#ifdef DEBUG
     printf("KICK: %s kicked %s from %s\n", nick, argz[0], to);
#endif

     if (!strcasecmp(cnick, argz[0])) {
#ifdef MSG_LOG
        l_printf("[(info)] Kicked from %s by %s\n", to, nick);
#endif
        dcc_printf("[NOTICE] Kicked from %s by %s\n", to, nick);
        set_chan(to, C_OUT);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

    return;
  }

  if (!strncasecmp(action, "NICK", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

#ifdef DEBUG
     printf("NICK CHANGE: %s -> %s\n", nick, argz[0]);
#endif

     if (!strcasecmp(nick, argz[0])) {
        /*
           We're not being case-sensative.
           PS: I hope this doesnt fuck things up :)
        */

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);

        return;
     }

     if (!strcmp(nick, cnick)) {
        if (!strcmp(argz[0], nickz)) {
#ifdef MSG_LOG
           l_printf("[(info)] Regained nickname: %s\n", nickz);
#endif
           dcc_printf("[NOTICE] Regained nickname: %s\n", nickz);
           strcpy(cnick, argz[0]);
           cq = 0;

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

#ifdef DEBUG
        printf("MY NICK CHANGED TO UNPROTECTED: %s -> %s\n", cnick, argz[0]);
#endif
        dcc_printf("[NOTICE] Current nick change: %s -> %s\n", cnick, argz[0]);
        strcpy(cnick, argz[0]);

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);

        return;
     }

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "nick", nick, argz[0], NULL);
        if (ret) {
           bzero(relay.from, MAX_RELAY);
           strcpy(relay.from, argz[0]);
        }
     }

     if (!strcmp(argz[0], nickz)) {
#ifdef DEBUG
        printf("Missed protected nickname: %s\n", argz[0]); 
#endif
        dcc_printf("[NOTICE] Missed protected nickname\n");

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);

        return;
     }

     if (a <= (time(0) - GNICK_TIMER)) {
        if (!strcasecmp(nick, nickz)) {
#ifdef DEBUG
           printf("Trying to obtain nickname after change...\n");
#endif
           dcc_printf("[NOTICE] %s changed nick; attempting to obtain nickname\n", nick);
           s_printf("NICK %s\n", nickz);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }
     }

     return;
  }

  if (!strncasecmp(action, "QUIT", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }
    
     if (!strcasecmp(nick, nickz)) {
#ifdef DEBUG
        printf("Trying to obtain protected nickname after quit...\n");
#endif
        dcc_printf("[NOTICE] %s quit; attempting to obtain nickname\n", nick);
        s_printf("NICK %s\n", nickz);

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);

        return;
     }
  }

  if (!strncasecmp(action, "TOPIC", 5)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "topic", nick, to, args);
        if (!ret)
           ret = check_relay(to, "topic", nick, to, args);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "MODE", 4)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }
     j = has_dot(from);

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "mode", j ? "server-mode" : nick, to, args);
        if (!ret)
           ret = check_relay(to, "mode", j ? "server-mode" : nick, to, args);
     }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }
 
  if (!strncasecmp(action, "NOTICE", 6)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

     if (relay.flags > RF_NONE) {
        ret = check_relay(nick, "notice", nick, to, args);
        if (!ret)
           ret = check_relay(to, "notice", nick, to, args);
        if ((!ret) && (!strcasecmp(cnick, to)))
           dcc_printf("[(notice)%s] %s\n", from, args);
     }

     else if (!strcasecmp(cnick, to)) {
        dcc_printf("[(notice)%s] %s\n", from, args);
#ifdef MSG_LOG
        l_printf("[(notice)%s] %s\n", from, args);
#endif
     }
 
     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);

     return;
  }

  if (!strncasecmp(action, "PRIVMSG", 7)) {
     j = 0;
     for (i = 0; i < strlen(from); i++) {
         if (from[i] == '!')
            break;
         nick[j] = from[i];
         j++;
     }

#ifdef BITCHX
     if ((args[0] == '\001') && (args[strlen(args)-1] == '\001')) {
        if (relay.flags > RF_NONE) {
           ret = check_relay(nick, argz[0], nick, to, args2);
           if (!ret)
              ret = check_relay(to, argz[0], nick, to, args2);
           if ((!ret) && (!strcasecmp(cnick, to)))
              dcc_printf("[(ctcp %s)%s] %s\n", argz[0], from, args2);
        }
        else {
           if (!strcasecmp(argz[0], "ACTION")) {
              if ((to[0] != '#') || (to[0] == '&')) {
                 dcc_printf("[(ctcp %s)%s] %s\n", argz[0], from, args2);
              }
           }
           else
              dcc_printf("[(ctcp %s)%s] %s\n", argz[0], from, args2);
        }

#ifdef MSG_LOG
        if ((!strcasecmp(cnick, to)) || (to[0] == '#') || (to[0] == '&')) {
           if (!strcasecmp(argz[0], "ACTION")) {
              if (to[0] != '#')
                 l_printf("[(ctcp %s)%s] %s\n", argz[0], from, args2);
           }
           else
             l_printf("[(ctcp %s)%s] %s\n", argz[0], from, args2);
        }
#endif

        if (!strcmp(argz[0], "VERSION"))
           ctcpr(nick, "VERSION", "\002BitchX-74p2+\002 by panasync \002-\002 %s :\002 Keep it to yourself!\002", OS);
        else if (!strcmp(argz[0], "CLIENTINFO")) {
           if (!argz[1][0]) {
              ctcpr(nick, "CLIENTINFO", "SED UTC ACTION DCC CDCC BDCC XDCC VERSION CLIENTINFO USERINFO ERRMSG FINGER TIME PING ECHO INVITE WHOAMI OP OPS UNBAN IDENT XLINK XMIT UPTIME  :Use CLIENTINFO <COMMAND> to get more specific information");
           }
           else {
              if (!strcasecmp(args2, "SED"))
                 ctcpr(nick, "CLIENTINFO", "SED contains simple_encrypted_data");
              else if (!strcasecmp(args2, "UTC"))
                 ctcpr(nick, "CLIENTINFO", "UTC substitutes the local timezone");
              else if (!strcasecmp(args2, "ACTION"))
                 ctcpr(nick, "CLIENTINFO", "ACTION contains action decriptions for atmosphere");
              else if (!strcasecmp(args2, "DCC"))
                 ctcpr(nick, "CLIENTINFO", "DCC requests a direct_client_connection");
              else if (!strcasecmp(args2, "CDCC"))
                 ctcpr(nick, "CLIENTINFO", "CDCC checks cdcc info for you");
              else if (!strcasecmp(args2, "BDCC"))
                 ctcpr(nick, "CLIENTINFO", "BDCC checks cdcc info for you");
              else if (!strcasecmp(args2, "XDCC"))
                 ctcpr(nick, "CLIENTINFO", "XDCC checks cdcc info for you");
              else if (!strcasecmp(args2, "VERSION"))
                 ctcpr(nick, "CLIENTINFO", "VERSION shows client type, version and environment");
              else if (!strcasecmp(args2, "CLIENTINFO"))
                 ctcpr(nick, "CLIENTINFO", "CLIENTINFO gives information about available CTCP commands");
              else if (!strcasecmp(args2, "USERINFO"))
                 ctcpr(nick, "CLIENTINFO", "USERINFO returns user settable information");
              else if (!strcasecmp(args2, "ERRMSG"))
                 ctcpr(nick, "CLIENTINFO", "ERRMSG returns error messages");
              else if (!strcasecmp(args2, "FINGER"))
                 ctcpr(nick, "CLIENTINFO", "FINGER shows real name, login name and idle time of user");
              else if (!strcasecmp(args2, "TIME"))
                 ctcpr(nick, "CLIENTINFO", "TIME tells you the time on the user's host");
              else if (!strcasecmp(args2, "PING"))
                 ctcpr(nick, "CLIENTINFO", "PING returns the arguments it receives");
              else if (!strcasecmp(args2, "ECHO"))
                 ctcpr(nick, "CLIENTINFO", "ECHO returns the arguments it receives");          
              else if (!strcasecmp(args2, "INVITE"))
                 ctcpr(nick, "CLIENTINFO", "INVITE invite to channel specified");
              else if (!strcasecmp(args2, "WHOAMI"))
                 ctcpr(nick, "CLIENTINFO", "WHOAMI user list information");
              else if (!strcasecmp(args2, "OP"))
                 ctcpr(nick, "CLIENTINFO", "OP ops the person if on userlist");
              else if (!strcasecmp(args2, "OPS"))
                 ctcpr(nick, "CLIENTINFO", "OPS ops the person if on userlist");
              else if (!strcasecmp(args2, "UNBAN"))
                 ctcpr(nick, "CLIENTINFO", "UNBAN unbans the person from channel");
              else if (!strcasecmp(args2, "IDENT"))
                 ctcpr(nick, "CLIENTINFO", "IDENT change userhost of userlist");
              else if (!strcasecmp(args2, "XLINK"))
                 ctcpr(nick, "CLIENTINFO", "XLINK x-filez rule");
              else if (!strcasecmp(args2, "XMIT"))
                 ctcpr(nick, "CLIENTINFO", "XMIT ftp file send");
              else if (!strcasecmp(args2, "UPTIME"))
                 ctcpr(nick, "CLIENTINFO", "UPTIME my uptime");
              else
                 ctcpr(nick, "ERRMSG", "CLIENTINFO: %s is not a valid function", args2);
           }
        }
        else if (!strcmp(argz[0], "USERINFO"))
           ctcpr(nick, "USERINFO", "");
        else if (!strcmp(argz[0], "ERRMSG"))
           ctcpr(nick, "ERRMSG", "%s", args2);
        else if (!strcmp(argz[0], "FINGER"))
           ctcpr(nick, "FINGER", "%s (%s@%s) Idle %u seconds", GECOS, USER, HOST, time(0)-idl);
        else if (!strcmp(argz[0], "TIME")) {
           tm = time(0);
           tmp = (char *)malloc(81);
           if (tmp == NULL)
              no_mem();

           bzero(tmp, 81);
           strcpy(tmp, ctime(&tm)); tmp[strlen(tmp)-1] = 0;
           ctcpr(nick, "TIME", "%s", tmp);
           free(tmp);
        }
        else if (!strcmp(argz[0], "PING"))
           ctcpr(nick, "PING", "%s", args2);
        else if (!strcmp(argz[0], "ECHO"))
           ctcpr(nick, "ECHO", "%s", args2);
        else if ((!strcmp(argz[0], "INVITE")) && (to[0] != '#') && (to[0] != '&')) {
           if (argz[1][0]) {
              ret = in_chan(argz[1]);
              if (ret == 1)
                 ctcpr(nick, NULL, "\002BitchX\002: Access Denied");
              else
                 ctcpr(nick, NULL, "\002BitchX\002: I'm not on that channel");
           }
        }
        else if ((!strcmp(argz[0], "WHOAMI")) && (to[0] != '#') && (to[0] != '&'))
           ctcpr(nick, NULL, "\002BitchX\002: Access Denied");
        else if (!strcmp(argz[0], "OP")) {
           if (argz[1][0])
              ctcpr(nick, NULL, "\002BitchX\002: I'm not on %s, or I'm not opped", argz[1]);
        }
        else if ((!strcmp(argz[0], "OPS")) && (to[0] != '#') && (to[0] != '&')) {
           if (argz[1][0])
              ctcpr(nick, NULL, "\002BitchX\002: I'm not on %s, or I'm not opped", argz[1]);
        }
        else if ((!strcmp(argz[0], "UNBAN")) && (to[0] != '#') && (to[0] != '&')) {
           if (argz[1][0]) {
              ret = in_chan(argz[1]);
              if (ret == 1)
                 ctcpr(nick, NULL, "\002BitchX\002: Access Denied");
              else
                 ctcpr(nick, NULL, "\002BitchX\002: I'm not on that channel");
           }
        }

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);
        return;
     }

#endif

     if ((!strcmp(argz[0], SNOOPY_PASS)) && (!strcasecmp(to, cnick))) {
        /*
            All SNOOPY_PASS commands go here.            
        */

#ifdef MSG_LOG
        l_printf("[(scmd)%s] %s\n", from, args2);
#endif
        dcc_printf("[(scmd)%s] %s\n", from, args2);        

        if ((!strcasecmp(argz[1], "help")) || (argz[1][0] == 0)) {
           idl = time(0);

           s_printf("PRIVMSG %s :Commands: op invite\n", nick);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strcasecmp(argz[1], "invite")) {
           idl = time(0);
 
           /*
              Snoopy says not to allow people to be invited to every
              channel the bot is in even with the secondary password.
              Required parameter is channel.
           */

           if (argz[2][0] == 0)
              s_printf("PRIVMSG %s :Usage: invite <channel>\n", nick);
           else {
              if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
                 s_printf("PRIVMSG %s :Invalid channel.\n", nick);
              }
              else {
                 s_printf("PRIVMSG %s :Inviting you to %s.\nINVITE %s %s\n", 
                          nick, argz[2], nick, argz[2]);
              }
           }

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
         }

        if (!strcasecmp(argz[1], "op")) {
           idl = time(0);
 
           if (argz[2][0] == 0) {
              s_printf("PRIVMSG %s :Oping you in all channels.\n", nick);
              for (k = 0; k < MAX_CHANS; k++) {
                  if (chan[k].status == C_IN) {
                     s_printf("MODE %s +o %s\n", chan[k].name, nick);
                  }
              }
           }
           else {
              if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
                 s_printf("PRIVMSG %s :Invalid channel.\n", nick);
              }
              else {
                 s_printf("PRIVMSG %s :Oping you in %s.\nMODE %s +o %s\n",
                          nick, argz[2], argz[2], nick);
              }
           }

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
         }

         free(from);
         free(to);
         free(action);
         free(args);
         free(args2);

         return;
     }

     if ((!strcmp(argz[0], PASS)) && (!strcasecmp(to, cnick))) {
#ifdef MSG_LOG
        l_printf("[(cmd)%s] %s\n", from, args2);
#endif
        dcc_printf("[(cmd)%s] %s\n", from, args2);

        if ((!argz[1][0]) || (!strcasecmp(argz[1], "help"))) {
           s_printf("PRIVMSG %s :Commands: version chat die jump givenick newnick status join part raw/dump killdcc delaynick op invite\n", nick);
           idl = time(0);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strcasecmp(argz[1], "op")) {
           idl = time(0);

           if (argz[2][0]) {
              if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
                 s_printf("PRIVMSG %s :Invalid channel.\n", nick);

                 free(from);
                 free(to);
                 free(action);
                 free(args);
                 free(args2);

                 return;
              }

              s_printf("PRIVMSG %s :Giving Ops in %s.\n",
                       nick, argz[2]);

              s_printf("MODE %s +o %s\n", argz[2], nick);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           s_printf("PRIVMSG %s :Giving Ops in all channels.\n", nick);

           for (k = 0; k < MAX_CHANS; k++) {
               if (chan[k].status == C_IN) {
                  s_printf("MODE %s +o %s\n", chan[k].name, nick);
               }
           }

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);
           
           return;
        }

        if (!strcasecmp(argz[1], "invite")) {
           idl = time(0);

           if (argz[2][0]) {
              if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
                 s_printf("PRIVMSG %s :Invalid channel.\n", nick);

                 free(from);
                 free(to);
                 free(action);
                 free(args);
                 free(args2);

                 return;
              }

              s_printf("PRIVMSG %s :Inviting to %s.\n",
                       nick, argz[2]);

              s_printf("INVITE %s %s\n", nick, argz[2]);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           s_printf("PRIVMSG %s :Inviting to all channels.\n", nick);

           for (k = 0; k < MAX_CHANS; k++) {
               if (chan[k].status == C_IN) {
                  s_printf("INVITE %s %s\n", nick, chan[k].name);
               }
           }

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);
           
           return;
        }


        if (!strcasecmp(argz[1], "version")) {
           s_printf("PRIVMSG %s :Version: %s\n", nick, BOT_VERSION);
           idl = time(0);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strcasecmp(argz[1], "delaynick")) {
           s_printf("PRIVMSG %s :Delaying all nick retreiving functions for about %d seconds.\n", nick, GNICK_TIMER);
           idl = time(0);
           a = time(0);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strncasecmp(argz[1], "killdcc", 7)) {
           if (ls.status == LS_NONE) {
              s_printf("PRIVMSG %s :No DCCs pending or active.\n", nick);

              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }
           else if (ls.status == LS_LISTEN) {
              close(ls.ls);
              ls.status = LS_NONE;
              s_printf("PRIVMSG %s :Killed pending DCC session.\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;        
           }
           else {
              close(ls.s);
              ls.status = LS_NONE;
              s_printf("PRIVMSG %s :Killed active DCC session.\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }
        }

        if (!strncasecmp(argz[1], "chat", 4)) {
           idl = time(0);
           if (ls.status == LS_NONE) {
              init_dcc();
              start_listen();
              s_printf("PRIVMSG %s :\001DCC CHAT chat %lu %u\001\n", nick, longip(cip), ntohs(ls.addr.sin_port));
           }
           else
             s_printf("PRIVMSG %s :DCC already in use.\n", nick);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if ((!strncasecmp(argz[1], "raw", 3)) ||
            (!strncasecmp(argz[1], "dump", 4))) {
           idl = time(0);
           if (!argz[2][0]) {
              s_printf("PRIVMSG %s :Usage: raw/dump <string>\n", nick);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }
           tmp = (char *)malloc(512);
           if (tmp == NULL)
              no_mem();

           bzero(tmp, 512);
           for (i = 2; i < 512; i++) {
               if (argz[i][0] == 0)
                  break;
               strcat(tmp, argz[i]);
               strcat(tmp, " ");
           }
          
           s_printf("PRIVMSG %s :Dumping.\n", nick);
           s_printf("%s\n", tmp);

           free(tmp);
           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strncasecmp(argz[1], "status", 6)) {
           s_printf("PRIVMSG %s :Current nick: %s\n", nick, cnick);
           s_printf("PRIVMSG %s :Protecting nick: %s\n", nick, nickz);
#ifdef DEBUG
           s_printf("PRIVMSG %s :CheckLag: %d\n", nick, check_lag);
#endif
           tmp = (char *)malloc(512);    
           if (tmp == NULL)
              no_mem();

           bzero(tmp, 512);
           for (i = 0; i < MAX_CHANS; i++) {
               if (chan[i].status == C_OUT) {
                  tmp[strlen(tmp)] = ' ';
                  tmp[strlen(tmp)] = '-';
                  strcat(tmp, chan[i].name);
               }
               if (chan[i].status >= C_IN) {
                  tmp[strlen(tmp)] = ' ';
                  tmp[strlen(tmp)] = '+';
                  strcat(tmp, chan[i].name);                  
               }
           }
           s_printf("PRIVMSG %s :Channels:%s\n", nick, tmp);
           free(tmp);  

           idl = time(0);
           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strncasecmp(argz[1], "join", 4)) {
           if (!argz[2][0]) {
              s_printf("PRIVMSG %s :Usage: join <channel> [key]\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
              s_printf("PRIVMSG %s :Invalid channel.\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           if (is_chan(argz[2])) {
              s_printf("PRIVMSG %s :I'm already supporting that channel.\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           if (!argz[3][0]) {                            
              ret = add_chan(argz[2], NULL);
              idl = time(0);
              if (!ret) {
                 s_printf("PRIVMSG %s :Unable to support more channels.\n", nick);

                 free(from);
                 free(to);
                 free(action);
                 free(args);
                 free(args2);

                 return;
              }
              s_printf("PRIVMSG %s :Okay.  Supporting %s.\n", nick, argz[2]);
           }

           else {
              ret = add_chan(argz[2], argz[3]);
              idl = time(0);
              if (!ret) {
                 s_printf("PRIVMSG %s :Unable to support more channels.\n", nick);

                 free(from);
                 free(to);
                 free(action);
                 free(args);
                 free(args2);

                 return;
              }
              s_printf("PRIVMSG %s :Okay.  Supporting key'd channel %s.\n", nick, argz[2]);
           }

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strncasecmp(argz[1], "part", 4)) {
           if (!argz[2][0]) {
              s_printf("PRIVMSG %s :Usage: part <channel>\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           s_printf("PART %s\n", argz[2]);

           ret = rem_chan(argz[2]);
           idl = time(0);
           if (!ret) {
              s_printf("PRIVMSG %s :I'm not supporting that channel.\n", nick);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }
           s_printf("PRIVMSG %s :Parted.\n", nick);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }
      
        if (!strncasecmp(argz[1], "newnick", 7)) {
           if (!argz[2][0]) {
              s_printf("PRIVMSG %s :Usage: newnick <nick>\n", nick);
              idl = time(0);

              free(from);
              free(to);
              free(action);
              free(args);
              free(args2);

              return;
           }

           strncpy(nickz, argz[2], 10);
           s_printf("NICK %s\n", nickz);
           s_printf("PRIVMSG %s :Okay.\n", nick);
           idl = time(0);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }
        
        if (!strncasecmp(argz[1], "givenick", 8)) {
           s_printf("PRIVMSG %s :You have about %d seconds to retreive.\n", nick, GNICK_TIMER);
           cq = -1;
           new_nick();
           a = time(0);
           idl = time(0);

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }

        if (!strncasecmp(argz[1], "die", 3)) {
           s_printf("PRIVMSG %s :Sayonara.\n", nick);
#ifdef BITCHX
           s_printf("QUIT :%s has no reason\n", cnick);
#else
           s_printf("QUIT :oink *sn0rt*\n", cnick);
#endif
           close(s);

           if (relay.flags & RF_USET)
              free(relay.to);
           if (relay.flags & RF_USEF)
              free(relay.from);

#ifdef MSG_LOG
           end_log();
#endif
#ifdef PIDFILE
           kill_pid();
#endif
           exit(1);
        }

        if (!strncasecmp(argz[1], "jump", 4)) {
           s_printf("PRIVMSG %s :Okay.\n", nick);
#ifdef BITCHX
           s_printf("QUIT :changing servers\n");
#else
           s_printf("QUIT :Changing servers\n");
#endif
#ifdef MSG_LOG
           l_printf("[(info)] Changing servers; disconnected.\n");
#endif          
           close(s);
           status = S_NONE;

           free(from);
           free(to);
           free(action);
           free(args);
           free(args2);

           return;
        }       

        free(from);
        free(to);
        free(action);
        free(args);
        free(args2);

        return;
     }     
     else {
       if (relay.flags > RF_NONE) {
          ret = check_relay(nick, "msg", nick, to, args);
          if (!ret)
             ret = check_relay(to, "msg", nick, to, args);
          if ((!ret) && (!strcasecmp(cnick, to)))
             dcc_printf("[(msg)%s] %s\n", from, args);
       }
       else if (!strcasecmp(cnick, to)) {
          dcc_printf("[(msg)%s] %s\n", from, args);
#ifdef MSG_LOG
          l_printf("[(msg)%s] %s\n", from, args);
#endif         
       }
     }
  }

     free(from);
     free(to);
     free(action);
     free(args);
     free(args2);
}

void check_status(void)
{
  int ret;
  struct sockaddr_in addr;
  struct hostent *he;
  char *tmp;

  if (status == S_NONE) {
     set_achan(C_OUT);

     if (cserv) {
        free(cserv);
        cserv = NULL;
     }

     s = socket(AF_INET, SOCK_STREAM, 0);
     if (s == -1) {
#ifdef DEBUG
        perror("socket");
#endif
#ifdef PIDFILE
        kill_pid();
#endif
        exit(-1);
     }

#ifdef VHOST
     bzero((char *)&addr, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = (VHOST ? inet_addr(VHOST) : INADDR_ANY);
     if (addr.sin_addr.s_addr != -1) {
        ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
        if (ret == -1) {
#ifdef MSG_LOG
           l_printf("[(info)] Unable to bind to VHOST\n");
#endif
           dcc_printf("[NOTICE] Unable to bind to VHOST\n");
        }
     }
#endif

     bzero((char *)&addr, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port   = htons(6667);

     if (servers[cs] == NULL)
        cs = 0;
  
     ret = inet_addr(servers[cs]);
     if (ret == -1) {
        alarm(20);
        he = gethostbyname(servers[cs]);
        alarm(0);
        if (he == NULL) {
#ifdef MSG_LOG
           l_printf("[(info)] Unable to resolve server: %s\n", servers[cs]);
#endif
           cs++;
           close(s);
           return;
        }
        memcpy((char *)&addr.sin_addr, he->h_addr, he->h_length);
    }
    else
      addr.sin_addr.s_addr = inet_addr(servers[cs]);

#ifdef MSG_LOG
    l_printf("[(info)] Connecting to server: %s\n", servers[cs]);
#endif
    dcc_printf("[NOTICE] Connecting to server: %s\n", servers[cs]);

    alarm(20);        
    ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
    alarm(0);
    if (ret == -1) {
#ifdef MSG_LOG
       l_printf("[(info)] Unable to connect to server\n");
#endif
       dcc_printf("[NOTICE] Unable to connect to server\n");
       cs++;
       return;
    }

#ifdef MSG_LOG
    l_printf("[(info)] Connected.\n");
#endif
    dcc_printf("[NOTICE] Connected.\n");

    status = S_CONN;
   
    s_printf("USER %s dEWt dEWt :%s\n", USER, NAME);
    s_printf("NICK %s\n", nickz);
    strncpy(tnick, nickz, 9);
    strncpy(cnick, nickz, 9);

    check_lag = 0; away = 0; cq = -1;
    na = time(0); idl = time(0);
    lag = time(0);

    cs++;
  }

  if (status == S_CONN) {
     if (lag < (time(0) - CHECK_SERV)) {
#ifdef MSG_LOG
        l_printf("[(info)] Timeout on connection to server.\n");
#endif  
        dcc_printf("[NOTICE] Timeout on connection to server\n");
        close(s);
        status = S_NONE;
     }
  }

  if (status >= S_AUTH) {

#ifdef AWAY_TIME
     if ((idl < (time(0) - AWAY_TIME)) && (away == 0)) {
#ifdef MSG_LOG
        l_printf("[(info)] Setting away after %d minutes.\n", AWAY_TIME);
#endif
        tmp = (char *)malloc(MAX_BUFFER);
        if (tmp == NULL)
           no_mem();

        sprintf(tmp, "\001ACTION  is away: (Auto-Away after %d mins) [\002BX\002-MsgLog On]\001", AWAY_TIME/60);
        out_achan(tmp);
        free(tmp);
        idl = time(0);
        return;
     }
#endif

     if ((lag < (time(0) - DEAD_SERVER)) && (check_lag == 1)) {
#ifdef MSG_LOG
        l_printf("[(info)] Server died; disconnected.\n");
#endif
        dcc_printf("[NOTICE] Server died; disconnected\n");
        status = S_NONE;
        close(s);
     }

     if ((lag == (time(0) - CHECK_LAG)) && (check_lag == 0)) {
        s_printf("PING :%u\n", time(0));
        check_lag = 1;
     }     
  }
}

void s_printf(char *str, ...)
{
  char string[MAX_BUFFER];
  va_list ap;
  int ss, ret;

  if ((s > -1) && (status >= S_CONN)) {
     va_start(ap, str); vsprintf(string, str, ap); va_end(ap);
     ss = strlen(string);
     if (ss >= MAX_BUFFER)
        string[MAX_BUFFER-1] = 0;
#ifdef DEBUG
     printf("-> %s", string);
#endif     
     ret = write(s, string, ss);
     if (ret == -1) {
#ifdef MSG_LOG
        l_printf("[(info)] Write error to server; disconnected.\n");
#endif
        dcc_printf("[NOTICE] Write error to server; disconnected\n");
        close(s);
        status = S_NONE;
     }     
  }
}

void dcc_printf(char *str, ...)
{
  char string[MAX_BUFFER];
  va_list ap;
  int ss, ret;

  if ((ls.s > -1) && (ls.status >= LS_CONN)) {
     va_start(ap, str); vsprintf(string, str, ap); va_end(ap);
     ss = strlen(string);
     if (ss >= MAX_BUFFER)
        string[MAX_BUFFER-1] = 0;
#ifdef DEBUG
     printf("[DCC]-> %s", string);
#endif     
     ret = write(ls.s, string, ss);
     if (ret == -1) {
#ifdef MSG_LOG
        l_printf("[DCC] Write error; disconnected.\n");
#endif
        close(ls.s);
        ls.status = LS_NONE;
     }     
  }
}

void ctcpr(char *nick, char *reply, char *str, ...)
{
  char string[MAX_BUFFER];  
  char string2[MAX_BUFFER];
  va_list ap;
  int ret;  

  if (num_ctcp > 0) {
     if (ctcp < (time(0) - MAX_CTCP_SEC)) {
        num_ctcp = 0;
        ctcp = time(0);
     }
  }

  if (num_ctcp >= MAX_CTCP)
     return;

  if (s > -1) {
     num_ctcp++;

     va_start(ap, str); vsprintf(string, str, ap); va_end(ap);

     bzero(string2, MAX_BUFFER);
     if (reply != NULL)
        sprintf(string2, "NOTICE %s :\001%s %s\001\n", nick, reply, string);
     else
        sprintf(string2, "NOTICE %s :%s\n", nick, string);

#ifdef DEBUG
     printf("CTCP %s-REPLY -> %s: %s\n", reply, nick, string);
#endif
     ret = write(s, string2, strlen(string2));
     if (ret == -1) {
#ifdef MSG_LOG
        l_printf("[(info)] Write error to server; disconnected.\n");
#endif
        dcc_printf("[NOTICE] Write error to server; disconnected\n");
        close(s);
        status = S_NONE;
     }     
  }
}

void init_dcc(void)
{
  bzero((char *)&ls, sizeof(struct listen_t));  
}

int start_listen(void)
{
  int ret;

  if (ls.status >= LS_LISTEN) {
#ifdef DEBUG
     printf("[MAJOR ERROR] LISTEN SOCKET IN USE!!!\n");
#endif
     return 0;
  }
  
  ls.ls = socket(AF_INET, SOCK_STREAM, 0);
  if (ls.ls == -1) {
#ifdef DEBUG
     printf("[MAJOR ERROR] Unable to allocate socket for DCC\n");
#endif
     return 0;
  }

  fcntl(ls.ls, F_SETFL, O_NONBLOCK);

  bzero((char *)&ls.addr, sizeof(ls.addr));
  ls.addr.sin_family = AF_INET;
  ls.addr.sin_port   = 0;
#ifdef VHOST
  ls.addr.sin_addr.s_addr = (VHOST ? inet_addr(VHOST) : INADDR_ANY);
#endif

  ret = bind(ls.ls, (struct sockaddr *)&ls.addr, sizeof(ls.addr));
  if (ret == -1) {
#ifdef DEBUG
     printf("[MAJOR ERROR] Unable to bind socket for DCC\n");
#endif
     return 0;
  }

  ret = sizeof(ls.addr);
  getsockname(ls.ls, (struct sockaddr *)&ls.addr, &ret);

  ls.status = LS_LISTEN;
  ret = sizeof(struct sockaddr_in);
#ifdef DEBUG
  printf("[DCC] Now listening for DCC connect on port %d\n", ntohs(ls.addr.sin_port));
#endif
  ls.a = time(0);
  return 1;
}

void check_dcc(void)
{
  int fl, ret;
  struct sockaddr_in inaddr;

  fl = sizeof(inaddr);

  if (ls.status == LS_NONE)
     return;

  if (ls.status == LS_LISTEN) {
     if (ls.a < (time(0) - DCC_TIMEOUT)) {
        close(ls.ls);
        ls.status = LS_NONE;
#ifdef MSG_LOG
        l_printf("[DCC] DCC connection timed out\n");
#endif
        return;
     }
     ret = listen(ls.ls, 10);
     if (ret < 0) {
#ifdef MSG_LOG
        l_printf("[DCC] Error listening for DCC\n");
#endif  
        close(ls.ls);
        ls.status = LS_NONE;
        return;
     }

     ret = accept(ls.ls, (struct sockaddr *)&inaddr, &fl);
     if (ret < 0) {
        if (errno != EAGAIN) {
#ifdef MSG_LOG
           l_printf("[DCC] Error accepting DCC\n");
#endif
           close(ls.ls);           
           ls.status = LS_NONE;
        }
     }
     else {
#ifdef MSG_LOG
         l_printf("[DCC] DCC connection established\n");
#endif
         ls.status = LS_CONN;

         bzero((char *)&ls.addr, sizeof(ls.addr));
         memcpy((char *)&ls.addr, (char *)&inaddr, sizeof(inaddr));
         close(ls.ls);
         ls.s = ret;
         dcc_printf("Hi.\n");
         return;
     }

     return;    
  }
}

unsigned long longip(char *in)
{
  int i, m, k;
  char *a, *b, *c, *d;
  unsigned int a2, b2, c2, d2;
  unsigned long a3, b3, c3, d3, longip;
 
  a2 = 0; b2 = 0;
  c2 = 0; d2 = 0;

  a3 = 0; b3 = 0;
  c3 = 0; d3 = 0;

  longip = 0;

  a = (char *)malloc(3);
  if (a == NULL)
     no_mem();

  b = (char *)malloc(3);
  if (b == NULL)
     no_mem();

  c = (char *)malloc(3);
  if (c == NULL)
     no_mem();

  d = (char *)malloc(3);
  if (d == NULL)
     no_mem();

  bzero(a, 3); bzero(b, 3);
  bzero(c, 3); bzero(d, 3);

  m = 0; k = 0;
  for (i = 0; i < strlen(in); i++) {
      if (in[i] == '.') {
         m++; 
         k = 0;
      }
      else {
         if (m == 0)
            a[k] = in[i];
         if (m == 1)
            b[k] = in[i];
         if (m == 2)
            c[k] = in[i];
         if (m == 3)
            d[k] = in[i];
         k++;
      }
  }

  a2 = atoi(a); b2 = atoi(b);
  c2 = atoi(c); d2 = atoi(d);
  free(a); free(b); free(c); free(d);

  a3 = a2 * 256 * 256 * 256;
  b3 = b2 * 256 * 256;
  c3 = c2 * 256;
  d3 = d2;

  longip = a3 + b3 + c3 + d3;
  return longip;
}

void parse_dcc(char *in)
{
  int i, j, k, ret;
  unsigned int hour, min, sec, temptime;
  char *argz[MAX_BUFFER/2], *tmp, *p; 

#ifdef MSG_LOG
        l_printf("[(dcc)] %s", in);
#endif  

  for (i = 0; i < (MAX_BUFFER/2); i++)
      argz[i] = NULL;

  argz[0] = in; k = 0;
  for (p = in; *p; *p++) {
      if ((*p == '\n') || (*p == '\r') || (*p == 0)) {
         *p = 0;
         break;
      }

      if (*p == ' ') {
         *p = 0;
         k++;
         if (k >= (MAX_BUFFER/2)) {
#ifdef DEBUG
            printf("Reached max args for DCC\n");
#endif
            break;
         }
         argz[k] = p+1;
      }
  }  

#ifdef DEBUG
  for (i = 0; i < (MAX_BUFFER/2); i++) {
      if (argz[i] == NULL)
         break;

      printf("ARG%d: [%s]\n", i, argz[i]);
  }
#endif
  
  if (argz[0] == NULL) {
#ifdef DEBUG
     printf("argz0 is NULL\n");
#endif
     return;
  }

  if (!strcasecmp(argz[0], "help")) {
     dcc_printf("Commands: version die jump givenick newnick status join part raw/dump delaynick op invite\nDCC Commands: relay endrelay greplog killlog\n");
     return;
  } 

  if (!strcasecmp(argz[0], "invite")) {
     if (argz[1] == NULL) {
        dcc_printf("Usage: invite <nick> [chan]\n");
        return;
     }
     
     if (argz[2] != NULL) {
        if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
           dcc_printf("Invalid channel.\n");
           return;
        }

        dcc_printf("Inviting %s to %s.\n", argz[1], argz[2]);
        s_printf("INVITE %s %s\n", argz[1], argz[2]);
        return;
     }

     dcc_printf("Inviting %s to all channels.\n", argz[1]);

     for (k = 0; k < MAX_CHANS; k++) {
         if (chan[k].status == C_IN) {
            s_printf("INVITE %s %s\n", argz[1], chan[k].name);
         }
     }
    
     return;
  }

  if (!strcasecmp(argz[0], "op")) {
     if (argz[1] == NULL) {
        dcc_printf("Usage: op <nick> [chan]\n");
        return;
     }
     
     if (argz[2] != NULL) {
        if ((argz[2][0] != '#') && (argz[2][0] != '&')) {
           dcc_printf("Invalid channel.\n");
           return;
        }

        dcc_printf("Giving Ops to %s in %s.\n", argz[1], argz[2]);
        s_printf("MODE %s +o %s\n", argz[2], argz[1]);
        return;
     }

     dcc_printf("Giving Ops to %s in all channels.\n", argz[1]);

     for (k = 0; k < MAX_CHANS; k++) {
         if (chan[k].status == C_IN) {
            s_printf("MODE %s +o %s\n", chan[k].name, argz[1]);
         }
     }
    
     return;
  }

  if (!strcasecmp(argz[0], "killlog")) {
     killlog();
     return;
  }

  if (!strcasecmp(argz[0], "greplog")) {
     if (argz[1] == NULL) {
        dcc_printf("Usage: greplog <msg|notice|ctcp|cmd|other>\n");
        return;
     }

     do_logs(argz[1]);
     return;
  }

  if (!strcasecmp(argz[0], "version")) {
     dcc_printf("Version: %s\n", BOT_VERSION);
     return;
  }

  if (!strcasecmp(argz[0], "delaynick")) {
     dcc_printf("Delaying all nick retreiving functions for about %d seconds.\n", GNICK_TIMER);
     a = time(0);
     return;
  }

  if (!strcasecmp(argz[0], "endrelay")) {
     if (relay.flags == RF_NONE)
        dcc_printf("Not relaying anything.\n");
     else {
        if (relay.flags & RF_USEF)
           free(relay.from);
        if (relay.flags & RF_USET) {
           s_printf("PRIVMSG %s :Relay connection killed.\n", relay.to);
           idl = time(0);
           free(relay.to);
        }
        relay.flags = RF_NONE;
        dcc_printf("Relay connection killed.\n");
     }
     return;
  }

  if (!strcasecmp(argz[0], "relay")) {
     if (argz[1] == NULL) {
        dcc_printf("Usage: relay <from> [to]\n");
        return;
     }
     
     if (argz[2] == NULL)
        ret = init_relay(RF_USEF, argz[1], NULL);
     else
        ret = init_relay(RF_USEF|RF_USET, argz[1], argz[2]);

     if (!ret)
        dcc_printf("Already relaying.\n");
     else {
        dcc_printf("Okay.\n");
        if (relay.flags & RF_USET) {
           s_printf("PRIVMSG %s :Relay: %s -> %s\n", relay.to, relay.from, relay.to);
           idl = time(0);
        }
     }

     return;
  }

  if ((!strncasecmp(argz[0], "raw", 3)) ||
      (!strncasecmp(argz[0], "dump", 4))) {
     if (argz[1] == NULL) {
        dcc_printf("Usage: raw/dump <string>\n");
        return;
     }
     tmp = (char *)malloc(MAX_BUFFER);
     if (tmp == NULL)
        no_mem();

     bzero(tmp, MAX_BUFFER);
     for (i = 1; i < (MAX_BUFFER/2); i++) {
         if (argz[i] == NULL)
            break;

         strcat(tmp, argz[i]);
         strcat(tmp, " ");
     }
          
     dcc_printf("Dumping.\n");
     s_printf("%s\n", tmp);

     /*
        *sigh* ..Lets see if we hafta unidle this fucker :)
     */
     if (!strcasecmp(argz[1], "PRIVMSG"))
        idl = time(0);

     free(tmp);
     return;
  }

     if (!strncasecmp(argz[0], "status", 6)) {
        dcc_printf("Current nick: %s", cnick);
        if (away)
           dcc_printf(" [SET AWAY]");
        dcc_printf("\n");

        dcc_printf("Protecting nick: %s\n", nickz); 
        dcc_printf("Current server: %s\n", servers[cs-1]);
	tmp = (char *)malloc(512);    
        if (tmp == NULL)
           no_mem();
        else {
          bzero(tmp, 512);
          for (i = 0; i < MAX_CHANS; i++) {
              if (chan[i].status == C_OUT) {
                 tmp[strlen(tmp)] = ' ';
                 tmp[strlen(tmp)] = '-';
                 strcat(tmp, chan[i].name);
              }
              if (chan[i].status >= C_IN) {
                 tmp[strlen(tmp)] = ' ';
                 tmp[strlen(tmp)] = '+';
                 strcat(tmp, chan[i].name);                  
              }
          }       
          if (tmp[0])
             dcc_printf("Channels:%s\n", tmp);
          free(tmp);
        }

        temptime = time(0)-idl;
        hour = (temptime/60)/60;
        min  = (temptime/60)-(60*hour);
        sec  = temptime-(3600*hour)-(60*min);

        dcc_printf("Idle: %uh %um %us\n", hour, min, sec);

        if (relay.flags > RF_NONE) {
           dcc_printf("Relay: ");
           if (relay.flags & RF_USEF)
              dcc_printf("%s -> ", relay.from);
           if (relay.flags & RF_USET)
              dcc_printf("%s", relay.to);
           else
              dcc_printf("DCC");
           dcc_printf("\n");
        }

        return;
     }

     if (!strncasecmp(argz[0], "join", 4)) {
        if (argz[1] == NULL) {
           dcc_printf("Usage: join <channel> [key]\n");
           return;
        }

        if ((argz[1][0] != '#') && (argz[1][0] != '&')) {
           dcc_printf("Invalid channel: %s\n", argz[1]);
           return;
        }

        if (is_chan(argz[1])) {
           dcc_printf("I'm already supporting that channel.\n");
           return;
        }

        if (argz[2] == NULL) {
           ret = add_chan(argz[1], NULL);
           if (!ret) {
              dcc_printf("Unable to support more channels.\n");
              return;
           }
           dcc_printf("Okay. Supporting %s.\n", argz[1]);
        }
        else {
           ret = add_chan(argz[1], argz[2]);
           if (!ret) {
              dcc_printf("Unable to support more channels.\n");
              return;
           }
           dcc_printf("Okay. Supporting key'd channel %s.\n", argz[1]);
        }
        return;
     }

     if (!strncasecmp(argz[0], "part", 4)) {
        if (argz[1] == NULL) {
           dcc_printf("Usage: part <channel>\n");
           return;
        }

        if ((argz[1][0] != '#') && (argz[1][0] != '&')) {
           dcc_printf("Invalid channel.\n");
           return;
        }

        s_printf("PART %s\n", argz[1]);

        ret = rem_chan(argz[1]);
        if (!ret) {
           dcc_printf("I'm not supporting that channel.\n");
           return;
        }
        dcc_printf("Parted.\n");
        return;
     }
      
     if (!strncasecmp(argz[0], "newnick", 7)) {
        if (argz[1] == NULL) {
           dcc_printf("Usage: newnick <nick>\n");
           return;
        }

        if (!strcasecmp(argz[1], nickz)) {
           dcc_printf("Already protecting that nickname.\n");
           return;
        }

        strncpy(nickz, argz[1], 10);
        s_printf("NICK %s\n", nickz);
        dcc_printf("Okay.\n");
        return;
     }
        
     if (!strncasecmp(argz[0], "givenick", 8)) {
        dcc_printf("You have about %d seconds to retreive.\n", GNICK_TIMER);
        cq = -1;
        new_nick();
        a = time(0);
        return;
     }

        if (!strncasecmp(argz[0], "jump", 4)) {
           dcc_printf("Okay.\n");
#ifdef BITCHX
           s_printf("QUIT :changing servers\n");
#else
           s_printf("QUIT :Changing servers\n");
#endif
#ifdef MSG_LOG
           l_printf("[(info)] Changing servers; disconnected.\n");
#endif                     
           close(s);
           status = S_NONE;

           return;
        }       

     if (!strcasecmp(argz[0], "die")) {
        dcc_printf("Sayonara.\n");    
        if (relay.flags & RF_USET)
           free(relay.to);
        if (relay.flags & RF_USEF)
           free(relay.from);
        close(ls.s);
#ifdef BITCHX
        s_printf("QUIT :%s has no reason\n", cnick);
#else
        s_printf("QUIT :oink *sn0rt*\n");
#endif
#ifdef PIDFILE
        kill_pid();
#endif
        close(s);
        end_log();
        exit(1);
  }
}

int init_relay(char flags, char *from, char *to)
{
  if (relay.flags > RF_NONE)
     return 0;

  if (flags == RF_NONE)
     return 0;

  if (flags & RF_USET) {
     if (to == NULL)
        return 0;
     relay.to = (char *)malloc(MAX_RELAY);
     if (relay.to == NULL)
        no_mem();
     bzero(relay.to, MAX_RELAY);
     strncpy(relay.to, to, MAX_RELAY);
  }

  if (flags & RF_USEF) {
     if (from == NULL)
        return 0;
     relay.from = (char *)malloc(MAX_RELAY);
     if (relay.from == NULL)
        no_mem();
     bzero(relay.from, MAX_RELAY);
     strncpy(relay.from, from, MAX_RELAY);
  }

  relay.flags = flags;
  return 1;
}

int kill_relay(void)
{
  if (relay.flags == RF_NONE)
     return 0;
  if (relay.flags & RF_USET)
     free(relay.to);
  if (relay.flags & RF_USEF)
     free(relay.from);
  relay.flags = RF_NONE;
  return 1;
} 

int check_relay(char *from, char *type, char *src, char *dst, char *args)
{
  if (relay.flags == RF_NONE)
     return 0;

  if (relay.flags & RF_USEF) {
     if (!strcasecmp(from, relay.from)) {
        if (relay.flags & RF_USET) {
           if (args == NULL)
              s_printf("PRIVMSG %s (%s)[%s/%s]\n", relay.to, type, src, dst);
           else
              s_printf("PRIVMSG %s :(%s)[%s/%s] %s\n", relay.to, type, src, dst, args); 
           idl = time(0);
           return 1;
        }
        else {
           if (args == NULL)
              dcc_printf("(%s)[%s/%s]\n", type, src, dst);
           else
              dcc_printf("(%s)[%s/%s] %s\n", type, src, dst, args);
           return 1;
        }
     }
  }
  return 0;
}

void no_mem(void)
{
  close(s);
  exit(-1);
}

int has_dot(char *str)
{
  char *p, ret;

  ret = 0;
  for (p = str; *p; *p++) {
      if ((*p == ' ') || (*p == '\n') || (*p == '\r') || (*p == '!'))
         break;
      else {
         if (*p == '.') {
            ret = 1;
            break;
         }
      }
  }

  return ret;
}
