#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#ifndef __GNUC__
#include <unixlib.h>
#endif
#include "global.h"
#include "constants.h"
#include "numeric.h"
#include "window_i.h"
#include "window.h"
#include "command.h"
#include "chan.h"
#include "qio.h"
#include "tolower.h"
#include "notify.h"
#include "server_i.h"
#include "queue.h"
#include "handle.h"
#include "set.h"
#include "on.h"
#include "split.h"
#include "inp.h"
#include "ignore.h"
#include "sock.h"
#include "dcc.h"
#include "system.h"
#include "mycompare.h"

extern char *g_from, *g_join, *g_public, *g_invite, date[], userhost[];
extern int flushing, old_display, waiting, last_keyhit;
extern struct list_structs list_rpl, names_rpl, who_rpl;
extern win_ptr awins, outwin, gwin;
extern server_ptr gsrv;
extern stackptr userhost_queue;

static server_ptr SRV = NULL;
static char prefix[4];

/* ------------------------------------------------------------------------ */
int strncasecmp(char *t1, char *t2, int num)
{
  if (t1==NULL) return (t2!=NULL);  
  if (t2==NULL) return -1;

  while ((*t1) && (*t2) && (num>0))
  {
    if (tolower(*t1) != tolower(*t2))
    {
      if (tolower(*t1) < tolower(*t2)) return -1;
      return 1;
    }
    t1++; t2++;
    num--;
  }
  if (!num) return 0;
  if (*t1==0)
    if (*t2==0) return 0;
    else return -1;
  return 1;
}

/* ------------------------------------------------------------------------ */
int strcasecmp(char *t1, char *t2)
{
  if (!t1) return (t2?1:0);
  if (!t2) return -1;
  while ((*t1) && (*t2))
  {
    if (tolower(*t1) != tolower(*t2))
      if (tolower(*t1) > tolower(*t2)) return 1;
      else return -1;
    t1++; t2++;
  }
  if (*t1 == 0)
    if (*t2 == 0) return 0;
    else return -1;
  return 1;
}

/* ------------------------------------------------------------------------ */
/* This is only valid because we can only receieve one message from any     */
/* server at any given point in time. If this changes, we will have         */
/* problems. -- dough                                                       */
/* ------------------------------------------------------------------------ */
static char on_line[MAXLEN];
/* ------------------------------------------------------------------------ */
/* Received an error from the server - what do we do about it?!             */
/* ------------------------------------------------------------------------ */
static void han_error(split *temp)
{
  say("%s ERROR - %s", prefix, temp->text);
}

/* ------------------------------------------------------------------------ */
static win_ptr win_get_chan(char *chan, int level)
{
  win_ptr win, old;
  get_chan_num(SRV->chan_list, chan, &win);
  if (!win) win = type_winget(SRV, CRAP);
  old = set_outwin(win);
  return old;
}
/* ------------------------------------------------------------------------ */
static void han_wallops(split *temp)
{
  if (strcasecmp(temp->from, SRV->server_name)!= 0)
    say("!%s! %s", temp->from, temp->text);
  else say("%s", temp->text);
}

/* ------------------------------------------------------------------------ */
static void han_join(split *temp)
{
  char *rest, *chan;
  win_ptr oldwin, towin;

  chan = temp->text;
  towin = gwin;
  if (towin && towin->server != SRV) {
    towin = type_winget(SRV, CRAP);
  }
  if (strcasecmp(temp->from, SRV->nick) == 0) {
    join_channel(SRV, chan, towin);
    (void)new_send(SRV, "MODE %s\n", chan);
  } else add_users_to_chan(SRV->chan_list, chan, temp->from);
  oldwin = win_get_chan(chan, CRAP);
  myfree(g_join);
  g_join = m_strcpy(temp->from);
  (void)sprintf(on_line, "%s %s", temp->from, chan);
  if (!ignore(temp->from, temp->userhost, CRAP) && 
      !handle_on(o_join, on_line, 0))
    say("--- %s (%s) has joined channel %s.", temp->from, temp->userhost, chan);
  set_outwin(oldwin);
}
/* ------------------------------------------------------------------------ */
static void han_part(split *temp)
{
  win_ptr window_num = NULL, old;

  remove_from_chan(SRV->chan_list, temp->p[0], temp->from);
  if (strcasecmp(temp->from, SRV->nick) == 0)
    window_num = (win_ptr)leave_channel(SRV, temp->p[0]);
  else
    get_chan_num(SRV->chan_list, temp->p[0], &window_num);
  if (!window_num) window_num = type_winget(SRV, CRAP);

  (void)sprintf(on_line, "%s %s", temp->from, temp->p[0]);
  old = set_outwin(window_num);
  if (!ignore(temp->from, temp->userhost, CRAP) && 
      !handle_on(o_leave, on_line, 0))
    win_say(window_num, "*** %s has left channel %s.", temp->from, temp->p[0]);
  set_outwin(old);
}

/* ------------------------------------------------------------------------ */
static void han_kick(split *temp)
{
  win_ptr oldwin;

  remove_from_chan(SRV->chan_list, temp->p[0], temp->p[1]);
  oldwin = win_get_chan(temp->p[0], CRAP);
  if (strcasecmp(temp->p[1], SRV->nick) == 0)
    (void)leave_channel(SRV, temp->p[0]);

  if (!ignore(temp->from, temp->userhost, CRAP))
    say("*** %s has been kicked off channel %s by %s (%s)",
        temp->p[1], temp->p[0], temp->from, temp->text);
  set_outwin(oldwin);
}

