#ifndef __lint
static const char rcsid[] = "@(#) $Header: killdup.c,v 1.28 94/10/06 16:15:41 deyke Exp $";
#endif

#include <sys/types.h>

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#ifdef ibm032
#include <sys/file.h>
#define SEEK_CUR L_INCR
#define SEEK_END L_XTND
#endif

#include "bbs.h"
#include "md5.h"

#define MAGIC           19940218
#define SAVETIME        (7*24*60*60)
#define SIZE_DIGEST     16
#define SIZE_HASH       4096
#define VERSION         1

struct header {
  int magic;
  int version;
  int nummesg;
  int lastmesg;
};

struct vitals {
  int mesg;
  unsigned char digest[SIZE_DIGEST];
};

static const unsigned char null_digest[SIZE_DIGEST] = {
  0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
  0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
};

static FILE *fpbbs;
static const char datafile[] = "killdup_data";
static const char tempfile[] = "killdup_temp";

/*---------------------------------------------------------------------------*/

#define hash_index(digest) \
	(((digest)[0] << 4) | ((digest)[1] & 0x0f))

/*---------------------------------------------------------------------------*/

static void halt(const char *str)
{
  perror(str);
  exit(1);
}

/*---------------------------------------------------------------------------*/

static int getdigest(const char *filename, MD5_CTX *mdContext)
{

  FILE *fp;
  char *from;
  char *to;
  char buf[1024];
  int chr;

  if (!(fp = fopen(filename, "r"))) return 0;
  MD5Init(mdContext);
  while (fgets(buf, sizeof(buf), fp)) {
    from = to = buf;
    while ((chr = (*from++ & 0x7f)))
      if (chr > ' ' && chr < 0x7f) *to++ = chr;
    *to = 0;
    if (!*buf) continue;
    if (buf[0] == 'R' && buf[1] == ':') continue;
    MD5Update(mdContext, (unsigned char *) buf, to - buf);
  }
  MD5Final(mdContext);
  fclose(fp);
  return 1;
}

/*---------------------------------------------------------------------------*/

static void delete_mesg(int m)
{
  if (!fpbbs) {
    fpbbs = popen("exec bbs", "w");
    if (!fpbbs) halt("bbs");
  }
  fprintf(fpbbs, "delete %d\n", m);
  fflush(fpbbs);
}

/*---------------------------------------------------------------------------*/

int main(void)
{

  MD5_CTX mdContext;
  char filename[1024];
  int *hash_link = 0;
  int entries;
  int fd;
  int firstmesg = 1;
  int h;
  int hash_table[SIZE_HASH];
  int i;
  int lastmesg;
  int m;
  int nummesg = 0;
  long timelimit;
  struct header header;
  struct index index;
  struct vitals *vitals = 0;

  timelimit = time(0) - SAVETIME;

  if (chdir(WRKDIR)) halt(WRKDIR);

  if ((fd = open(INDEXFILE, O_RDONLY, 0644)) < 0) halt(INDEXFILE);
  if (read(fd, (char *) &index, sizeof(index)) != sizeof(index))
    halt(INDEXFILE);
  if (index.date > timelimit) return 0;
  if (lseek(fd, -sizeof(index), SEEK_END) < 0) halt(INDEXFILE);
  for (; ; ) {
    if (read(fd, (char *) &index, sizeof(index)) != sizeof(index))
      halt(INDEXFILE);
    if (index.date <= timelimit) {
      lastmesg = index.mesg;
      break;
    }
    if (lseek(fd, -2 * sizeof(index), SEEK_CUR) < 0) halt(INDEXFILE);
  }
  close(fd);

  if ((fd = open(datafile, O_RDONLY, 0644)) >= 0 &&
      read(fd, (char *) &header, sizeof(header)) == sizeof(header) &&
      header.magic == MAGIC &&
      header.version == VERSION) {
    nummesg = header.nummesg;
    firstmesg = header.lastmesg + 1;
    if (firstmesg > lastmesg) return 0;
    entries = nummesg + lastmesg - header.lastmesg;
    if (!(vitals = (struct vitals *) malloc(entries * sizeof(struct vitals)))) halt("malloc");
    if (!(hash_link = (int *) malloc(entries * sizeof(int)))) halt("malloc");
    if (read(fd, (char *) vitals, nummesg * sizeof(struct vitals)) != nummesg * sizeof(struct vitals)) halt(datafile);
  }
  if (fd >= 0) close(fd);
  if (!vitals) {
    if (!(vitals = (struct vitals *) malloc(lastmesg * sizeof(struct vitals)))) halt("malloc");
    if (!(hash_link = (int *) malloc(lastmesg * sizeof(int)))) halt("malloc");
  }

  for (h = 0; h < SIZE_HASH; h++) hash_table[h] = -1;

  for (i = 0; i < nummesg; i++) {
    h = hash_index(vitals[i].digest);
    hash_link[i] = hash_table[h];
    hash_table[h] = i;
  }

  for (m = firstmesg; m <= lastmesg; m++) {
    sprintf(filename,
	    "%02x/%02x/%02x/%02x",
	    (m >> 24) & 0xff,
	    (m >> 16) & 0xff,
	    (m >>  8) & 0xff,
	    (m      ) & 0xff);
    if (getdigest(filename, &mdContext)) {
      if (!memcmp((char *) mdContext.digest, (char *) null_digest, SIZE_DIGEST)) {
	delete_mesg(m);
      } else {
	h = hash_index(mdContext.digest);
	for (i = hash_table[h];
	     i >= 0 && memcmp((char *) vitals[i].digest, (char *) mdContext.digest, SIZE_DIGEST);
	     i = hash_link[i]) ;
	if (i >= 0) {
	  delete_mesg(vitals[i].mesg);
	  vitals[i].mesg = m;
	} else {
	  vitals[nummesg].mesg = m;
	  memcpy((char *) vitals[nummesg].digest, (char *) mdContext.digest, SIZE_DIGEST);
	  hash_link[nummesg] = hash_table[h];
	  hash_table[h] = nummesg;
	  nummesg++;
	}
      }
    }
  }

  header.magic = MAGIC;
  header.version = VERSION;
  header.nummesg = nummesg;
  header.lastmesg = lastmesg;

  if ((fd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
    halt(tempfile);
  if (write(fd, (char *) &header, sizeof(header)) != sizeof(header))
    halt(tempfile);
  if (write(fd, (char *) vitals, nummesg * sizeof(struct vitals)) != nummesg * sizeof(struct vitals))
    halt(tempfile);
  close(fd);
  if (rename(tempfile, datafile)) halt(datafile);

  if (fpbbs) {
    fprintf(fpbbs, "quit\n");
    fflush(fpbbs);
    pclose(fpbbs);
  }

  return 0;
}

