#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "base_includes.h"
#include "on.h"
#include "ignore.h"
#include "mycompare.h"
#include "level.h"
#include "system.h"

/* ----------------------------------------------------------------------- */
/* If nick is set, thing is a nick. Type is what should be ignored. Negate */
/* is what should *not* be ignored                                         */
/* ----------------------------------------------------------------------- */

typedef struct ignore_list *iptr;
struct ignore_list {
  char *thing;
  int nick, negate, ignore;
  iptr next;
};

/* List of all ignores */

static iptr master_ignore = NULL;
extern server_ptr gsrv;

/* ----------------------------------------------------------------------- */
/* Check to see if a "type" of server message is being ignored             */
/* Scan the ignore list and compare the nick (or) userhost to entries      */
/* in the list. If we find a match, see if it is an ignore.. if it is,     */
/* return FALSE. If it is not, return TRUE;                                */
/* ----------------------------------------------------------------------- */

int ignore(char *nick, char *userhost, int type)
{
  iptr temp, match;
  int comp, biggest, negate, igg;
  char msg[MAXLEN];

  temp = master_ignore;
  match = NULL;
  igg = FALSE;
  biggest = -1;
  while (temp!=NULL)
  {
    if (temp->nick)
      comp = wild_match(temp->thing, nick);
    else
      comp = wild_match(temp->thing, userhost);
    if (comp>biggest)
    {
      match = temp;
      biggest = comp;
    }
    if (comp>-1)
      if (type & temp->negate) return FALSE;
    temp = temp->next;
  }

/* If nothing matched, we are not ignoreing */
  if (!match) return FALSE;

/* Else, check to see it this type of message is being ignored */
  igg = (type & match->ignore);
  return igg;
}

static struct command_node lastlog_levels[] =
{
  {{"NONE"},     (char *)NONE},
  {{"PUBLIC"},   (char *)PUBLIC},
  {{"MSGS"},     (char *)MSGS},
  {{"NOTICE"},   (char *)NOTICES},
  {{"WALL"},     (char *)WALL},
  {{"WALLOP"},   (char *)WALLOP},
  {{"ACTIONS"},  (char *)ACTIONS},
  {{"DCC"},      (char *)DCC},
  {{"CRAP"},     (char *)CRAP},
  {{"ALL"},      (char *)ALL},
  {{ NULL},      (char *)NULL}
};

static void show_ignore(user)
  char *user;
{
  iptr temp;

  say("*** Ignorance List:");
  for (temp=master_ignore; temp; temp=temp->next)
    if (strncasecmp(user, temp->thing, strlen(user)) == 0)
      show_level(temp->thing, temp->ignore, temp->negate, NULL);
  say("*** End of Ignorance List.");
}

static void add_ignore(user, rest)
  char *user, *rest;
{
  int ignore = NONE, negate = NONE, loop, cmp;
  char type[MAXLEN], msg[MAXLEN];
  iptr front, back = NULL, walk;

  front = master_ignore;
  while (front && ((cmp = strcasecmp(front->thing, user)) == -1))
  {
    back = front;
    front = front->next;
  }
  if (front && (cmp == 0)) {
    ignore = front->ignore;
    negate = front->negate;
    if (!back) master_ignore = front->next;
    else back->next = front->next;
    myfree(front->thing);
    myfree(front);
  }
  if (!lookup_level(rest, &ignore, &negate)) {
    say("*** You must specify one of the following:");
    (void)sprintf(msg, "***     ");
    for (loop=0;lastlog_levels[loop].command;loop++)
    {
      (void)strcat(msg, lastlog_levels[loop].command);
      (void)strcat(msg, "  ");
    }
    say("%s", msg);
  } else if ((ignore==NONE) && (negate==NONE)) {
    say("*** %s removed from ignorance list", user);
  } else {
    if ((front = (iptr)mymalloc(sizeof(struct ignore_list))) == NULL)
    {
      say("Could not malloc for ignore!");
      return;
    }
    walk = master_ignore;
    back = NULL;
    while (walk && (strcasecmp(walk->thing, user) < 0)) {
      back = walk;
      walk = walk->next;
    }
    front->next = walk;
    if (!back) master_ignore = front;
    else back->next = front;
    front->ignore = ignore;
    front->negate = negate;
    front->thing = mymalloc(strlen(user) + 1);
    (void)strcpy(front->thing, user);
    if (strstr(user, "@") == NULL) front->nick = TRUE;
    else front->nick = FALSE;
    show_ignore(user);
  }
}

void user_ignore(char *string, int varpar, char *subparams)
{
  char user[MAXLEN];

  (void)grab_word(&string, ' ', user);
  if (*string) add_ignore(user, string);
  else show_ignore(user);
}
/* ----------------------------------------------------------------------- */
/* ----------------------------------------------------------------------- */
typedef struct {
  char *uhost;
  int start;
  int count, hcount;
} flood;
static flood *flooders = NULL;

/* ----------------------------------------------------------------------- */
int check_flood(char *nick, char *uhost, char *type)
{
  static int max_users = 0;
  int loop, blank = -1, diff, uh = 0, curr, rate;
  char *host, *end = NULL, line[80];
  flood *node;

  if (getivar(VAR_FLOOD_USERS) != max_users)
  {
    for (loop=0;loop<max_users;loop++) myfree(flooders[loop].uhost);
    myfree(flooders);
    max_users = getivar(VAR_FLOOD_USERS);
    flooders = (flood *)mymalloc(sizeof(flood) * max_users);
    memset(flooders, 0, sizeof(flood) * max_users);
  }
  if (!max_users) return 0;
  host = (char *)strstr(uhost, "@");
  curr = (int)time(0);
  for (loop=0;loop<max_users;loop++)
    if (flooders[loop].uhost && host && strstr(flooders[loop].uhost, host))
      flooders[loop].hcount++;
  for (loop=0;loop<max_users;loop++)
    if (flooders[loop].uhost)
    {
      if ((curr-flooders[loop].start) <= (getivar(VAR_FLOOD_RATE)*2)) {
        if (strcasecmp(uhost, flooders[loop].uhost) == 0) break;
      } else {
        myfree(flooders[loop].uhost);
        flooders[loop].uhost = NULL;
        blank = loop;
      }
    } else blank = loop;
  if (loop==max_users) {
    if (blank==-1) node = &flooders[max_users-1];
    else node = &flooders[blank];
    myfree(node->uhost);
    node->uhost = m_strcpy(uhost);
    node->count = node->hcount = 1;
    node->start = curr;
    return 0;
  }
  node = &flooders[loop];
  if (++node->count >= getivar(VAR_FLOOD_AFTER)) {
    diff = curr - node->start;
    rate = (diff)?node->count/diff:0;
    if (rate >= getivar(VAR_FLOOD_RATE)) end = uhost;
  }
  if (!end)
    for (loop=0; host && (loop<max_users); loop++)
      if (flooders[loop].uhost && strstr(flooders[loop].uhost, host))
        if (flooders[loop].hcount >= getivar(VAR_FLOOD_AFTER)) {
          diff = curr - flooders[loop].start;
          rate = (diff)?flooders[loop].hcount/diff:0;
          if (rate >= getivar(VAR_FLOOD_RATE)) {
            end = host;
            break;
          }
        }
  if (!end) return 0;
  myfree(flooders[loop].uhost);
  flooders[loop].uhost = NULL;
  if (getbvar(VAR_FLOOD_WARNING)) 
    say("*** %s flooding detected from %s (%s)", type, nick, end);
  sprintf(line, "*%s %s", end, type);
  if (handle_on(o_flood, line, 0)) return 1;
  return 0;
}
