muse1.7b4/
muse1.7b4/config/
muse1.7b4/doc/
muse1.7b4/run/
muse1.7b4/run/db/
muse1.7b4/src/
muse1.7b4/src/db/
muse1.7b4/src/files/
muse1.7b4/src/io/
muse1.7b4/src/prog/
muse1.7b4/src/util/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "externs.h"
#include "db.h"
#include "nalloc.h"

int mdb_alloc;
int mdb_top;
int mdb_first_free;

extern NALLOC *glurp;

typedef int mdbref;
#define NOMAIL ((mdbref)-1)

struct mdb_entry {
  dbref from;
  long date;
  int flags;			/* see the following. */
#define MF_DELETED 1
#define MF_READ 2
#define MF_NEW 4
  char *message;		/* null if unused entry */
  mdbref next;			/* next garbage, or next message. */
} *mdb;

static
#ifdef __GNUC__
inline
#endif
mdbref get_mailk(player)
     dbref player;
{
   char *i;

  if (!*(i=atr_get(player,A_MAILK)))
    return NOMAIL;
  else
    return atoi(i);
}

static
#ifdef __GNUC__
inline
#endif
void set_mailk(player, mailk)
     dbref player;
     mdbref mailk;
{
  char buf[20];
  sprintf(buf,"%d",mailk);
  atr_add(player,A_MAILK,buf);
}

void check_mail(player)
     dbref player;
{
  if (get_mailk(player) != NOMAIL) {
    int read=0, new=0, tot=0;
    char buf[1024];
    
    mdbref i;


    for(i=get_mailk(player); i != NOMAIL; i=mdb[i].next) {
      if (mdb[i].flags&MF_READ)
	read++;
      if (mdb[i].flags&MF_NEW)
	new++;
      if (!(mdb[i].flags&MF_DELETED))
	tot++;
    }
    sprintf(buf,"You have %d message%s.",tot,(tot==1)?"":"s");
    if (new) {
      sprintf(buf+strlen(buf)," %d of them %s new.",new,(new==1)?"is":"are");
      if ((tot-read-new)>0)
	sprintf(buf+strlen(buf)-1,"; %d other%s unread.",tot-read-new,(tot-read-new==1)?" is":"s are");
    } else
      if ((tot-read)>0)
	sprintf(buf+strlen(buf)," %d of them %s unread.",tot-read,(tot-read==1)?"is":"are");
    notify(player,buf);
  }
}

static mdbref grab_free_mail_slot()
{
  if (mdb_first_free != NOMAIL)
    if (mdb[mdb_first_free].message) {
      log_error("+mail's first_free's message isn't null!");
      mdb_first_free = NOMAIL;
    } else {
      mdbref z=mdb_first_free;
      mdb_first_free = mdb[mdb_first_free].next;
      return z;
    }
  if (++mdb_top >= mdb_alloc) {
    mdb_alloc *= 2;
    mdb = realloc(mdb,sizeof(struct mdb_entry) * mdb_alloc);
  }
  mdb[mdb_top-1].message = NULL;
  return mdb_top-1;
}

static void make_free_mail_slot(i)
     mdbref i;
{
  if (mdb[i].message) free(mdb[i].message);
  mdb[i].message = NULL;
  mdb[i].next = mdb_first_free;
  mdb_first_free = i;
}

void init_mail()
{
  mdb_top = 0;
  mdb_alloc = 512;
  mdb = malloc(sizeof(struct mdb_entry)*mdb_alloc);
  mdb_first_free = NOMAIL;
}

void free_mail()
{
  int i;
  for (i=0; i<mdb_top; i++)
    if (mdb[i].message) free(mdb[i].message);
  if (mdb) free(mdb);
}

static void send_mail_as(from,recip,message,when,flags)
     dbref from,recip;
     char *message;
     time_t when;
     int flags;
{
  mdbref i, prev=NOMAIL;
  int msgno=1;

  for (i=get_mailk(recip); i!=NOMAIL; i=mdb[i].next) 
    if (mdb[i].flags&MF_DELETED) {
      /* we found a spot! */
      break;
    } else {
      prev=i;
      msgno++;
    }
  if (i == NOMAIL) {
    /* sigh. no deleted messages to fill in. we'll have to tack a */
    /* new one on the end. */
    if (prev == NOMAIL) {
      /* they've got no mail at all. */
      i = grab_free_mail_slot();
      
      set_mailk(recip,i);
    } else {
      mdb[prev].next = i = grab_free_mail_slot();
    }
    mdb[i].next = NOMAIL;
  }
  mdb[i].from = from;
  mdb[i].date = when;
  mdb[i].flags = flags;
  SET(mdb[i].message, message);

  if (from != NOTHING)
    notify(recip,tprintf("You sense that you have new mail from %s (message number %d)",unparse_object(recip,from),msgno));
  else
    notify(recip,"You sense that you have new mail.");
}

void send_mail(from,recip,message)
     dbref from,recip;
     char *message;
{
  send_mail_as(from,recip,message,time(NULL),MF_NEW);
}

