/* 
   users.c -- handles:
   testing and enforcing bans and ignores
   adding and removing bans and ignores
   listing bans and ignores
   auto-linking bots
   sending and receiving a userfile from a bot
   listing users ('.whois' and '.match')
   reading the user file

   dprintf'ized, 9nov95
 */
/*
   This file is part of the eggdrop source code
   copyright (c) 1997 Robey Pointer
   and is distributed according to the GNU general public license.
   For full details, read the top of 'main.c' or the file called
   COPYING that was distributed with this code.
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "eggdrop.h"
#include "users.h"
#include "chan.h"
#include "proto.h"
#ifdef MODULES
#include "modules.h"
#endif
#ifdef HAVE_NAT
char natip[121] = "";
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
/* 
   bans:
   <banmask>:<expire-time>:[+<time-added>:<last-active>]:<user>:<encoded-desc>
   expire-time: timestamp when the ban was made, or 0 for permanent
   (if it starts with '+': when the ban will expire)
   (if it ends with '*', it's "sticky" -- not dynamic)
   time-added: when the ban was first created
   last-active: last time ban was enforced
   user: who placed the ban

   ignores:
   <ignoremask>:+<expire-time>:<user>[:<time-added>:<encoded-desc>]
   time-added: when the ignore was created
   user: who placed the ignore
 */


extern char botname[];
extern char botuser[];
extern char botuserhost[];
extern int serv;
extern struct dcc_t dcc[];
extern int dcc_total;
extern int noshare;
extern struct userrec *userlist, *lastuser, *banu, *ignu;
extern char origbotname[];
extern char botnetnick[];
extern struct chanset_t *chanset;

/* where the user records are stored */
char userfile[121] = "";
/* how many minutes will bans last? */
int ban_time = 60;
/* how many minutes will ignores last? */
int ignore_time = 10;
/* Total number of global bans */
int gban_total = 0;


/* is this nick!user@host being ignored? */
int match_ignore PROTO1(char *, uhost)
{
   struct userrec *u;
   struct eggqueue *q;
   char host[UHOSTLEN], s[161];
   u = get_user_by_handle(userlist, IGNORE_NAME);
   if (u == NULL)
      return 0;
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if (wild_match(host, uhost))
	 return 1;
      q = q->next;
   }
   return 0;
}

/* is this ban sticky? */
int u_sticky_ban PROTO2(struct userrec *, u, char *, uhost)
{
   struct eggqueue *q;
   char host[UHOSTLEN], s[256];
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if (strcasecmp(host, uhost) == 0) {
	 splitc(host, s, ':');
	 if (strchr(host, '*') == NULL)
	    return 0;
	 else
	    return 1;
      }
      q = q->next;
   }
   return 0;
}

/* set sticky attribute for a ban */
int u_setsticky_ban PROTO3(struct userrec *, u, char *, uhost, int, sticky)
{
   struct eggqueue *q;
   char host[UHOSTLEN], s[256], s1[256], *p;
   int j, k;
   j = k = atoi(uhost);
   if (!j)
      j = (-1);
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if ((j >= 0) && (strcmp(q->item, "none") != 0))
	 j--;
      if ((j == 0) || (strcasecmp(host, uhost) == 0)) {
	 strcpy(uhost, host);
	 splitc(host, s, ':');
	 p = strchr(host, '*');
	 if ((p == NULL) && (sticky))
	    strcat(host, "*");
	 if ((p != NULL) && (!sticky))
	    strcpy(p, p + 1);
	 sprintf(s1, "%s:%s:%s", uhost, host, s);
	 chg_q(q, s1);
	 if (!noshare) {
	    if (strcasecmp(u->handle, BAN_NAME) == 0)
	       shareout("stick %s %c\n", uhost, sticky);
	    else {
	       struct chanset_t *cst = findchan(u->info);
	       if (cst->stat & CHAN_SHARED)
		  shareout("stick %s %c %s\n", uhost, sticky, u->info);
	    }
	 }
	 return 1;
      }
      q = q->next;
   }
   if (j >= 0)
      return j - k;
   else
      return 0;
}

/* returns 1 if temporary ban, 2 if permban, 0 if not a ban at all */
int u_equals_ban PROTO2(struct userrec *, u, char *, uhost)
{
   struct eggqueue *q;
   char host[UHOSTLEN], s[256], *p;
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if (strcasecmp(host, uhost) == 0) {
	 p = s;
	 if (*p == '+')
	    p++;
	 if (atoi(p) == 0)
	    return 2;
	 else
	    return 1;
      }
      q = q->next;
   }
   return 0;			/* not equal */
}

int sticky_ban PROTO1(char *, uhost)
{
   struct userrec *u;
   u = get_user_by_handle(userlist, BAN_NAME);
   if (u == NULL)
      return 0;
   return u_sticky_ban(u, uhost);
}

