#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <ncurses.h>

#include "defines.h"
#include "colors.h"
#include "codes.h"
#include "scheck.h"
#include "scmds.h"
#include "nap.h"
#include "winio.h"
#include "irc.h"
#include "alias.h"
#include "lists.h"

#ifdef MEMWATCH
  #include "memwatch.h"
#endif

extern info_t info;
extern chans_t *chanl, *curchan, *recent;
extern int wmode;
extern int tind;

int ircsock = 0;
char *nbuf;
char *mnick = NULL;
int ircin=0, ircmode=0, nwho=0;
char ircbuf[4096] = "\0";
int cpos = 0, cport = -1;

in_irc_cmd_t handlers[] = {
  { "ping", hping },
  { "433", h433 },
  { "372", h372 },
  { "353", h353 },
  { "332", h332 },
  { "333", h333 },
  { "311", h311 },
  { "319", h319 },
  { "312", h312 }, 
  { "367", h367 },
  { "376", h376 },
  { "315", h315 },
  { "352", h352 },
  { "324", h324 },
  { "341", h341 },
  { "314", h314 },
  { "317", h317 },
  { "313", h313 },
  { "join", hjoin },
  { "part", hpart },
  { "topic", htopic },
  { "quit", hquit },
  { "nick", hnick }, 
  { "mode", hmode },
  { "kick", hkick },
  { "privmsg", hprivmsg },
  { "notice", hnotice },
  { "invite", hinvite },
  { "482", herr },
  { "475", herr },
  { "474", herr },
  { "473", herr },
  { "472", herr },
  { "471", herr },
  { "461", herr },
  { "443", herr },
  { "442", herr },
  { "421", herr },
  { "406", herr },
  { "404", herr },
  { "401", herr },
  { "error", herr2 },
  { NULL, 0 }
};

out_irc_cmd_t outs[] = {
  { "msg", imsg },
  { "quit", iquit },
  { "kick", ikick },
  { "me", ime },
  { "notice", inotice },
  { "nick", inick },
  { "irc", iirc },
  { "whois", iwhois },
  { "topic", itopic },
  { "part", ipart },
  { "addr", iaddr },
  { "ban", iban },
  { "nap", inap },
  { "ctcp", ictcp },
  { "invite", iinvite },
  { "opsay", iopsay },
  { "dcc", idcc },
  { "chat", ichat },
  { NULL, NULL },
};


int dccfunc(WINDOW *win, sock_t *m)
{
  char *nm = strchr(m->socknm, ' ')+1;
  unsigned char msg[1024];
  int i;
  
  memset(msg, 0, sizeof(msg));
  
  for (i=0;i<1024;i++)
  {
    if (read(m->fd, msg+i, 1) <= 0)
    {
      wp(win, "%s* DCC Connection to %s closed%s\n", RED, nm, WHITE);
      drw(win);
      delsock(m->fd);
      return(1);
    }
    if (msg[i] == '\n')
    {
      msg[i] = 0;
      if (i > 0 && msg[i-1] == '\r')
        msg[i-1] = 0;
      break;
    }
  }
  
  wp(win, "%s* [%s(%s)%s]%s %s\n", BRIGHT(GREEN), WHITE, nm, BRIGHT(GREEN), WHITE, msg);
  drw(win);
  
  return(1);
}

int ldcc(WINDOW *win, sock_t *m)
{
  int s;
  struct sockaddr_in frm;
  int frmlen = sizeof(frm);
  char *nm=NULL;
  
  s = accept(m->fd, (struct sockaddr *)&frm, &frmlen);
  if (s == -1)
  {
    delsock(m->fd);
    return(1);
  }
  
  msprintf(&nm, "chat %s", strchr(m->socknm, ' ')+1);
  
  addsock(s, nm, S_R, dccfunc);
  free(nm);
  
  wp(win, "%s* DCC Connection to %s established%s\n", BRIGHT(CYAN), strchr(m->socknm, ' ')+1, WHITE);
  drw(win);
  
  delsock(m->fd);
  return(1);
}

int inirc(WINDOW *win, sock_t *m)
{
  int s = m->fd, i, r, cnt, pt, j;
  char buf[4096], **tok;
  sock_t *t;
  
  memset(buf, 0, sizeof(buf));
  
  j = recv(s, &buf, sizeof(buf), MSG_PEEK);
  if (j <= 0)
  {
    wp(win, "%s* Disconnected from IRC server%s\n", RED, WHITE);
    drw(win);
    close(ircsock);
    ircsock = 0;
    ircin = 0;
    nwho = 0;
    delircchans();
    if (wmode && screen==MAIN_SCREEN)
    {
      dscr(win);
      drw(win);
      indraw();
      dstatus();
    }

    ircmode = 0;
    mnick = NULL;
    delsock(m->fd);
    return(1);
  }
  
  if (!strchr(buf, '\n') && !(*ircbuf))
  {
    r = recv(s, ircbuf, j, 0);
    cpos += r;
    return(1);
  }
  else if (!(*ircbuf))
  {
    for (i=0;buf[i]!='\n';i++);
    memset(buf, 0, sizeof(buf));
    r = recv(s, buf, i+1, 0);
  }
  else if (!strchr(buf, '\n'))
  {
    r = recv(s, ((char *)ircbuf)+cpos, j, 0);
    cpos += r;
    return(1);
  }
  else
  {
    for (i=0;buf[i]!='\n';i++);
    r = recv(s, ((char *)ircbuf)+cpos, i+1, 0);
    cpos = 0;
    strcpy(buf, ircbuf);
    memset(ircbuf, 0, sizeof(ircbuf));
  }
  
  if (buf[strlen(buf)-1] == '\n')
    buf[strlen(buf)-1] = 0;
  if (buf[strlen(buf)-1] == '\r')
    buf[strlen(buf)-1] = 0;
  
  if (nvar("debug") == 2)
  {
    wp(win, "<-- |%s|\n", buf);
    drw(win);
  }
  
  tok = form_tokso(buf, &cnt);
  
  if (*buf == ':')
    pt = 1;
  else
    pt = 0;
  
  for (i=0;;i++)
  {
    if (!handlers[i].text)
    {
      if (nvar("debug") == 1)
      {
        wp(win, "<-- |%s|\n", buf);
        drw(win);
      }
      for (i=0;tok[i];i++)
        free(tok[i]);
      free(tok);
      return(1);
    }
    if (!strcasecmp(tok[pt], handlers[i].text))
    {
      r = handlers[i].func(s, buf, tok, cnt, win);
      for (i=0;tok[i];i++)
        free(tok[i]);
      free(tok);
      t = findsock("input");
      if (t)
      {
	indraw();
      }
      dstatus();
      if (!r) {
	delsock(m->fd);
      }
      return(1);
    }
  }
  
  if (!r) {
    delsock(m->fd);
  }
  return(1);
}