/* ------------------------------------------------------------------------ */
static void ctcp_action(temp, str)
  split *temp;
  char *str;
{
  int oldlevel;
  char msg[MAXLEN], *walk;
  win_ptr index, win = NW;

  walk = temp->text;
  (void)grab_word(&walk, ' ', msg);  /* Grab the word ACTION */
  if (ignore(temp->from, temp->userhost, ACTIONS)) return;
  (void)sprintf(on_line, "%s %s %s %s", temp->from, temp->p[0], msg, walk);
  if (is_chan(temp->p[0]))
    (void)get_chan_num(SRV->chan_list, temp->p[0], &win);
  else
    win = isquery(SRV, temp->from);
  if (!win) win = type_winget(SRV, ACTIONS);
  if (!handle_on(o_action, on_line, 0)) {
    oldlevel = set_loglevel(ACTIONS);
    if (strcasecmp(temp->p[0], SRV->nick) == 0)
      win_say(win, "*> %s %s", temp->from, str);
    else
      win_say(win, "* %s %s", temp->from, str);
    set_loglevel(oldlevel);
  }
}

/* ------------------------------------------------------------------------ */
static void ctcp_dcc(temp, rest)
  split *temp;
  char *rest;
{
  char type[MAXLEN], file[MAXLEN], host[MAXLEN];
  unsigned long hostid;
  unsigned short port;

  (void)grab_word(&rest, ' ', type);   /* The request type */
  (void)grab_word(&rest, ' ', file);   /* Paramater 1 - type dependant */
  (void)grab_word(&rest, ' ', host);   /* host id in network byte order */

  if (*host) hostid = atol(host);
  port = atoi(rest);                    /* Port - host byte order */
  logdcc_in(hostid, port, temp->from, file, type);
}

/* ------------------------------------------------------------------------ */
static void han_msg_not(int notice, split *temp, char *to, char *msg)
{
  char *rest, format[20], from[MAXLEN];
  win_ptr index, win = NW, old;
  int oldlevel, tome, lvl, o_type;

  lvl = notice?NOTICES:MSGS;
  tome = (strcasecmp(to, SRV->nick) == 0);
  if (temp->userhost[0]) /* Don't do this stuff if it came from a server */
  {
    if (ignore(temp->from, temp->userhost, lvl))
    {
      if (!notice && tome)
        if (getivar(VAR_SEND_IGNORE_MESSAGE))
          if (strcasecmp(to, SRV->nick) == 0)
            (void)new_send(SRV, "NOTICE %s :%s is ignoring you\n",
                           temp->from, SRV->nick);
        return;
    }
    if (check_flood(temp->from, temp->userhost, notice?"NOTICE":"MSGS"))
      return;
  }

/* Save who the last message was from */
  if (!notice && tome) {
    myfree(g_from);
    g_from = m_strcpy(temp->from);
  }

  (void)sprintf(on_line, "%s %s", temp->from, msg);

  if (is_chan(temp->p[0]))
    get_chan_num(SRV->chan_list, temp->p[0], &win);
  else
    win = isquery(SRV, temp->from);
  if (!win) win = type_winget(SRV, lvl);

  if (!is_chan(temp->p[0]) && !tome)
    sprintf(from, "%s\\%s", temp->from, to);
  else
    strcpy(from, temp->from);

  old = set_outwin(win);
  oldlevel = set_loglevel(lvl);
  if (notice) {
     if (!temp->userhost[0]) o_type = o_server_notice;
     else o_type = o_notice;
  } else o_type = o_msg;

  if (!handle_on(o_type, on_line, 0)) {
    char delim = notice?'\026':'*';
    if (notice)
      if (!temp->userhost[0])
        if (strcasecmp(SRV->server_name, temp->from) == 0)
/* a notice from my server.. don't put a prefix on the message */
           *from = 0;
    if (is_away(SRV) && !notice) {
      (void)strcpy(format, "%c%s%c%c%s <%s>");
      if (getivar(VAR_BEEP_WHEN_AWAY)) (void)strcat(format, "\007");
    } else
      (void)strcpy(format, "%c%s%c%c%s");
    if (*from)
      win_say(win, format, delim, from, delim, ' ', msg, date);
    else
    {
      char *tmp = (char *)(int)format + 8; /* jump over the '%c%s%c%c' */
      win_say(win, tmp, msg, date);
    }

  }
  set_loglevel(oldlevel);
  set_outwin(old);
}