int setsticky_ban PROTO2(char *, uhost, int, par)
{
   struct userrec *u;
   u = get_user_by_handle(userlist, BAN_NAME);
   if (u == NULL)
      return 0;
   return u_setsticky_ban(u, uhost, par);
}

int equals_ban PROTO1(char *, uhost)
{
   struct userrec *u;
   u = get_user_by_handle(userlist, BAN_NAME);
   if (u == NULL)
      return 0;
   return u_equals_ban(u, uhost);
}

int equals_ignore PROTO1(char *, uhost)
{
   struct userrec *u;
   struct eggqueue *q;
   char host[UHOSTLEN], s[256];
   u = get_user_by_handle(userlist, IGNORE_NAME);
   if (u == NULL)
      return 0;
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if (strcasecmp(host, uhost) == 0) {
	 if (s[0] == '0')
	    return 1;
	 else
	    return 2;
      }
      q = q->next;
   }
   return 0;			/* not equal */
}

int u_match_ban PROTO2(struct userrec *, u, char *, uhost)
{
   struct eggqueue *q;
   char host[UHOSTLEN], s[256];
   q = u->host;
   while (q != NULL) {
      strcpy(s, q->item);
      splitc(host, s, ':');
      if (wild_match(host, uhost))
	 return 1;
      q = q->next;
   }
   return 0;
}

int match_ban PROTO1(char *, uhost)
{
   struct userrec *u;
   u = get_user_by_handle(userlist, BAN_NAME);
   if (u == NULL)
      return 0;
   return u_match_ban(u, uhost);
}

/* if any bans match this wildcard expression, refresh them on the channel */
void refresh_ban_kick PROTO3(struct chanset_t *, chan, char *, user, char *, nick)
{
   struct userrec *u;
   struct eggqueue *q;
   char host[UHOSTLEN], s[256], ts[21], s1[161], *p, new_expire = 0;
   time_t expire_time, time_added = (time_t) 0L, last_active = (time_t) 0L;
   int cycle = 0, sticky = 0;
   u = get_user_by_handle(userlist, BAN_NAME);
   while (u != NULL) {
      q = u->host;
      while ((q != NULL) && (strcmp(q->item, "none") != 0)) {
	 strcpy(s, q->item);
	 splitc(host, s, ':');
	 if (wild_match(host, user)) {
	    /* if this ban was placed in the last 60 seconds, it may not have */
	    /* propagated yet -- or it could be a desync, which can't be solved */
	    /* from here. :(  */
	    if (q->stamp < time(NULL) - 60) {
	       if (member_op(chan->name, nick))
		  add_mode(chan, '-', 'o', nick);	/* guess it can't hurt */
	       add_mode(chan, '+', 'b', host);
	       flush_mode(chan, QUICK);		/* do it IMMEDIATELY */
	       splitc(ts, s, ':');
	       if (ts[0] == '+') {
		  strcpy(ts, &ts[1]);
		  new_expire = 1;
		  if (strchr(ts, '*') != NULL)
		     sticky = 1;
	       }
	       expire_time = (time_t) atol(ts);
	       if (s[0] == '+') {
		  /* strip off new timestamps */
		  strcpy(s, &s[1]);
		  splitc(ts, s, ':');
		  time_added = (time_t) atol(ts);
		  splitc(ts, s, ':');
		  last_active = (time_t) atol(ts);
		  /* (update last-active timestamp) */
		  sprintf(s1, "%s:%s%lu%s:+%lu:%lu:%s", host, new_expire ? "+" : "",
			  expire_time, sticky ? "*" : "", time_added, time(NULL), s);
		  chg_q(q, s1);
	       }
	       /* split off nick */
	       splitc(s1, s, ':');
	       if (s[0] && (s[0] != '@')) {
		  /* ban reason stored */
		  p = strchr(s, '~');
		  while (p != NULL) {
		     *p = ' ';
		     p = strchr(s, '~');
		  }
		  p = strchr(s, '`');
		  while (p != NULL) {
		     *p = ',';
		     p = strchr(s, '`');
		  }
		  mprintf(serv, "KICK %s %s :banned: %s\n", chan->name, nick, s);
	       } else
		  mprintf(serv, "KICK %s %s :You are banned.\n", chan->name, nick);
	    }
	 }
	 q = q->next;
      }
      cycle++;
      if (cycle == 1)
	 u = chan->bans;
      else
	 u = NULL;
   }
}