char *gnick(char *in)
{
  int i;
  
  for (i=0;in[i]!='!';i++)
  {
    if (!in[i])
    {
      nbuf = strdup(in);
      return(nbuf);
    }
  }
  
  nbuf = (char *)malloc(i+1);
  memset(nbuf, 0, i+1);
  
  strncpy(nbuf, in, i);
  
  return(nbuf);
}

/* delete all irc channels (retain napster channels) */
void delircchans()
{
  chans_t *c;

  while (1) {
    list_find(c, chanl, c->q == 2);
    if (!c) {
      break;
    }
    delchan(c);
  }
}

/* delete all napster channels (retain irc channels) */
void delnapchans()
{
  chans_t *c;

  while (1) {
    list_find(c, chanl, c->q != 2);
    if (!c) {
      break;
    }
    delchan(c);
  }
}

int checkouts(int s, char *str, char **tok, int num, WINDOW *win)
{
  int i;
  
  for (i=0;;i++)
  {
    if (!outs[i].text)
      return(0);
    if (!strcasecmp(outs[i].text, tok[0]))
    {
      outs[i].func(s, str, tok, num, win);
      return(1);
    }
  }
}

I_IRC_FUNC(hping)
{
  ssock(s, "PONG %s\n", tok[1]);
  
  return(1);
}