/* ------------------------------------------------------------------------ */
static void msg(temp, to, msg)
  split *temp;
  char *to, *msg;
{
  char *rest, format[20], from[MAXLEN];
  win_ptr index, win = NW, old;
  int oldlevel;

  if (ignore(temp->from, temp->userhost, MSGS))
  {
    if (getivar(VAR_SEND_IGNORE_MESSAGE))
      if (strcasecmp(to, SRV->nick) == 0)
        (void)new_send(SRV, "NOTICE %s :%s is ignoring you\n",
                       temp->from, SRV->nick);
    return;
  }
  if (check_flood(temp->from, temp->userhost, "MSGS")) return;

  myfree(g_from);
  g_from = m_strcpy(temp->from);
  (void)sprintf(on_line, "%s %s", temp->from, msg);
  win = isquery(SRV, temp->from);
  if (!win) win = type_winget(SRV, MSGS);
  if (strcasecmp(to, SRV->nick) != 0)
    sprintf(from, "%s\\%s", temp->from, to);
  else
    strcpy(from, temp->from);
  old = set_outwin(win);
  oldlevel = set_loglevel(MSGS);
  if (!handle_on(o_msg, on_line, 0)) {
    if (is_away(SRV)) {
      (void)strcpy(format, "*%s* %s <%s>");
      if (getivar(VAR_BEEP_WHEN_AWAY)) (void)strcat(format, "\007");
    } else
      (void)strcpy(format, "*%s* %s");
    win_say(win, format, from, msg, date);
  }
  set_loglevel(oldlevel);
  set_outwin(old);
}

/* ------------------------------------------------------------------------ */
static void public(temp, msg)
  split *temp;
  char *msg;
{
  win_ptr win = NW;
  int oldlevel;

  if (ignore(temp->from, temp->userhost, PUBLIC)) return;
  if (check_flood(temp->from,temp->userhost, "PUBLIC")) return;

  myfree(g_public);
  g_public = m_strcpy(temp->from);
  (void)get_chan_num(SRV->chan_list, temp->p[0], &win);
  oldlevel = set_loglevel(PUBLIC);
  (void)sprintf(on_line, "%s %s %s", temp->from, temp->p[0], msg);
  if (win != NW) {
    if (!handle_on(o_public, on_line, 0))
      if (user_on_chan(SRV->chan_list, temp->p[0], temp->from))
        win_say(win, "<%s> %s", temp->from, msg);
      else
        win_say(win, "(%s/%s) %s", temp->from, temp->p[0], msg);
  } else {
    win = type_winget(SRV, PUBLIC);
    if (!handle_on(o_public_other, on_line, 0))
      win_say(win, "<%s\\%s> %s", temp->from, temp->p[0], msg);
  }
  set_loglevel(oldlevel);
}

/* ------------------------------------------------------------------------ */
static void han_ctcp(temp, to, rest)
  split *temp;
  char *to, *rest;
{
  char type[MAXLEN];

  (void)sprintf(on_line, "%s %s %s", temp->from, temp->p[0], rest);
  (void)grab_word(&rest, ' ', type);
  if (check_flood(temp->from, temp->userhost, "CTCP")) return;
  if (getbvar(VAR_VERBOSE_CTCP) && (strcasecmp(type, "ACTION")!=0))
    if (strcasecmp(temp->p[0], SRV->nick) == 0)
      say("*** CTCP %s from %s%s%s", type, temp->from, *rest?": ":"",
          *rest?rest:"");
    else
      say("*** CTCP %s from %s to %s%s%s", type, temp->from, temp->p[0],
          *rest?": ":"", *rest?rest:"");
  if (strcmp(type, "DCC")!=0)
    if (handle_on(o_ctcp, on_line, 0)) return;

  if (strcmp("ACTION", type) == 0)
    ctcp_action(temp, rest);  /* Get rid of the space */
  else if (strcmp("CLIENTINFO", type) == 0)
    (void)new_send(SRV,
               "NOTICE %s :\001CLIENTINFO ACTION CLIENTINFO DCC VERSION\001\n",
             temp->from);
  else if (strcmp("DCC", type) == 0) {
    ctcp_dcc(temp, rest);
    if (handle_on(o_ctcp, on_line, 0)) return;
  } else if (strcmp("ERRMSG", type) == 0) { }
  else if (strcmp("FINGER", type) == 0)
  {
    char real[MAXLEN], user[MAXLEN], host[MAXLEN];
    get_username(user, 0);
    get_username(real, 1);                         
    tcp_gethostname(host);
    new_send(SRV, "NOTICE %s :\001FINGER %s (%s@%s) Idle %d seconds\001\n",
             temp->from, real, user, host, time(NULL)-last_keyhit);
  } else if (strcmp("PID", type) == 0)
    (void)new_send(SRV, "NOTICE %s :\001PID %X\001\n", temp->from, getpid());
  else if (strcmp("PING", type) == 0)
    (void)new_send(SRV, "NOTICE %s :\001PING %s\001\n", temp->from, rest);
  else if (strcmp("VERSION", type) == 0)
    (void)new_send(SRV, "NOTICE %s :\001VERSION Dough's vms client:%s:VMS\001\n",
             temp->from, VERSION);
}

/* ------------------------------------------------------------------------ */
static void han_ctcp_reply(split *temp, char *str)
{
  char *first;
  sprintf(on_line, "%s %s", temp->from, str);
  first = m_grab_word(&str, ' ');
  if (!handle_on(o_ctcp_reply, on_line, 0)) {
    if (strncmp(first, "PING", 4) == 0) 
      say("CTCP PING reply from %s: %d seconds",
          temp->from, time(NULL) - atoi(str));
    else
      say("*** CTCP %s reply from %s:%s", first, temp->from, str);
  }
  myfree(first);
}