int u_delban PROTO2(struct userrec *, u, char *, who)
{
   int i, j;
   struct eggqueue *q;
   char s[256], host[UHOSTLEN];
   struct chanset_t *cst;
   i = 0;
   if (atoi(who)) {
      j = atoi(who);
      q = u->host;
      while ((j > 0) && (q != NULL)) {
	 if (strcmp(q->item, "none") != 0)
	    j--;
	 if (j > 0)
	    q = q->next;
      }
      if (q != NULL) {
	 strcpy(s, q->item);
	 splitc(who, s, ':');
	 if (!who[0])
	    strcpy(who, s);
	 u->host = del_q(q->item, u->host, &i);
      } else
	 return j - atoi(who);
   } else {
      /* find matching host, if there is one */
      q = u->host;
      while ((q != NULL) && (!i)) {
	 strcpy(s, q->item);
	 splitc(host, s, ':');
	 if (!host[0])
	    strcpy(host, s);
	 if (strcasecmp(who, host) == 0)
	    u->host = del_q(q->item, u->host, &i);
	 q = q->next;
      }
   }
   if (i) {
      if (!noshare) {
	 /* distribute chan bans differently */
	 if (strcasecmp(u->handle, BAN_NAME) == 0)
	    shareout("-ban %s\n", who);
	 else {
	    cst = findchan(u->info);
	    if (cst->stat & CHAN_SHARED)
	       shareout("-banchan %s %s\n", u->info, who);
	 }
      }
   }
   return i;
}

int delban PROTO1(char *, who)
{
   struct userrec *u;
   int i;
   u = get_user_by_handle(userlist, BAN_NAME);
   if (u == NULL)
      return 0;
   i = u_delban(u, who);
   if (i > 0)
      gban_total--;
   if (u->host == NULL)
      deluser(BAN_NAME);
   return i;
}

int delignore PROTO1(char *, ign)
{
   struct userrec *u;
   int i, j;
   struct eggqueue *q;
   char s[161], host[UHOSTLEN];
   context;
   u = get_user_by_handle(userlist, IGNORE_NAME);
   i = 0;
   if (u == NULL)
      return 0;
   if (atoi(ign)) {
      j = atoi(ign) - 1;
      q = u->host;
      while (j > 0) {
	 if (q != NULL)
	    q = q->next;
	 j--;
      }
      if (q != NULL) {
	 strcpy(s, q->item);
	 splitc(ign, s, ':');
	 u->host = del_q(q->item, u->host, &i);
      }
   } else {
      /* find the matching host, if there is one */
      q = u->host;
      while ((q != NULL) && (!i)) {
	 strcpy(s, q->item);
	 splitc(host, s, ':');
	 context;
	 if (strcasecmp(ign, host) == 0)
	    u->host = del_q(q->item, u->host, &i);
	 q = q->next;
      }
   }
   if (i) {
      if (u->host == NULL)
	 deluser(IGNORE_NAME);
      if (!noshare)
	 shareout("-ignore %s\n", ign);
   }
   return i;
}

/* new method of creating bans */
/* if first char of note is '*' it's a sticky ban */
int u_addban PROTO5(struct userrec *, u, char *, ban, char *, from, char *, note,
		     time_t, expire_time)
{
   char s[UHOSTLEN], host[UHOSTLEN], *p, oldnote[256];
   time_t t, now = time(NULL);
   int sticky = 0;
   struct chanset_t *cst;
   strcpy(host, ban);
   /* choke check: fix broken bans (must have '!' and '@') */
   if ((strchr(host, '!') == NULL) && (strchr(host, '@') == NULL))
      strcat(host, "!*@*");
   else if (strchr(host, '@') == NULL)
      strcat(host, "@*");
   else if (strchr(host, '!') == NULL) {
      p = strchr(host, '@');
      strcpy(s, p);
      *p = 0;
      strcat(host, "!*");
      strcat(host, s);
   }
   sprintf(s, "%s!%s", botname, botuserhost);
   if (wild_match(host, s)) {
      putlog(LOG_MISC, "*", "Wanted to ban myself: deflected.");
      return 0;
   }
   if (u_equals_ban(u, host))
      u_delban(u, host);	/* remove old ban */
   /* it shouldn't expire and be sticky also */
   if (expire_time != 0L && note[0] == '*')
      strcpy(note, &note[1]);
   /* new format: */
   sprintf(s, "%s:+%lu%s:+%lu:%lu:%s:", host, expire_time, note[0] == '*' ? "*" : "",
	   now, now, from);
   if (note[0] == '*') {
      strcpy(note, &note[1]);
      sticky = 1;
   }
   if (note[0]) {
      strcpy(oldnote, note);
      /* remove spaces & commas */
      p = strchr(note, ' ');
      while (p != NULL) {
	 *p = '~';
	 p = strchr(note, ' ');
      }
      p = strchr(note, ',');
      while (p != NULL) {
	 *p = '`';
	 p = strchr(note, ',');
      }
      strcat(s, note);
   } else
      oldnote[0] = 0;
   t = 0L;
   if (expire_time != 0L) {
      t = (expire_time - now);
      if (t == 0)
	 t = 1;
   }
   u->host = add_q(s, u->host);
   if (!noshare) {
      if (sticky) {
	 strcpy(&note[1],