I_IRC_FUNC(h433)
{
  if (!ircin && mnick && mnick[strlen(mnick)-1] != '_')
  {
    strcat(mnick, "_");
    ssock(s, "NICK %s\n", mnick);
    return(1);
  }
  
  if (!ircin)
  {
    if (mnick)
      free(mnick);
    mnick = NULL;
  }

  wp(win, "%s* Nickname is already taken, specify a new one%s\n", RED, WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h372)
{
  wp(win, "%s* %s%s\n", YELLOW, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+4, WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h353)
{
  int i, c, j;
  chans_t *cur;
  user_t *usr;
  
  cur = findchan(chanl, tok[4]);
  if (!cur)
    return(1);
  
  recent = cur;
  
  wp(win, "%s* Users %s:%s\n", YELLOW, cur->nm, WHITE);
  
  for (i=5,c=0;i<num;i++)
  {
    if (c == 4)
    {
      wp(win, "\n");
      c = 0;
    }
    if (!(*tok[i]))
      continue;
    if (i == 5)
    {
      c++;
      wp(win, "%s[%s%s", DARK, WHITE, tok[i]+1);
      for (j=16-strlen(tok[i]+1);j>=0;j--)
        wp(win, " ");
      wp(win, "%s]%s", DARK, WHITE);
      if (*(tok[i]+1) == '@' || *(tok[i]+1) == '+')
      {
        if (finduser(cur, tok[i]+2))
          continue;
        adduser(cur, tok[i]+2, 0, 0);
        usr = finduser(cur, tok[i]+2);
        if (*(tok[i]+1) == '@')
          usr->flag |= NAP_OP;
        else
          usr->flag |= NAP_VOICE;
      }
      else
      {
        if (finduser(cur, tok[i]+1))
          continue;
        adduser(cur, tok[i]+1, 0, 0);
      }
    }
    else
    {
      c++;
      wp(win, "%s[%s%s", DARK, WHITE, tok[i]);
      for (j=16-strlen(tok[i]);j>=0;j--)
        wp(win, " ");
      wp(win, "%s]%s", DARK, WHITE);
      if (*tok[i] == '@' || *tok[i] == '+')
      {
        if (finduser(cur, tok[i]+1))
          continue;
        adduser(cur, tok[i]+1, 0, 0);
        usr = finduser(cur, tok[i]+1);
        if (*tok[i] == '@')
          usr->flag |= NAP_OP;
        else
          usr->flag |= NAP_VOICE;
      }
      else
      {
        if (finduser(cur, tok[i]))
          continue;
        adduser(cur, tok[i], 0, 0);
      }
    }
  }
  
  if (c)
    wp(win, "\n");
  
  drw(win);
  
  recent = NULL;
  
  return(1);
}

I_IRC_FUNC(hjoin)
{
  chans_t *cur, *cur1=NULL;
  char *nk = gnick(tok[0]+1);
  
  if (strcasecmp(nk, mnick))
  {
    if (!(cur = findchan(chanl, tok[2]+1)))
    {
      free(nk);
      return(1);
    }
    adduser(cur, nk, 0, 0);
    finduser(cur, nk)->addr = strdup(tok[0]+strlen(nk)+2);
    recent = cur;
    wp(win, "%s* %s (%s%s%s) has joined %s%s\n", BRIGHT(GREEN), nk, WHITE, tok[0]+strlen(nk)+2, BRIGHT(GREEN), cur->nm, WHITE);
    drw(win);
    recent = NULL;
    free(nk);
    return(1);
  }
  
  if (!chanl)
  {
    chanl = (chans_t *)malloc(sizeof(chans_t));
    cur = chanl;
  }
  else
  {
    for (cur=chanl;cur!=NULL;cur=cur->next)
      cur1 = cur;
    cur = (chans_t *)malloc(sizeof(chans_t));
    cur1->next = cur;
  }
  
  cur->nm = strdup(tok[2]+1);
  cur->users = NULL;
  cur->q = 2;
  cur->key = 0;
  cur->l = 0;
  cur->flag = 0;
  cur->topic = NULL;
  cur->p = 0;
  cur->next = NULL;
  curchan = cur;
  
  ssock(s, "MODE %s\n", cur->nm);
  ssock(s, "WHO %s\n", cur->nm);
  nwho++;
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(hpart)
{
  chans_t *cur, *pt;
  char *nk = gnick(tok[0]+1);
  
  if (strcasecmp(nk, mnick))
  {
    if (!(cur = findchan(chanl, tok[2])))
    {
      free(nk);
      return(1);
    }
    deluser(cur, nk);
    recent = cur;
    wp(win, "%s* %s (%s%s%s) has left %s%s\n", GREEN, nk, WHITE, tok[0]+strlen(nk)+2, GREEN, cur->nm, WHITE);
    drw(win);
    recent = NULL;
    free(nk);
    return(1);
  }
  
  pt = findchan(chanl, tok[2]);
  
  wp(win, "%s* Left channel %s%s\n", GREEN, pt->nm, WHITE);
  drw(win);
  
  delchan(pt);
  
  drw(win);
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(hquit)
{
  chans_t *cur;
  char *nk = gnick(tok[0]+1);
  
  for (cur=chanl;cur;cur=cur->next)
  {
    if (finduser(cur, nk))
    {
      recent = cur;
      if (wmode)
        wp(win, "%s* %s (%s%s%s) has quit IRC (%s%s%s)%s\n", BRIGHT(BLUE), nk, WHITE, tok[0]+strlen(nk)+2, BRIGHT(BLUE), WHITE, str+strlen(tok[0])+strlen(tok[1])+3, BRIGHT(BLUE), WHITE);
      deluser(cur, nk);
      recent = NULL;
    }
  }
  
  if (!wmode)
    wp(win, "%s* %s (%s%s%s) has quit IRC (%s%s%s)%s\n", BRIGHT(BLUE), nk, WHITE, tok[0]+strlen(nk)+2, BRIGHT(BLUE), WHITE, str+strlen(tok[0])+strlen(tok[1])+3, BRIGHT(BLUE), WHITE);
  drw(win);
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(hnick)
{
  chans_t *cur;
  char *nk = gnick(tok[0]+1);
  user_t *usr;
  
  for (cur=chanl;cur;cur=cur->next)
  {
    if ((usr = finduser(cur, nk)))
    {
      recent = cur;
      if (wmode)
        wp(win, "%s* %s has changed his nick to %s%s\n", BRIGHT(YELLOW), nk, tok[2]+1, WHITE);
      free(usr->nm);
      usr->nm = strdup(tok[2]+1);
    }
  }
  
  if (!wmode)
    wp(win, "%s* %s has changed his nick to %s%s\n", BRIGHT(YELLOW), nk, tok[2]+1, WHITE);
  drw(win);
  
  if (!strcasecmp(nk, mnick))
  {
    free(mnick);
    mnick = strdup(tok[2]+1);
  }
  
  free(nk);
  
  return(1);
}
    
I_IRC_FUNC(h332)
{
  chans_t *cur = findchan(chanl, tok[3]);
  int i;
  
  if (!cur)
    return(1);
  
  cur->topic = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+5);
  for (i=0;cur->topic[i];i++)
  {
    if (cur->topic[i] < 26)
    {
      strncpy(cur->topic+i, cur->topic+i+1, strlen(cur->topic+i+1));
      cur->topic[strlen(cur->topic)-1] = 0;
    }
  }

  wp(win, "%s* Topic for %s: %s%s\n", YELLOW, cur->nm, cur->topic, WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h333)
{
  wp(win, "%s* Topic for %s set by %s%s\n", YELLOW, tok[3], tok[4], WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(htopic)
{
  chans_t *cur = findchan(chanl, tok[2]);
  char *nk = gnick(tok[0]+1);
  int i;
  
  if (!cur)
  {
    free(nk);
    return(1);
  }
  
  if (cur->topic)
    free(cur->topic);
  
  cur->topic = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+4);
  
  for (i=0;cur->topic[i];i++)
  {
    if (cur->topic[i] < 26)
    {
      strncpy(cur->topic+i, cur->topic+i+1, strlen(cur->topic+i+1));
      cur->topic[strlen(cur->topic)-1] = 0;
    }
  }
  
  if (!strlen(cur->topic))
  {
    free(cur->topic);
    cur->topic = NULL;
  }
  else
    cur->topic = (char *)realloc(cur->topic, strlen(cur->topic)+1);
  
  recent = cur;
  
  if (!cur->topic)
    wp(win, "%s* %s has unset the topic of %s%s\n", YELLOW, nk, cur->nm, WHITE);
  else
    wp(win, "%s* %s changed the topic of %s: %s%s\n", YELLOW, nk, cur->nm, cur->topic, WHITE);
  drw(win);
  
  if (curchan == recent)
    tind = 0;
  
  recent = NULL;
  free(nk);
  
  return(1);
}

I_IRC_FUNC(h311)
{
  wp(win, "%s _________________%s\n", DARK, WHITE);
  wp(win, "%s|%s User: %s\n", DARK, WHITE, tok[3]);
  wp(win, "%s|%s IRC name: %s\n", DARK, WHITE, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+strlen(tok[4])+strlen(tok[5])+strlen(tok[6])+8);
  wp(win, "%s|%s Address: %s@%s\n", DARK, WHITE, tok[4], tok[5]);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h319)
{
  wp(win, "%s|%s Channels: %s\n", DARK, WHITE, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+5);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h312)
{
  wp(win, "%s|%s Server: %s (%s)\n", DARK, WHITE, tok[4], str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+strlen(tok[4])+6);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h313)
{
  wp(win, "%s|%s Class: %s\n", DARK, WHITE, cstr(str, 4)+1);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h317)
{
  int hr, min, sec, t;
  
  t = atoi(tok[4]);
  
  min = t/60;
  hr = min/60;
  sec = t%60;
  
  while (min >= 60)
    min-=60;

  wp(win, "%s|%s Idle: %i h %i m %i s\n", DARK, WHITE, hr, min, sec);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(h367)
{
  time_t t;
  char *tm;
  
  t = strtoul(tok[6], (char **)NULL, 10);
  tm = ctime(&t);
  
  tm[strlen(tm)-1] = 0;

  wp(win, "%s* Banned: %s by %s at %s%s\n", BRIGHT(BLUE), tok[4], tok[5], tm, WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(hmode)
{
  int i, j, m=0, l;
  user_t *cur;
  chans_t *c = findchan(chanl, tok[2]);
  char *nk = gnick(tok[0]+1), *pt = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+3);
  
  if (pt[strlen(pt)-1] == ' ')
    pt[strlen(pt)-1] = 0;
  
  recent = c;
  
  if (!strcasecmp(tok[2], mnick))
    wp(win, "%s* %s sets mode [%s%s%s]%s\n", BRIGHT(YELLOW), nk, WHITE, pt+1, BRIGHT(YELLOW), WHITE);
  else
    wp(win, "%s* %s sets mode %s [%s%s%s]%s\n", BRIGHT(YELLOW), nk, tok[2], WHITE, pt, BRIGHT(YELLOW), WHITE);
  free(pt);
  
  drw(win);
  
  recent = NULL;
  
  if (!c)
  {
    free(nk);
    return(1);
  }
  
  l = strlen(tok[3]);

  for (i=0,j=4;i<l;i++)
  {
    if (tok[3][i] == '+')
      m = '+';
    else if (tok[3][i] == '-')
      m = '-';
    else if (tok[3][i] == 'o')
    {
      if (m == '+')
      {
        cur = finduser(c, tok[j]);
        if (cur)
          cur->flag |= NAP_OP;
      }
      else if (m == '-')
      {
        cur = finduser(c, tok[j]);
        if (cur)
          cur->flag &= ~NAP_OP;
      }
      j++;
    }
    else if (tok[3][i] == 'v')
    {
      if (m == '+')
      {
        cur = finduser(c, tok[j]);
        if (cur)
          cur->flag |= NAP_VOICE;
      }
      else if (m == '-')
      {
        cur = finduser(c, tok[j]);
        if (cur)
          cur->flag &= ~NAP_VOICE;
      }
      j++;
    }
    else if (tok[3][i] == 'b')
      j++;
    else if (tok[3][i] == 'i')
    {
      if (m == '+')
        c->flag |= NAP_I;
      else if (m == '-')
        c->flag &= ~NAP_I;
    }
    else if (tok[3][i] == 's')
    {
      if (m == '+')
        c->flag |= NAP_S;
      else if (m == '-')
        c->flag &= ~NAP_S;
    }
    else if (tok[3][i] == 'p')
    {
      if (m == '+')
        c->flag |= NAP_P;
      else if (m == '-')
        c->flag &= ~NAP_P;
    }
    else if (tok[3][i] == 't')
    {
      if (m == '+')
        c->flag |= NAP_T;
      else if (m == '-')
        c->flag &= ~NAP_T;
    }
    else if (tok[3][i] == 'm')
    {
      if (m == '+')
        c->flag |= NAP_M;
      else if (m == '-')
        c->flag &= ~NAP_M;
    }
    else if (tok[3][i] == 'n')
    {
      if (m == '+')
        c->flag |= NAP_N;
      else if (m == '-')
        c->flag &= ~NAP_N;
    }
    else if (tok[3][i] == 'k')
    {
      if (m == '+')
      {
        c->flag |= NAP_K;
        c->key = strdup(tok[j]);
      }
      else if (m == '-')
      {
        c->flag &= ~NAP_K;
        free(c->key);
        c->key = NULL;
      }
      j++;
    }
    else if (tok[3][i] == 'l')
    {
      if (m == '+')
      {
        c->flag |= NAP_L;
        c->l = atoi(tok[j]);
        j++;
      }
      else if (m == '-')
      {
        c->flag &= ~NAP_L;
        c->l = 0;
      }
    }
  }
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(h324)
{
  int i, j, m=0, l;
  chans_t *c = findchan(chanl, tok[3]);
  char *pt = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+4);
  
  if (pt[strlen(pt)-1] == ' ')
    pt[strlen(pt)-1] = 0;
  
  if (!c)
    return(1);
  
  l = strlen(tok[4]);

  for (i=0,j=5;i<l;i++)
  {
    if (tok[4][i] == '+')
      m = '+';
    else if (tok[4][i] == '-')
      m = '-';
    else if (tok[4][i] == 'i')
    {
      if (m == '+')
        c->flag |= NAP_I;
      else if (m == '-')
        c->flag &= ~NAP_I;
    }
    else if (tok[4][i] == 's')
    {
      if (m == '+')
        c->flag |= NAP_S;
      else if (m == '-')
        c->flag &= ~NAP_S;
    }
    else if (tok[4][i] == 'p')
    {
      if (m == '+')
        c->flag |= NAP_P;
      else if (m == '-')
        c->flag &= ~NAP_P;
    }
    else if (tok[4][i] == 't')
    {
      if (m == '+')
        c->flag |= NAP_T;
      else if (m == '-')
        c->flag &= ~NAP_T;
    }
    else if (tok[4][i] == 'm')
    {
      if (m == '+')
        c->flag |= NAP_M;
      else if (m == '-')
        c->flag &= ~NAP_M;
    }
    else if (tok[4][i] == 'n')
    {
      if (m == '+')
        c->flag |= NAP_N;
      else if (m == '-')
        c->flag &= ~NAP_N;
    }
    else if (tok[4][i] == 'k')
    {
      if (m == '+')
      {
        c->flag |= NAP_K;
        c->key = strdup(tok[j]);
      }
      else if (m == '-')
      {
        c->flag &= ~NAP_K;
        free(c->key);
        c->key = NULL;
      }
      j++;
    }
    else if (tok[4][i] == 'l')
    {
      if (m == '+')
      {
        c->flag |= NAP_L;
        c->l = atoi(tok[j]);
        j++;
      }
      else if (m == '-')
      {
        c->flag &= ~NAP_L;
        c->l = 0;
      }
    }
  }
  
  return(1);
}

I_IRC_FUNC(hkick)
{
  char *nk = gnick(tok[0]+1);
  chans_t *pt = findchan(chanl, tok[2]);
  
  recent = pt;
  
  wp(win, "%s* %s was kicked from %s by %s (%s%s%s)%s\n", GREEN, tok[3], tok[2], nk, WHITE, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+5, GREEN, WHITE);
  drw(win);
  
  recent = NULL;
  
  if (strcasecmp(tok[3], mnick) != 0)
  {
    deluser(pt, tok[3]);
    free(nk);
    return(1);
  }
  
  delchan(pt);
  
  drw(win);
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(hprivmsg)
{
  char *nk = gnick(tok[0]+1), *msg = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+4), *req, *pt, *nm=NULL;
  chans_t *cur = findchan(chanl, tok[2]);
  sock_t *sk;
  
  recent = cur;
  
  if (*msg != 1 && strcasecmp(tok[2], mnick))
  {
    if (curchan && (!strcasecmp(curchan->nm, tok[2]) || wmode))
      wp(win, "%s<%s%s%s>%s %s\n", BRIGHT(BLUE), WHITE, nk, BRIGHT(BLUE), WHITE, msg);
    else
      wp(win, "%s<%s%s%s:%s%s%s>%s %s\n", BLUE, WHITE, nk, DARK, WHITE, tok[2], BLUE, WHITE, msg);
    drw(win);
    recent = NULL;
    free(msg);
    free(nk);
    return(1);
  }
  else if (*msg != 1)
  {
    wp(win, "%s* [%s%s%s(%s%s%s)]%s %s\n", BRIGHT(GREEN), WHITE, nk, BRIGHT(GREEN), WHITE, tok[0]+strlen(nk)+2, BRIGHT(GREEN), WHITE, msg);
    drw(win);
    free(msg);
    free(nk);
    return(1);
  }
  
  if (msg[strlen(msg)-1] == 1)
    msg[strlen(msg)-1] = 0;
  
  if (!strcasecmp(tok[3]+2, "ACTION"))
  {
    pt = strdup(msg+strlen(tok[3]));
    if (pt[strlen(pt)-1] == 1)
      pt[strlen(pt)-1] = 0;
    if (!strcasecmp(curchan->nm, tok[2]) || wmode)
    {
      wp(win, "%s* %s%s %s\n", CYAN, nk, WHITE, pt);
      drw(win);
    }
    else
    {
      wp(win, "%s* %s%s/%s%s%s %s\n", CYAN, nk, WHITE, CYAN, tok[2], WHITE, pt);
      drw(win);
    }
    recent = NULL;
    free(pt);
    free(msg);
    free(nk);
    return(1);
  }
  else if (!strcasecmp(tok[3]+2, "DCC") && tok[4] && !strcasecmp(tok[4], "CHAT") && tok[5] && tok[6] && tok[7])
  {
    msprintf(&nm, "pending %s", nk);
    sk = addsock(-1, nm, 0, NULL);
    sk->dxx = NULL;
    msprintf(&sk->dxx, "%s %s", tok[6], tok[7]);
    wp(win, "%s* %s has requested a DCC chat%s\n", BRIGHT(CYAN), nk, WHITE);
    drw(win);
    free(nm);
    free(msg);
    free(nk);
    return(1);
  }
  
  req = strdup(tok[3]+2);
  if (req[strlen(req)-1] == 1)
    req[strlen(req)-1] = 0;
  
  wp(win, "%s* CTCP request %s from %s (%s) to %s%s\n", CYAN, req, nk, tok[0]+strlen(nk)+2, tok[2], WHITE);
  drw(win);
  
  if (!strcasecmp(req, "VERSION"))
    ssock(s, "NOTICE %s :\001VERSION nap v%s\001\n", nk, VERSION);
  else if (!strcasecmp(req, "PING"))
    ssock(s, "NOTICE %s :%s\001\n", nk, msg);
  
  recent = NULL;
  
  free(msg);
  free(req);
  free(nk);
  
  return(1);
}

I_IRC_FUNC(hnotice)
{
  char *nk = gnick(tok[0]+1), *msg = strdup(str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+4);
  chans_t *cur = findchan(chanl, tok[2]);
  
  if (!strcasecmp(tok[1], "AUTH") || !strcasecmp(tok[1], mnick))
  {
    wp(win, "%s%s%s\n", MAGENTA, str+strlen(tok[0])+strlen(tok[1])+3, WHITE);
    drw(win);
    free(msg);
    free(nk);
    return(1);
  }
  
  recent = cur;
  
  if (*msg != 1 && strcasecmp(tok[2], mnick))
  {
    if (curchan && (!strcasecmp(curchan->nm, tok[2]) || wmode))
      wp(win, "%s*%s*%s %s\n", MAGENTA, nk, WHITE, msg);
    else
      wp(win, "%s*%s%s/%s%s*%s %s\n", MAGENTA, nk, WHITE, MAGENTA, tok[2], WHITE, msg);
    drw(win);
    recent = NULL;
    free(msg);
    free(nk);
    return(1);
  }
  else if (*msg != 1)
  {
    wp(win, "%s* [%s%s%s(%s%s%s)]%s %s\n", MAGENTA, WHITE, nk, MAGENTA, WHITE, tok[0]+strlen(nk)+2, MAGENTA, WHITE, msg);
    drw(win);
    free(msg);
    free(nk);
    return(1);
  }
  
  if (msg[strlen(msg)-1] == 1)
    msg[strlen(msg)-1] = 0;
  
  wp(win, "%s* CTCP reply from %s (%s):%s %s\n", CYAN, nk, tok[0]+strlen(nk)+2, WHITE, msg+strlen(tok[3]));
  drw(win);
  
  recent = NULL;
  
  free(msg);
  free(nk);
  
  return(1);
}

I_IRC_FUNC(h376)
{
  ircin = 1;
  
  if (mnick)
    free(mnick);
  mnick = strdup(tok[2]);
  
  return(1);
}

I_IRC_FUNC(h352)
{
  chans_t *c = findchan(chanl, tok[3]);
  user_t *cur;
  
  if (!nwho)
  {
    recent = c;
    wp(win, "%s _________________%s\n", DARK, WHITE);
    wp(win, "%s|%s User: %s\n", DARK, WHITE, tok[7]);
    wp(win, "%s|%s IRC name: %s\n", DARK, WHITE, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+strlen(tok[4])+strlen(tok[5])+strlen(tok[6])+strlen(tok[7])+strlen(tok[8])+strlen(tok[9])+10);
    wp(win, "%s|%s Address: %s@%s\n", DARK, WHITE, tok[4], tok[5]);
    if (*tok[3] != '*')
    {
      if (tok[8][1] == '@')
        wp(win, "%s|%s Channel: @%s\n", DARK, WHITE, tok[3]);
      else if (tok[8][1] == '+')
        wp(win, "%s|%s Channel: +%s\n", DARK, WHITE, tok[3]);
      else
        wp(win, "%s|%s Channel: %s\n", DARK, WHITE, tok[3]);
    }
    wp(win, "%s|%s Server: %s\n", DARK, WHITE, tok[6]);
    drw(win);
    recent = NULL;
    return(1);
  }
  
  if (!c)
    return(1);
  
  cur = finduser(c, tok[7]);
  if (!cur)
    return(1);
  
  msprintf(&cur->addr, "%s@%s", tok[4], tok[5]);
  
  return(1);
}

I_IRC_FUNC(h314)
{
  chans_t *c = findchan(chanl, tok[3]);
  
  recent = c;
  
  wp(win, "%s _________________%s\n", DARK, WHITE);
  wp(win, "%s|%s User: %s\n", DARK, WHITE, tok[3]);
  wp(win, "%s|%s IRC name: %s\n", DARK, WHITE, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+strlen(tok[4])+strlen(tok[5])+strlen(tok[6])+8);
  wp(win, "%s|%s Address: %s@%s\n", DARK, WHITE, tok[4], tok[5]);
  drw(win);
  
  recent = NULL;
  
  return(1);
}

I_IRC_FUNC(h315)
{
  nwho--;
  
  return(1);
}

I_IRC_FUNC(h341)
{
  wp(win, "%s* Inviting %s to %s%s\n", BRIGHT(BLUE), tok[3], tok[4], WHITE);
  drw(win);
  
  return(1);
}

I_IRC_FUNC(hinvite)
{
  char *nk = gnick(tok[0]+1);
  
  wp(win, "%s* [%s%s%s] has invited you to %s%s\n", BRIGHT(BLUE), WHITE, nk, BRIGHT(BLUE), tok[3]+1, WHITE);
  drw(win);
  
  free(nk);
  
  return(1);
}

I_IRC_FUNC(herr)
{
  wp(win, "%s* %s%s\n", RED, str+strlen(tok[0])+strlen(tok[1])+strlen(tok[2])+strlen(tok[3])+5, WHITE);
  drw(win);
  
  return(1);
}

/* changed to not collide with netdb.h */
I_IRC_FUNC(herr2)
{
  wp(win, "%s* %s%s\n", RED, str+strlen(tok[0])+2, WHITE);
  drw(win);
  
  return(1);
}

    
O_IRC_FUNC(imsg)
{
  sock_t *sk;
  char *nm=NULL;

  if (num < 2)
  {
    wp(win, "%s* No nickname or channel given%s\n", RED, WHITE);
    drw(win);
    return;
  }
  if (num < 3)
  {
    wp(win, "%s* No message given%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  if (*tok[1] == '=')
  {
    msprintf(&nm, "chat %s", tok[1]+1);
    sk = findsock(nm);
    free(nm);
    if (!sk)
    {
      wp(win, "%s* No DCC connection to %s found%s\n", RED, tok[1]+1, WHITE);
      drw(win);
      return;
    }
    ssock(sk->fd, "%s\n", cstr(str, 2));
    wp(win, "%s* --> [%s(%s)%s]%s %s\n", GREEN, WHITE, tok[1]+1, GREEN, WHITE, cstr(str, 2));
    drw(win);
    return;
  }

  ssock(s, "PRIVMSG %s :%s\n", tok[1], cstr(str, 2));
  
  wp(win, "%s* --> (%s%s%s)%s %s\n", GREEN, WHITE, tok[1], GREEN, WHITE, cstr(str, 2));
  drw(win);
}

O_IRC_FUNC(inotice)
{
  if (num < 2)
  {
    wp(win, "%s* No nickname or channel given%s\n", RED, WHITE);
    drw(win);
    return;
  }
  if (num < 3)
  {
    wp(win, "%s* No message given%s\n", RED, WHITE);
    drw(win);
    return;
  }

  ssock(s, "NOTICE %s :%s\n", tok[1], cstr(str, 2));
  
  wp(win, "%s* --> (%s)%s %s\n", MAGENTA, tok[1], WHITE, cstr(str, 2));
  drw(win);
}

O_IRC_FUNC(iquit)
{
  if (num < 2)
    ssock(s, "QUIT :Leaving\n");
  else
    ssock(s, "QUIT :%s\n", cstr(str, 1));
}

O_IRC_FUNC(ikick)
{
  if (!curchan || curchan->q != 2)
  {
    wp(win, "%s* You are not on a channel%s\n", RED, WHITE);
    drw(win);
    return;
  }
  if (num < 2)
  {
    wp(win, "%s* No nickname given%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  ssock(s, "KICK %s %s :%s\n", curchan->nm, tok[1], cstr(str, 2));
}

O_IRC_FUNC(ime)
{
  if (!curchan || curchan->q == 1)
  {
    wp(win, "%s* You are not on a channel%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  ssock(s, "PRIVMSG %s :\001ACTION %s\001\n", curchan->nm, cstr(str, 1));
  
  recent = curchan;
  
  wp(win, "%s* %s%s %s\n", CYAN, mnick, WHITE, cstr(str, 1));
  drw(win);
  
  recent = NULL;
}

O_IRC_FUNC(inick)
{
  if (!ircin)
  {
    if (mnick)
      free(mnick);
    mnick = strdup(cstr(str, 1));
  }
  
  ssock(s, "%s\n", str);
}

O_IRC_FUNC(iirc)
{
  if (ircmode)
  {
    wp(win, "%s* IRC mode toggled: Off%s\n", RED, WHITE);
    drw(win);
    ircmode = 0;
  }
  else
  {
    wp(win, "%s* IRC mode toggled: On%s\n", RED, WHITE);
    drw(win);
    ircmode = 1;
  }
}

O_IRC_FUNC(iwhois)
{
  if (num < 2)
    ssock(s, "WHOIS %s\n", mnick);
  else
    ssock(s, "%s\n", str);
}

O_IRC_FUNC(itopic)
{
  chans_t *cur;

  if (num == 1)
  {
    if (!curchan || curchan->q != 2)
    {
      wp(win, "%s* Error: You're not on channel%s\n", RED, WHITE);
      drw(win);
      return;
    }
    if (curchan->q == 2)
    {
      ssock(s, "TOPIC %s\n", curchan->nm);
      return;
    }
    if (!curchan->topic)
      wp(win, "%s* No topic set%s\n", YELLOW, WHITE);
    else
      wp(win, "%s* Topic for %s: %s%s\n", YELLOW, curchan->nm, curchan->topic, WHITE);
    drw(win);
    return;
  }
  else if (num == 2)
  {
    cur = findchan(chanl, tok[1]);
    if (!cur || cur->q == 2)
    {
      ssock(s, "TOPIC %s\n", tok[1]);
      return;
    }
    if (!cur->topic)
      wp(win, "%s* No topic set%s\n", YELLOW, WHITE);
    else
      wp(win, "%s* Topic for %s: %s%s\n", YELLOW, cur->nm, cur->topic, WHITE);
    drw(win);
    return;
  }
      
  ssock(s, "TOPIC %s :%s\n", tok[1], cstr(str, 2));
  
  return;
}

O_IRC_FUNC(ipart)
{
  chans_t *pt;
  sock_t *sk;

  if (num == 1)
    pt = curchan;
  else
    pt = findchan(chanl, tok[1]);
  
  if (!pt && num > 1)
  {
    wp(win, "%s* You are not on %s channel %s\n", RED, tok[1], WHITE);
    drw(win);
    return;
  }
  else if (!pt)
  {
    wp(win, "%s* You are not on a channel %s\n", RED, WHITE);
    drw(win);
    return;
  }
    
  if (!pt->q)
  {
    sk = findsock("server");
    if (sk)
    {
      sendpack(sk->fd, NAP_PART, "%s", pt->nm);
      pt->p |= 1;
    }
    return;
  }
  else if (pt->q == 2)
  {
    ssock(s, "PART %s\n", pt->nm);
    pt->p |= 1;
    return;
  }

  wp(win, "%s* Ended query with %s%s\n", GREEN, pt->nm, WHITE);
  drw(win);
  
  delchan(pt);
  
  drw(win);
  
  return;
}

O_IRC_FUNC(iaddr)
{
  if (!curchan || num < 2 || !finduser(curchan, tok[1]))
    return;
  
  wp(win, "|%s|\n", finduser(curchan, tok[1])->addr);
  drw(win);
}

O_IRC_FUNC(iban)
{
  int i, c=0, j;
  user_t *cur;
  char rhost[256], *mask=NULL;
  
  if (!curchan || curchan->q != 2)
  {
    wp(win, "%s* You are not on a channel %s\n", RED, WHITE);
    drw(win);
    return;
  }
  if (num < 2)
  {
    wp(win, "%s* No nickname specified%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  cur = finduser(curchan, tok[1]);
  if (!cur)
  {
    wp(win, "%s* No such user on channel%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  memset(rhost, 0, sizeof(rhost));
  
  if (isdigit(cur->addr[strlen(cur->addr)-1]))
  {
    strcpy(rhost, cur->addr);
    i = 0;
    while (rhost[i] != '@')
      i++;
    i++;
    for (c=0;c!=2;i++)
      if (rhost[i] == '.')
        c++;
    rhost[i] = 0;
    if (*rhost == '~')
      msprintf(&mask, "*!*%s*", rhost+1);
    else
      msprintf(&mask, "*!*%s*", rhost);
  }
  else
  {
    for (i=strlen(cur->addr),c=0;c!=2;i--)
      if (cur->addr[i] == '.')
        c++;
    for (j=0;cur->addr[j]!='@';j++)
      rhost[j] = cur->addr[j];
    rhost[j] = 0;
    if (*rhost == '~')
      msprintf(&mask, "*!*%s@*%s", rhost+1, cur->addr+i+1);
    else
      msprintf(&mask, "*!*%s@*%s", rhost, cur->addr+i+1);
  }
  
  ssock(s, "MODE %s +b %s\n", curchan->nm, mask);
  free(mask);
}

O_IRC_FUNC(inap)
{
  int t = ircmode, ns, r;
  sock_t *sk;
  
  sk = findsock("server");
  if (!sk)
    ns = -1;
  else
    ns = sk->fd;
  
  ircmode = 0;
  lpbrk = 0;   /* clear previous loop interrupt request */
  r = parseout(ns, cstr(str, 1), win);
  ircmode = t;
}

O_IRC_FUNC(ictcp)
{
  if (num < 2)
  {
    wp(win, "%s* No nickname specified%s\n", RED, WHITE);
    drw(win);
    return;
  }
  else if (num < 3)
  {
    wp(win, "%s* No CTCP command specified%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  ssock(s, "PRIVMSG %s :\001%s\001\n", tok[1], cstr(str, 2));
  
  recent = curchan;
  wp(win, "%s* CTCP %s request to %s%s\n", CYAN, tok[2], tok[1], WHITE);
  drw(win);
  recent = NULL;
}

O_IRC_FUNC(iinvite)
{
  if (num < 2)
    return;
  else if (num < 3)
    ssock(s, "INVITE %s %s\n", tok[1], curchan->nm);
  else
    ssock(s, "INVITE %s %s\n", tok[1], tok[2]);
}  

O_IRC_FUNC(iopsay)
{
  user_t *usr;
  char buf[256];
  int c;

  if (num < 2)
    return;
  
  if (!curchan)
  {
    wp(win, "%s* You're not on a channel%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  memset(buf, 0, sizeof(buf));
  
  for (usr=curchan->users,c=0;;usr=usr->next)
  {
    if (!usr)
    {
      if (*buf)
      {
        buf[strlen(buf)-1] = 0;
        ssock(s, "NOTICE %s :%s\n", buf, cstr(str, 1));
      }
      break;
    }
    if (c == 4)
    {
      buf[strlen(buf)-1] = 0;
      ssock(s, "NOTICE %s :%s\n", buf, cstr(str, 1));
      memset(buf, 0, sizeof(buf));
      c = 0;
    }
    
    if (usr->flag & NAP_OP)
    {
      strcat(buf, usr->nm);
      strcat(buf, ",");
      c++;
    }
  }
}

O_IRC_FUNC(idcc)
{
  char *nm=NULL;
  struct sockaddr_in me, frm;
  int sock, port, frmlen = sizeof(me);
  sock_t *sk;

  if (num < 3)
  {
    wp(win, "%s* Insufficient parameters%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  if (!strcasecmp(tok[1], "close"))
  {
    msprintf(&nm, "chat %s", tok[2]);
    sk = findsock(nm);
    free(nm);
    nm = NULL;
    if (sk)
    {
      close(sk->fd);
      delsock(sk->fd);
      wp(win, "%s* Closed active DCC chat to %s%s\n", RED, tok[2], WHITE);
      drw(win);
      return;
    }
    msprintf(&nm, "pending %s", tok[2]);
    sk = findsock(nm);
    free(nm);
    nm = NULL;
    if (sk)
    {
      close(sk->fd);
      free(sk->dxx);
      sk->dxx = NULL;
      delsock(sk->fd);
      wp(win, "%s* Rejected pending DCC chat to %s%s\n", RED, tok[2], WHITE);
      drw(win);
      ssock(s, "NOTICE %s :\001DCC REJECT CHAT chat\001\n", tok[2]);
      return;
    }
    msprintf(&nm, "listen %s", tok[2]);
    sk = findsock(nm);
    free(nm);
    nm = NULL;
    if (sk)
    {
      close(sk->fd);
      delsock(sk->fd);
      wp(win, "%s* Closed waiting DCC chat to %s%s\n", RED, tok[2], WHITE);
      drw(win);
      return;
    }
    wp(win, "%s* Unable to find any DCC involving %s%s\n", RED, tok[2], WHITE);
    drw(win);
    return;
  }
  
  if (cport == -1)
    cport = 1024+getpid();
  
  port = cport++;
  
  me.sin_addr.s_addr = INADDR_ANY;
  me.sin_port = htons(port);
  me.sin_family = AF_INET;
  
  sock = socket(AF_INET, SOCK_STREAM, 0);
  
  if (bind(sock, (struct sockaddr *)&me, sizeof(me)) == -1)
  {
    wp(win, "%s* Error creating DCC socket%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  listen(sock, 0);
  
  msprintf(&nm, "listen %s", tok[2]);
  
  addsock(sock, nm, S_R, ldcc);
  free(nm);
  
  getsockname(s, (struct sockaddr *)&frm, &frmlen);
  
  ssock(s, "PRIVMSG %s :\001DCC CHAT chat %lu %i\001\n", tok[2], (long)ntohl(frm.sin_addr.s_addr), port);
}

O_IRC_FUNC(ichat)
{
  char *nm=NULL;
  sock_t *sk;
  unsigned long addr;
  int port, sock;
  struct sockaddr_in dst;
  
  if (num < 2)
  {
    wp(win, "%s* Need to specify a nickname%s\n", RED, WHITE);
    drw(win);
    return;
  }
  
  msprintf(&nm, "pending %s", tok[1]);
  sk = findsock(nm);
  free(nm);
  nm = NULL;
  if (!sk)
  {
    wp(win, "%s* No pending DCC to %s could be found%s\n", RED, tok[1], WHITE);
    drw(win);
    return;
  }
  
  sscanf(sk->dxx, "%lu %i", &addr, &port);
  sock = socket(AF_INET, SOCK_STREAM, 0);
  
  dst.sin_addr.s_addr = htonl(addr);
  dst.sin_port = htons(port);
  dst.sin_family = AF_INET;
  
  free(sk->dxx);
  sk->dxx = NULL;
  delsock(sk->fd);
  
  if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) == -1)
  {
    wp(win, "%s* Error creating DCC connection to %s%s\n", RED, tok[1], WHITE);
    drw(win);
    close(sock);
    return;
  }

  setkeepalive(sock);

  msprintf(&nm, "chat %s", tok[1]);
  addsock(sock, nm, S_R, dccfunc);
  
  wp(win, "%s* DCC Connection to %s established%s\n", BRIGHT(CYAN), tok[1], WHITE);
  drw(win);
  free(nm);
}  