/* ------------------------------------------------------------------------ */
#define X_DELIM   1
#define X_QUOTE  '\\'
static void strip_ctcp(split *temp, char *msg, char *ret, int notice)
{
  int num, done = 0;
  char *c_ind;
  num = getbvar(VAR_NO_CTCP_FLOOD);
  while (*msg)
  {
    if (*msg == X_DELIM) {
      c_ind = ++msg;
      while (*msg && ((*msg != X_DELIM) || (msg[-1] == X_QUOTE))) msg++;
      if (*msg) { /* balanced */
        *msg = 0;
        if (!num || !done) 
          if (!notice) han_ctcp(temp, temp->p[0], c_ind);
          else han_ctcp_reply(temp, c_ind);
        done = TRUE;
        *msg++ = X_DELIM;
      }
    } else *ret++ = *msg++;
  }
  *ret = 0;
}
/* ------------------------------------------------------------------------ */
static void han_privmsg(temp)
  split *temp;
{
  char privmsg[MAXLEN];

  if ((is_chan(temp->p[0]) ||
       (strcasecmp(temp->p[0], get_server_nick(SRV))==0)) &&
      !strstr(temp->from, "."))
    strip_ctcp(temp, temp->text, privmsg, FALSE);
  else
    strcpy(privmsg, temp->text);
  if (!*privmsg) return;
  if (is_chan(temp->p[0])) public(temp, privmsg);
  else
    han_msg_not(FALSE, temp, temp->p[0], privmsg);
}

