/*
 * cleanupdb.c
 * (C) Christian Magnusson 2000, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "settings.h"
#include "bottypes.h"
#include "globals.h"
#include "dl.h"

void m_cleanupdb(char *a);

MODULE_PUBLIC int _init_cleanupdb(void *a, void *b)
{
  dl_command_entry_t *dl = a;
  if(!a) return 0;

  snprintf(dl->help, MAX_COMMAND_HELP_LEN,
	   "/msg %s CLEANUPDB\nCleanup user-database and delete old users.\n"\
	   "Old, unused and empty channels will also be deleted. A channel\n"\
	   "without any operators for 40 days will be deleted."
	   , BOT_NAME);
  snprintf(dl->summary, MAX_COMMAND_SUMMARY_LEN,
	   "Cleanup user/channel-database");
  dl->level = 999;
#if 0
  dl->func = m_cleanupdb;
#else
  dl->func = (void (*)(char*)) DL_sym(dl->val, "m_cleanupdb");
#endif
  return 1;
}

MODULE_PUBLIC int _remove_cleanupdb(char *a, char *b)
{
  remove_command("cleanupdb");
  return 1;
}

MODULE_PUBLIC void m_cleanupdb(char *a)
{
  int i;
  ListElem *el, *el2;
  struct chanpriv *p;
  struct chanopts *c;
  struct dbaseuser *u;
  struct chanpriv *cp;
  char channel[CHANNELLEN+1];
  char tmp[100];
  int chan_stat1, chan_stat2, chan_stat3, chan_stat4;
  int no_owner;
  int nr = 0, chan_deleted = 0;
  int chan_deleted_old = 0;
  long from_age = 0L;
  long to_age = 0L;
  time_t tnow;
  char delete_it = 0;

  struct dbaseuser *db;
  db = sender_au->dbuser;
  if(db) {
    log("From: %s cleanupdb [%s]\n", db->nick, a);
  } else
    return;
  if(!is_oper()) {
    say_sender("You are not an Oper.");
    log("To: %s Is not an Oper\n", db->nick);
    return;
  }

  i=0;
  /*
    This function MUST be fixed and clean up everything from
    all channels, and in all lists.
  */
  from_age = 0L;
  to_age = 40*3600*24;
  tnow = time(NULL);
  tnow -= to_age;
  to_age = tnow;

  el = GetHead(dbuser_list);
  while(el) {
    u = (struct dbaseuser *)el->data;
    if(from_age && to_age) {
      if(u->last_auth>from_age && u->last_auth<to_age) {
	delete_it = 1;
      }
    } else if(from_age) {
      if(u->last_auth>from_age) {
	delete_it = 1;
      }
    } else if(to_age) {
      if(u->last_auth<to_age) {
	delete_it = 1;
      }
    }
    if(delete_it) {
      nr++;
      delete_it = 0;
      p = u->privs;
      while(p) {
	strncpy(channel, p->channel, CHANNELLEN);
	channel[CHANNELLEN] = '\0';
	u->privs = p->next;
	Free(p);
	p = u->privs;
	/* eventually remove the channel if last user on it */
	if(is_empty_channel(channel)) {
	  chan_deleted++;
	  sprintf(tmp, "Delete [%s]\n", channel);
	  say_sender( tmp);
	  log(tmp);
	  delchannel(channel);
	}
      }

      /*
	find the successor here, since it will be deallocated later
      */
      el2 = GetSucc(el);
      log("remove [%s]\n", u->nick);
      if(u->h_elem_nick) { 
	Remove(u->h_elem_nick);
	FreeElem(u->h_elem_nick);
	u->h_elem_nick = NULL;
      }
      if(u->h_elem_lastyxx) {
	Remove(u->h_elem_lastyxx);  /* this cause coredump ? */
	FreeElem(u->h_elem_lastyxx);
	u->h_elem_lastyxx = NULL;
      }
      if(u->h_elem_userid) { 
	Remove(u->h_elem_userid);
	FreeElem(u->h_elem_userid);
	u->h_elem_userid = NULL;
      }
      /*
	Last of all... remove it from the list, and the the whole object
	will be de-allocated.
      */
      Remove(u->l_elem);
      FreeElem(u->l_elem);
      /*
	next object is el2
      */
      el = el2;
    } else {
      el = GetSucc(el);
    }
  }

  tnow = time(NULL);
  el = GetHead(channel_list);
  while(el) {
    c = (struct chanopts *)el->data;
    el = GetSucc(el);
    if(c->last_joined && ((tnow - 40*3600*24) > c->last_joined)) {
      sprintf(tmp, "Delete [%s] (old)\n", c->channel);
      say_sender( tmp);
      log(tmp);
      delchannel(c->channel);
      chan_deleted_old++;
    }
  }

  sprintf(tmp, "%d users deleted. (older than 40 days)", nr);
  say_sender( tmp);
  sprintf(tmp, "%d channels removed (empty)", chan_deleted);
  say_sender( tmp);
  sprintf(tmp, "%d channels removed (old)", chan_deleted_old);
  say_sender( tmp);

  nr = 0;
  chan_stat1 = 0;
  chan_stat2 = 0;
  chan_stat3 = 0;
  chan_stat4 = 0;


  el = GetHead(channel_list);
  while(el) {
    c = (struct chanopts *)el->data;
    c->nr_members = 0;
    el = GetSucc(el);
  }

  el2 = GetHead(dbuser_list);
  while(el2) {
    cp = ((struct dbaseuser *)el2->data)->privs;
    p = NULL;
    while(cp) {
      c = findchannel(cp->channel);
      if(c) {
	c->nr_members++;
	p = cp;
	cp = cp->next;
      } else {
	log("Can't find: [%s]\n", cp->channel);
	/* for some reason there could be some ghost channels */
	if(p) {
	  p->next = cp->next;
	  Free(cp);
	  cp = p->next;
	} else {
	  ((struct dbaseuser *)el2->data)->privs = cp->next;
	  Free(cp);
	  cp = ((struct dbaseuser *)el2->data)->privs;
	}
      }
    }
    el2 = GetSucc(el2);
  }

  no_owner = 0;
  el = GetHead(channel_list);
  while(el) {
    c = (struct chanopts *)el->data;
    u = finduserfromid(c->owner);
    if(!u) {
      no_owner++;
    }
    if(c->nr_members <= 1) {
      chan_stat1++;
    } else if(c->nr_members <= 2) {
      chan_stat2++;
    } else if(c->nr_members <= 3) {
      chan_stat3++;
    } else if(c->nr_members <= 4) {
      chan_stat4++;
    }
    el = GetSucc(el);
  }

  sprintf(tmp, "%d channels without primary owner", no_owner);
  say_sender( tmp);
  sprintf(tmp, "%d channels with <=4 users.", chan_stat4);
  say_sender( tmp);
  sprintf(tmp, "%d channels with <=3 users.", chan_stat3);
  say_sender( tmp);
  sprintf(tmp, "%d channels with <=2 users.", chan_stat2);
  say_sender( tmp);
  sprintf(tmp, "%d channels with <=1 users.", chan_stat1);
  say_sender( tmp);

#ifdef DMALLOC
  say_sender( "Dump Malloc stats to disk.");
  Malloc_stat();
#endif
  say_sender( "Ready.");
  return;
}