void do_mail(player,arg1,arg2)
     dbref player;
     char *arg1;
     char *arg2;
{
  if (Typeof(player)!=TYPE_PLAYER || Guest(player)) {
    notify(player,"Sorry, only real players can use mail.");
    return;
  }

   if (!string_compare(arg1,"delete")) {
    /* delete a message. */
    mdbref i;
    int num;

    if (*arg2) {
      num = atoi(arg2);
      if (num <= 0) {
	notify(player,"You have no such message.");
	return;
      }
      for (i=get_mailk(player); i!=NOMAIL && 0<--num; i=mdb[i].next);
      if (i == NOMAIL)
	notify(player,"You have no such message.");
      else {
	mdb[i].flags = MF_DELETED;
	notify(player, "Ok, deleted.");
      }
    } else {
      for (i=get_mailk(player); i!=NOMAIL; i=mdb[i].next)
	mdb[i].flags = MF_DELETED;
      notify(player, "All messages deleted.");
    }
  } else if (!string_compare(arg1,"undelete")) {
    /* delete a message. */
    mdbref i;
    int num;

    if (*arg2) {
      num = atoi(arg2);
      if (num <= 0) {
	notify(player,"You have no such message.");
	return;
      }
      for (i=get_mailk(player); i!=NOMAIL && 0<--num; i=mdb[i].next);
      if (i == NOMAIL)
	notify(player,"You have no such message.");
      else {
	mdb[i].flags = MF_READ;
	notify(player, "Ok, undeleted.");
      }
    } else {
      for (i=get_mailk(player); i!=NOMAIL; i=mdb[i].next)
	mdb[i].flags = MF_READ;
      notify(player, "All messages undeleted.");
    }
  } else if (!string_compare(arg1, "purge")) {
    /* purge deleted messages. */
    mdbref i,next;
    mdbref prev=NOMAIL;
    for (i=get_mailk(player); i!=NOMAIL; i=next) {
      next = mdb[i].next;
      if (mdb[i].flags&MF_DELETED) {
	if (prev != NOMAIL)
	  mdb[prev].next = mdb[i].next;
	else
	  set_mailk(player,mdb[i].next);
	make_free_mail_slot(i);
      } else
	prev=i;
    }
    notify(player,"deleted messages purged.");
  } else if (*arg1 && !*arg2) {	/* must be reading a message. */
    int i,k;
    mdbref j=NOMAIL;
    char buf[1024];

    k=i=atoi(arg1);
    if (i>0)
      for (j=get_mailk(player); j != NOMAIL && i>1; j=mdb[j].next, i--);
    if (j == NOMAIL) {
      notify(player,"Invalid message number.");
      return;
    }
    
    notify(player,tprintf("Message %d:",k));
    notify(player,tprintf("From: %s",(mdb[j].from != NOTHING)?unparse_object(player,mdb[j].from):"The MUSE server"));
    sprintf(buf,"Date: %s",mktm(mdb[j].date,"D",player));
    notify(player,buf);
    strcpy(buf,"Flags:");
    if (mdb[j].flags&MF_DELETED) strcat(buf," deleted");
    if (mdb[j].flags&MF_READ) strcat(buf," read");
    if (mdb[j].flags&MF_NEW) strcat(buf," new");
    notify(player,buf);
    if (power(player,POW_SECURITY))
      notify(player,tprintf("Mailk: %d",j));
    
    notify(player,"");
    notify(player,mdb[j].message);

    mdb[j].flags &=~MF_NEW;
    mdb[j].flags |= MF_READ;
  } else if (!*arg1 && !*arg2) { /* list mail. */
    mdbref j;
    int i=1;
    char buf[1024];

    for (j=get_mailk(player); j!=NOMAIL; j=mdb[j].next, i++) {
      char status='u';
      if (mdb[j].flags&MF_DELETED)
	status='d';
      else if (mdb[j].flags&MF_NEW) {
	status='*';
	mdb[j].flags &= ~MF_NEW;
      } else if (mdb[j].flags&MF_READ)
	status=' ';

      sprintf(buf,tprintf("%3d) %c %s %s",i,status,unparse_object(player,mdb[j].from),mktm(mdb[j].date,"D",player)));
      notify(player,buf);
    }
    notify(player,"");
  } else if (!*arg1 && *arg2)
    notify(player,"You want to do what?");
  else if (*arg1 && *arg2) {	/* send mail */
    dbref recip;

    recip = lookup_player(arg1);
    if (recip == NOTHING || Typeof(recip) != TYPE_PLAYER) {
      notify(player,"i haven't a clue who you're talking about.");
      return;
    }

    send_mail(player,recip,arg2);
    notify(player,tprintf("You mailed %s with '%s'",unparse_object(player,recip),arg2));
  } else
    log_error(tprintf("We shouldn't get here +mail. arg1: %s. arg2: %s.",arg1,arg2));
}


void write_mail(f)
     FILE *f;
{
  dbref d;
  mdbref i;

  for (d=0; d<db_top; d++)
    if (Typeof(d)==TYPE_PLAYER && (i=get_mailk(d))!=NOMAIL)
      for (; i != NOMAIL; i=mdb[i].next)
	if (!(mdb[i].flags&MF_DELETED))
	  fprintf(f,"+%d:%d:%d:%d:%s\n",mdb[i].from,d,mdb[i].date,mdb[i].flags,mdb[i].message);
}

void read_mail(f)
     FILE *f;
{
  char buf[2048];
  dbref to;
  dbref from;
  time_t date;
  int flags;
  char message[1024];
  char *s;

  while (fgets(buf, 2048, f)) {
    if (*buf == '+') {
      from = atoi(s=buf+1);
      if ((s=strchr(buf,':'))) {
	to = atoi(++s);
	if ((s=strchr(s,':'))) {
	  date = atoi(++s);
	  if ((s=strchr(s,':'))) {
	    flags = atoi(++s);
	    if ((s=strchr(s,':'))) {
	      strcpy (message, ++s);
	      if ((s=strchr(message,'\n'))) {
		*s = '\0';
		/* a good one. we just ignore bad ones. */
		send_mail_as(from,to,message,date,flags);
	      }
	    }
	  }
	}
      }
    }
  }
}