/* ------------------------------------------------------------------------ */
static void han_notice(split *temp)
{
  char notice[MAXLEN];

  if ((is_chan(temp->p[0]) ||
       (strcasecmp(temp->p[0], get_server_nick(SRV))==0)) &&
      !strstr(temp->from, "."))
    strip_ctcp(temp, temp->text, notice, TRUE);
  else
    strcpy(notice, temp->text);
  if (!*notice) return;
  han_msg_not(TRUE, temp, temp->p[0], notice);
}
/* ------------------------------------------------------------------------ */
static void han_ping(split *temp)
{
  (void)new_send(SRV, "PONG %s\n", temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_whois(split *temp, int IS)
{
  say("%s %s\017 %s %s\017@%s (%s\017)", prefix, temp->p[1],
      IS?"is":"was", temp->p[2], temp->p[3], temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_whochan(split *temp)
{
  say("%s on channels: %s", prefix, temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_whoserv(split *temp)
{
  say("%s on irc via server %s (%s)", prefix, temp->p[2], temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_whoidle(split *temp)
{
  say("%s %s has been idling for %s seconds.", prefix, temp->p[1], temp->p[2]);
}
/* ------------------------------------------------------------------------ */
static void han_whoircop(split *temp)
{
  say("%s %s %s", prefix, temp->p[1], temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_server_message(split *temp)
{
  say("%s %s: %s", prefix, temp->p[1], temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_noparams(split *temp)
{
  say("%s %s", prefix, temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_oneparam(split *temp)
{
  win_ptr oldwin;
  oldwin = win_get_chan(temp->p[1], CRAP);
  say("%s %s: %s", prefix, temp->p[1], temp->text);
  set_outwin(oldwin);
}
/* ------------------------------------------------------------------------ */
static void han_twoparams(split *temp)
{
  say("%s %s %s: %s", prefix, temp->p[1], temp->p[2], temp->text);
}
/* ------------------------------------------------------------------------ */
static void han_nonick(split *temp)
{
  say("%s %s: %s", prefix, temp->p[1], temp->text);
  if (getivar(VAR_AUTO_WHOWAS)) new_send(SRV, "WHOWAS %s 1\n", temp->p[1]);
}
/* ------------------------------------------------------------------------ */
void user_chan_nick();
/* ------------------------------------------------------------------------ */
static void han_nick(temp)
  split *temp;
{
  char chan[MAXLEN];
  win_ptr win = NW;

  user_chan_nick(SRV->chan_list, temp->from, chan, 3);
  if (*chan) (void)get_chan_num(SRV->chan_list, chan, &win);
  if (!win) win = type_winget(SRV, CRAP);

  if (strcmp(temp->from, SRV->nick) == 0)
  {
    win_say(win, "--> You are now known as %s.", temp->text);
    myfree(SRV->nick);
    SRV->nick = m_strcpy(temp->text);
  }
  else
    win_say(win, "--> %s is now known as %s.", temp->from, temp->text);

  (void)sprintf(on_line, "%s %s", temp->from, temp->text);
  user_chan_nick(SRV->chan_list, temp->from, temp->text, 1);
  handle_on(o_nickname, on_line, 0); 
}

/* ------------------------------------------------------------------------ */
static void han_def(split *temp)
{
  if (ignore(temp->from, temp->userhost, CRAP)) return;
  say("%s %s %s %s %s %s %s %s %s",
      temp->from, temp->userhost, temp->command, temp->p[0],
      temp->p[1], temp->p[2], temp->p[3], temp->p[4], temp->text);
}

/* ------------------------------------------------------------------------ */
static void han_away(temp)
  split *temp;
{
  say("%s %s is away: %s", prefix, temp->p[1], temp->text);
}

/* ------------------------------------------------------------------------ */
static void han_invite(temp)
  split *temp;
{
  myfree(g_invite);
  g_invite = m_strcpy(temp->text);
  if (ignore(temp->from, temp->userhost, CRAP)) return;
  (void)sprintf(on_line, "%s %s", temp->from, temp->text);
  if (handle_on(o_invite, on_line, 0)) return; 
  say("%s You have been invited to channel \"%s\" by %s.", prefix,
      temp->text, temp->from);
}

/* ------------------------------------------------------------------------ */
static void han_modeis(temp)
  split *temp;
{
  int loop;
  char str[255];
  win_ptr win;
  (void)get_chan_num(SRV->chan_list, temp->p[1], &win);
  strcpy(str, temp->p[2]);
  for (loop=3; temp->p[loop][0]; loop++)
  {
    strcat(str, " ");
    strcat(str, temp->p[loop]);
  }
  if (!win) win = type_winget(SRV, CRAP);
  else set_chan_mode(SRV->chan_list, temp->p[1], str);
  if (temp->p[2][0] && temp->p[2][1])
    win_say(win, "%s Mode for channel %s is \"%s\"", prefix, temp->p[1], str);
}

/* ------------------------------------------------------------------------ */
static void han_invited(temp)
  split *temp;
{
  win_ptr win;
  (void)get_chan_num(SRV->chan_list, temp->p[2], &win);
  if (!win) win = type_winget(SRV, CRAP);
  win_say(win, "%s %s has been invited to %s", prefix, temp->p[1], temp->p[2]);
}

/* ------------------------------------------------------------------------ */
static void han_stats_basic(temp)
  split *temp;
{
  say("%s %s: H: %s N: %s Port: %s Class: %s", prefix, 
      temp->p[1], temp->p[2], temp->p[3], temp->p[4], temp->p[5]);
}

/* ------------------------------------------------------------------------ */
static void han_mode(split *temp)
{
  int args;
  char templine[MAXLEN], mode[MAXLEN];
  win_ptr win;

  (void)strcpy(mode, temp->text);
  for (args=1;*(temp->p[args]); args++) {
    (void)strcat(mode, temp->p[args]);
    (void)strcat(mode, " ");
  }
  if (ignore(temp->from, temp->userhost, CRAP)) return;
  (void)sprintf(on_line, "%s %s %s", temp->from, temp->p[0], mode);
  if (!is_chan(temp->p[0]))
    (void)sprintf(templine, "%s Mode change \"%s\" for user %s by %s",
                  prefix, mode, temp->p[0], temp->from);
  else if (strlen(temp->from) == 0)
    (void)sprintf(templine, "%s Mode change \"%s\" on channel %s by %s",
                  prefix, mode, temp->p[0], temp->userhost);
  else
    (void)sprintf(templine, "%s Mode change \"%s\" on channel %s by %s",
                  prefix, mode, temp->p[0], temp->from);
  (void)get_chan_num(SRV->chan_list, temp->p[0], &win);
  if (!win) win = type_winget(SRV, CRAP);
  else set_chan_mode(SRV->chan_list, temp->p[0], mode);
  if (!handle_on(o_mode, on_line, 0))
    win_say(win, "%s", templine);
}

/* ------------------------------------------------------------------------ */
static void han_userhost(temp)
  split *temp;
{
  char *str, nick[MAXLEN], param[MAXLEN], *ret, p1 = '-', p2 = '+';
  char user[MAXLEN];
  char *snick, *command;
  int loop, len, print = 1;

  if (flushing) {
    setivar(VAR_DISPLAY, old_display);
    flushing = FALSE;
    say("*** Flushing completed.");
    print = 0;
    return;
  } 
  command = pop_element(userhost_queue, 0, &snick);
  str = temp->text;
  len = grab_word(&str, '=', nick);
  if (*str) {
    if (nick[len-1]=='*') {
      nick[len-1] = 0;
      p1 = '+';
    }
    p2 = *str++;
    grab_word(&str, '@', user);
  } else {
    len = 0;
    strcpy(user, "<UNKNOWN>");
    strcpy(str,  "<UNKNOWN>");
  }
  sprintf(param, "%s %c %c %s %s", (len)?nick:snick, p1, p2, user, str);

  if (command)
    parse_line(command, param, FALSE, FALSE);
  else
    if (!handle_on(o_numeric, param, 302))
      if (print)
        say("%s %s is %s@%s%s%s", prefix, (len)?nick:snick, user, str,
            (p1=='+')?" (Is an IRC operator)":"", (p2=='-')?" (Away)":"");
}

/* ------------------------------------------------------------------------ */
static void han_join_list(temp)
  split *temp;
{
  char *str, on_line[MAXLEN];
  int count = 0;
  win_ptr win = NULL;

  add_users_to_chan(SRV->chan_list, temp->p[2], temp->text);
  sprintf(on_line, "%s %s", temp->p[2], temp->text);
  if (handle_on(o_names, on_line, 0)) return;

  if (*temp->p[2] == '*')  {
    if (!names_rpl.private) return;
  } else
   if (!names_rpl.public) return;

  str = temp->text;
  while (*str) if (*str++ == ' ') count++;
  if (count < names_rpl.min) return;
  if ((count > names_rpl.max) && (names_rpl.max != -1)) return;
  if (names_rpl.str)
    if (wild_match(names_rpl.str, temp->p[2]) < 0) return;
  (void)get_chan_num(SRV->chan_list, temp->p[2], &win);
  if (!win) win = type_winget(SRV, CRAP);
  win_say(win, "%s Users on %s: %s", prefix, temp->p[2], temp->text);
}

/* ------------------------------------------------------------------------ */
static void han_quit(temp)
  split *temp;
{
  int gone = FALSE;
  char chan[MAXLEN];
  win_ptr win = NW, old;

  user_chan_nick(SRV->chan_list, temp->from, chan, 2);
  (void)get_chan_num(SRV->chan_list, chan, &win);
  if (!win) win = type_winget(SRV, CRAP);
  (void)sprintf(on_line, "%s %s", temp->from, temp->text);
  old = set_outwin(win);
  if (handle_on(o_signoff, on_line, 0)) gone = TRUE;
  if (!gone) {
    (void)sprintf(on_line, "%s %s %s", chan, temp->from, temp->text);
    if (handle_on(o_channel_signoff, on_line, 0)) gone = TRUE;
    if (!gone)
      win_say(win, "%s Signoff by %s (%s)", prefix, temp->from, temp->text);
  }
  set_outwin(old);
}

/* ------------------------------------------------------------------------ */
static void han_unknown(temp)
  split *temp;
{
  if (strcasecmp(temp->p[1], "WAIT") == 0) {
    waiting = FALSE;
  } else if (strcasecmp(temp->p[1], "REDIRECT") == 0)
    set_redirect(NULL, SRV);
}

/* ------------------------------------------------------------------------ */
static void han_topic(split *temp)
{
  win_ptr oldwin;
  (void)sprintf(on_line, "%s %s %s", temp->from, temp->p[0], temp->text);
  oldwin = win_get_chan(temp->p[0], CRAP);
  if (!handle_on(o_topic, on_line, 0))
    say("%s %s has changed the topic on channel %s to %s", prefix, 
        temp->from, temp->p[0], temp->text);
  set_outwin(oldwin);
}

/* ------------------------------------------------------------------------ */
static void han_who(split *temp)
{
  sprintf(on_line, "%s %s %s %s %s %s", temp->p[1], temp->p[5],
          temp->p[6], temp->p[2], temp->p[3], temp->text);
  if (handle_on(o_who, on_line, 0)) return;
  if (who_rpl.chop_only && (!strchr(temp->p[6], '@'))) return;
  if (who_rpl.ircop_only && (!strchr(temp->p[6], '*'))) return;
  say("%-11.11s %-10.10s %-4.4s %s@%s (%s)",
      temp->p[1], temp->p[5], temp->p[6], temp->p[2], temp->p[3], temp->text);
}

static void han_list(split *temp)
{
  int num = 0, loop;
  (void)sprintf(on_line, "%s %s %s %s", temp->from, temp->p[1], temp->p[2],
                temp->text);
  if (handle_on(o_numeric, on_line, RPL_LIST)) return;
  num = atoi(temp->p[2]);
  if (num<list_rpl.min) return;
  if ((num>list_rpl.max) && (list_rpl.max != -1)) return;
  if (list_rpl.str)
    if (wild_match(list_rpl.str, temp->p[1]) < 0) return;
  if (*(temp->p[1]) == '*') {
    if (!list_rpl.private) return;
  } else if (!list_rpl.public) return;
  num = sprintf(on_line, "%*.*s", -getivar(VAR_CHANNEL_NAME_WIDTH),
                getivar(VAR_CHANNEL_NAME_WIDTH), temp->p[1]);
  for (loop=num;loop<getivar(VAR_CHANNEL_NAME_WIDTH);loop++)
    on_line[loop] = ' ';
  on_line[loop] = 0;
  say("*** %s %-5s %s", on_line, temp->p[2], temp->text);
}

/* ------------------------------------------------------------------------ */
static void han_cantjoin(split *temp, int num)
{
  win_ptr oldwin;
  oldwin = win_get_chan(temp->p[1], CRAP);
  say("%s %s: %s (%s)", prefix, temp->p[1], temp->text,
       (num==ERR_KEYSET)?"key is set":
       (num==ERR_INVITEONLYCHAN)?"invite only":
       (num==ERR_CHANNELISFULL)?"channel full":
       (num==ERR_BANNEDFROMCHAN)?"banned":
       (num==ERR_BADCHANMASK)?"bad channel mask":
       (num==ERR_BADCHANNELKEY)?"incorrect key":"unknown reason");
}
/* ------------------------------------------------------------------------ */
static CmdList handle_list[] =
{
 {{"ERROR"},	han_error,	0},
 {{"INVITE"},	han_invite,	0},
 {{"JOIN"},	han_join,	0},
 {{"KICK"},	han_kick,	0},
 {{"MODE"},	han_mode,	0},
 {{"NICK"},	han_nick,	0},
 {{"NOTICE"},	han_notice,	0},
 {{"PART"},	han_part,	0},
 {{"PING"},	han_ping,	0},
 {{"PRIVMSG"},	han_privmsg,	0},
 {{"QUIT"},	han_quit,	0},
 {{"TOPIC"},	han_topic,	0},
 {{"WALLOPS"},	han_wallops,	0}
};
#define NUMBER_HANDLE (sizeof(handle_list) / sizeof(CmdList)) -1

/* ------------------------------------------------------------------------ */
void handle_line(line, srv)
  char line[MAXLEN];
  server_ptr srv;
{
  CmdList *command;
  split temp;
  char *eek, str[10];
  int loop, cnt;
  win_ptr old_outwin;
  server_ptr old_gsrv;

  SRV = srv;
  old_outwin = outwin;
  outwin = type_winget(SRV, CRAP);
  if (!line_split(line, &temp)) return;
  old_gsrv = gsrv;
  gsrv = SRV;
  if (*line==':') eek = &line[1];
  else eek = line;
  if (handle_on(o_raw_irc, eek, 0)) {
    gsrv = old_gsrv;
    return;
  }
/* 302 is special .. the userhost reply - lame ass compatibilty for phone */
  if (temp.commandnum && (temp.commandnum != 302)) {
    (void)strcpy(on_line, temp.from);
    for (loop=1; (loop<MAX_ARGS) && *temp.p[loop]; loop++) {
      strcat(on_line, " ");
      strcat(on_line, temp.p[loop]);
    }
    strcat(on_line, " ");
    strcat(on_line, temp.text);
    if (handle_on(o_numeric, on_line, temp.commandnum)) {
      outwin = old_outwin;
      gsrv = old_gsrv;
      return;
     }
  }
  if (temp.commandnum && getbvar(VAR_SHOW_NUMERICS))
    sprintf(prefix, "%3.3d", temp.commandnum);
  else
    sprintf(prefix, "***");
  switch (temp.commandnum) {
    case RPL_AWAY:
      han_away(&temp);
      break;
    case RPL_ISON:
      han_notify(gsrv, temp.text);
      break;
    case RPL_WHOISUSER:
      han_whois(&temp, TRUE);
      break;
    case RPL_WHOISSERVER:
      han_whoserv(&temp);
      break;
    case RPL_WHOISOPERATOR:
      han_whoircop(&temp);
      break;
    case RPL_WHOWASUSER:
      han_whois(&temp, FALSE);
      break;
    case RPL_WHOISIDLE:
      han_whoidle(&temp);
      break;
    case RPL_WHOISCHANNELS:
      han_whochan(&temp);
      break;
    case RPL_WHOREPLY:
      han_who(&temp);
      break;
    case RPL_NAMREPLY:
      han_join_list(&temp);
      break;
    case RPL_LISTSTART:
      han_server_message(&temp);
      break;
    case RPL_LIST:
      han_list(&temp);
      break;
    case ERR_USERNOTINCHANNEL:
    case ERR_USERONCHANNEL:
    case RPL_LINKS:
    case RPL_VERSION:
      han_twoparams(&temp);
      break;
    case RPL_MYINFO:
     /* we really don't want to see this information */
      break;
    case RPL_UMODEIS:
      say("%s Your user mode is \"%s\"", prefix, temp.p[1]);
      break;
    case RPL_INVITING:
      han_invited(&temp);
      break;
    case RPL_CHANNELMODEIS:
      han_modeis(&temp);
      break;
    case RPL_USERHOST:
      han_userhost(&temp);
      break;
    case RPL_WELCOME:
      gsrv->welcomed = TRUE;
      myfree(gsrv->nick);
      gsrv->nick = m_strcpy(temp.p[0]);
      if (gsrv->to_join) {
        new_send(gsrv, "JOIN %s\n", SRV->to_join);
        myfree(gsrv->to_join);
        gsrv->to_join = NULL;
      }
      han_noparams(&temp);
      break;
    case ERR_NOSUCHNICK:
      han_nonick(&temp);
      break;
    case RPL_ENDOFNAMES:
      if (temp.commandnum==RPL_ENDOFNAMES)
        if (names_rpl.str) {
          myfree(names_rpl.str);
          names_rpl.str = NULL;
        }
    case RPL_ENDOFWHO:
      if (temp.commandnum==RPL_ENDOFWHO)
        if (who_rpl.str) {
          myfree(who_rpl.str);
          who_rpl.str = NULL;
        }
    case RPL_ENDOFWHOIS:
    case RPL_ENDOFWHOWAS:
    case RPL_ENDOFSTATS:
    case RPL_ENDOFLINKS:
    case RPL_ENDOFBANLIST:
      if (getivar(VAR_SHOW_END_OF_MSGS))
        han_oneparam(&temp);
      break;
    case ERR_NOORIGIN:
    case ERR_NORECIPIENT:
    case ERR_NOTEXTTOSEND:
    case ERR_NOMOTD:
    case ERR_FILEERROR:
    case ERR_SUMMONDISABLED:
    case ERR_USERSDISABLED:
    case ERR_ALREADYREGISTRED:
    case ERR_NOPERMFORHOST:
    case ERR_PASSWDMISMATCH:
    case ERR_YOUREBANNEDCREEP:
    case ERR_NOPRIVILEGES:
    case ERR_CANTKILLSERVER:
    case ERR_NOOPERHOST:
    case ERR_UMODEUNKNOWNFLAG:
    case ERR_USERSDONTMATCH:
    case RPL_YOURHOST:
    case RPL_CREATED:
    case RPL_MOTD:
    case RPL_ENDOFMOTD:
    case RPL_MOTDSTART:
    case RPL_UNAWAY:
    case RPL_NOWAWAY:
      if ((temp.commandnum == RPL_UNAWAY) || (temp.commandnum == RPL_NOWAWAY))
      {
        set_server_away(gsrv, (temp.commandnum == RPL_NOWAWAY));
      }
    case RPL_LISTEND:
      if (temp.commandnum==RPL_LISTEND)
        if (list_rpl.str) {
          myfree(list_rpl.str);
          list_rpl.str = NULL;
        }
    case RPL_LUSERCLIENT:
    case RPL_LUSERME:
    case RPL_USERSSTART:
    case RPL_USERS:
    case RPL_ENDOFUSERS:
    case RPL_NOUSERS:
    case RPL_STATSUPTIME:
    case RPL_YOUREOPER:
    case RPL_INFO:
    case RPL_ENDOFINFO:
    case RPL_ADMINLOC1:
    case RPL_ADMINLOC2:
    case RPL_ADMINEMAIL:
    case ERR_NOTREGISTERED:
      han_noparams(&temp);
      break;
    case ERR_UNKNOWNCOMMAND:
      han_unknown(&temp);
      break;
    case ERR_ERRONEUSNICKNAME:
      if (temp.commandnum == ERR_ERRONEUSNICKNAME)
        say("%s You entered an invalid nickname.", prefix);
    case ERR_NONICKNAMEGIVEN:
      if (temp.commandnum == ERR_NONICKNAMEGIVEN)
        say("%s No nickname was given.", prefix);
    case ERR_NICKNAMEINUSE:
      if (temp.commandnum == ERR_NICKNAMEINUSE)
        say("%s Someone already has that nickname. Please chose another.",
             prefix);
      if (!gsrv->welcomed) {
        read_input(str, 9, "Nickname: ");
        new_send(gsrv, "NICK %s\n", str);
      }
      break;
    case ERR_KEYSET:
    case ERR_CHANNELISFULL:
    case ERR_INVITEONLYCHAN:
    case ERR_BANNEDFROMCHAN:
    case ERR_BADCHANNELKEY:
      han_cantjoin(&temp, temp.commandnum);
      break;
    case RPL_TOPIC:
    case ERR_NOSUCHSERVER:
    case ERR_NOSUCHCHANNEL:
    case ERR_CANNOTSENDTOCHAN:
    case ERR_TOOMANYCHANNELS:
    case ERR_WASNOSUCHNICK:
    case ERR_TOOMANYTARGETS:
    case ERR_NOTOPLEVEL:
    case ERR_WILDTOPLEVEL:
    case ERR_NOADMININFO:
    case ERR_NICKCOLLISION:
    case ERR_NOTONCHANNEL:
    case ERR_NOLOGIN:
    case ERR_NEEDMOREPARAMS:
    case ERR_UNKNOWNMODE:
    case ERR_CHANOPRIVSNEEDED:
    case RPL_NOTOPIC:
    case RPL_SUMMONING:
    case RPL_LUSEROP:
    case RPL_LUSERCHANNELS:
    case RPL_LUSERUNKNOWN:
    case RPL_TIME:
    case RPL_REHASHING:
    case RPL_ADMINME:
      han_oneparam(&temp);
      break;
    case ERR_NOSUCHSERVICE:
    case ERR_SERVICENAMEINUSE:
    case ERR_SERVICECONFUSED:
    case RPL_TEXT:
    case RPL_YOURESERVICE:
    case RPL_NOTOPERANYMORE:
    case RPL_TRACESERVICE:
      say("+++ Numeric (%d) not in rfc.", temp.commandnum);
      han_def(&temp);
      break;
    case ERR_BADCHANMASK:
    case ERR_NOSERVICEHOST: 
    case RPL_KILLDONE:
    case RPL_CLOSING:
    case RPL_CLOSEEND:
    case RPL_INFOSTART:
    case RPL_MYPORTIS:
    case RPL_SERVICEINFO:
    case RPL_ENDOFSERVICES:
    case RPL_SERVICE:
    case RPL_SERVLIST:
    case RPL_SERVLISTEND:
    case RPL_WHOISCHANOP:
      say("+++ Reserved/not in use from rfc.");
      han_def(&temp);
      break;
    case RPL_STATSCLINE:
    case RPL_STATSNLINE:
    case RPL_STATSILINE:
    case RPL_STATSKLINE:
    case RPL_STATSQLINE:
      han_stats_basic(&temp);
      break;
    case RPL_STATSLINKINFO:
    case RPL_STATSCOMMANDS:
    case RPL_STATSYLINE:
    case RPL_STATSOLINE:
    case RPL_STATSHLINE:
    case RPL_STATSSLINE:
    case RPL_TRACELINK:
    case RPL_TRACECONNECTING:
    case RPL_TRACEHANDSHAKE:
    case RPL_TRACEUNKNOWN:
    case RPL_TRACEOPERATOR:
    case RPL_TRACEUSER:
    case RPL_TRACESERVER:
    case RPL_TRACENEWTYPE:
    case RPL_TRACECLASS:
    case RPL_BANLIST:
    case RPL_STATSLLINE:
    case RPL_TRACELOG:
      say("%s %s %s %s %s %s %s", prefix, temp.p[1],
          temp.p[2], temp.p[3], temp.p[4], temp.p[5], temp.p[6]);
      break;
    case 0: 
      strcpy(prefix, "***");
      (void)strcpy(userhost, temp.userhost);
      command = exact_find_command(handle_list, NUMBER_HANDLE, temp.command);
      if (command)
        command->cmdfcn(&temp);
      else
        han_def(&temp);
      break;
    default:
      han_def(&temp);
  } /* switch statement */
  outwin = old_outwin;
  gsrv = old_gsrv;
  (void)find_input_prompt(FALSE);
  updatestatwin();
}
