TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "externs.h"
#include "db.h"
#include "mail.h"

int num_mail = 0;
MLIST *mlist_list = NULL;

extern int quiet_reboot;

int mail_size(OBJ *player)
{
  long size = 0;
  MAIL *m;

  for(m = player->mail;m;m = m->next)
    size += sizeof(MAIL)+strlen(m->message)+1;

  return(size);
}

void check_mail(OBJ *player, int new_only)
{
  int new = 0, total = 0;
  MAIL *m;
  char buf[4096];

  for(m = player->mail;m;m = m->next)
  {
    total++;

    if(m->flags & MF_NEW)
      new++;
  }

  if(new_only && !new)
    return;

  sprintf(buf, "|+W|You have |+Y|%d|+W| message%s.",
    total, check_plural(total, "", "s"));

  if(new)
    sprintf(buf+strlen(buf), " |+G|%d|+W| of them %s new.",
      new, check_plural(new, "is", "are"));

  notify(player, buf);
}

MAIL *new_mail(OBJ *player)
{
  MAIL *m, *new;

  new = (MAIL *)stack_alloc(sizeof(MAIL), 1, 0);

  new->next = NULL;

  if(!player->mail)
    player->mail = new;
  else
  {
    for(m = player->mail;m->next;m = m->next);
    m->next = new;
  }

  num_mail++;

  return(new);
}

static void send_mail_as(OBJ *from, OBJ *recip, char *message,
                         time_t when, int donotify, int flags)
{
  MAIL *m;

  m = new_mail(recip);
  m->from = from;
  m->date = when;
  m->rdate = (time_t)0;    /* Hasn't been read yet */
  m->flags = flags;
  m->message = stack_string_alloc(message, 1);

  notify(recip,
    tprintf("You have new mail from %s.", unparse_object(recip, from)));
}

static void send_mail(OBJ *from, char *recip, char *message, int maillist)
{
  int flags = MF_NEW;
  OBJ *who;

  who = match_player(NULL, recip);

  if(maillist == 2)
  {
    if(!who)
      return;
    flags |= MF_MAILLIST;
  }

  if(maillist == 1)
  {
    if(!who)
    {
      notify(from, no_match(recip));
      return;
    }

    if(!could_doit(from, who, "LPAGE"))
    {
      notify(from, tprintf("%s isn't accepting +mail from you.",
        unparse_object(from, who)));
      return;
    }
  }

  send_mail_as(from, who, message, now, maillist, flags);

  if(maillist == 1)
    notify(from, tprintf("You mailed %s with: %s",
      unparse_object(from, who), message));
}

static MAIL *get_mail(OBJ *player, int msgnum)
{
  MAIL *m;
  int i = 1;

  if(msgnum < 1)
    return((MAIL *)NULL);

  for(m = player->mail;m && i < msgnum;m = m->next)
    i++;

  return(m);
}

static void do_protect_mail(OBJ *player, char *arg)
{
  int msgnum = atoi(arg);
  MAIL *m;

  if(!(m = get_mail(player, msgnum)))
  {
    notify(player, "No such message number.");
    return;
  }

  if(m->flags & MF_PROTECTED)
  {
    m->flags &= ~MF_PROTECTED;
    notify(player, tprintf("Message %d has been unprotected.", msgnum));
  }
  else
  {
    m->flags |= MF_PROTECTED;
    notify(player, tprintf("Message %d has been protected.", msgnum));
  }
}

static void delete_mail(OBJ *player, MAIL *mail)
{
  MAIL *m, *mprev = NULL;

  for(m = player->mail;m;m = m->next)
  {
    if(m == mail)
      break;
    mprev = m;
  }

  if(!m)
    return;

  if(mprev)
    mprev->next = m->next;
  else
    player->mail = m->next;

  if(m->message)
    stack_free(m->message);
  stack_free(m);

  num_mail--;
}

static void del_msg(OBJ *player, char *arg)
{
  MAIL *m;
  int msgnum = atoi(arg);
  int protected = 0;
  int deleted = 0;

  if(string_compare(arg, "all"))
  {
    if(!(m = get_mail(player, msgnum)))
    {
      notify(player, "No such message number.");
      return;
    }

    if(m->flags & MF_PROTECTED)
    {
      notify(player, "You can't delete a protected message!");
      return;
    }

    delete_mail(player, m);
    notify(player, tprintf("Message %d deleted.", msgnum));
    return;
  }

  for(m = player->mail;m;m = m->next)
  {
    if(m->flags & MF_PROTECTED)
    {
      protected++;
      continue;
    }
    deleted++;
    delete_mail(player, m);
  }

  notify(player, tprintf("%d message%s deleted.",
    deleted, check_plural(deleted, " was", "s were")));
  if(protected)
    notify(player, tprintf("You have %d protected message%s.",
      protected, check_plural(protected, "", "s")));
}

static void show_mail(OBJ *player, char *arg)  
{
  MAIL *m;
  int msgnum = atoi(arg);
  char buf[4096];

  if(!(m = get_mail(player, msgnum)))
  {
    notify(player, "No such message number.");
    return;
  }

  notify(player, tprintf("Message %d:", msgnum));
  notify(player, tprintf("To: %s", name(player)));
  notify(player, tprintf("From: %s", unparse_object(player, m->from)));
  notify(player, tprintf("Date: %s", mktm(m->date, "D", player)));
  if(m->flags & MF_READ)
    notify(player, tprintf("Read: %s", mktm(m->rdate, "D", player)));

  strcpy(buf, "Flags:");

  if(m->flags & MF_READ)
    strcat(buf, " read");
  if(m->flags & MF_NEW)
    strcat(buf, " new");
  if(m->flags & MF_MAILLIST)
    strcat(buf, " maillist");
  if(m->flags & MF_PROTECTED)
    strcat(buf, " protected");

  notify(player, buf);

  notify(player, "");
  notify(player, m->message);

  m->flags &= ~MF_NEW;
  m->flags |= MF_READ;

  m->rdate = now;
}

static void list_mail(OBJ *player)
{
  char status[20];
  MAIL *m;
  int ctr = 0;

  for(m = player->mail;m;m = m->next)
  {
    status[0] = (m->flags & MF_PROTECTED)?'P':' ';
    status[1] = (m->flags & MF_MAILLIST)?'M':' ';
    status[2] = (m->flags & MF_NEW)?'*':' ';
    status[3] = '\0';

    ctr++;

    notify(player, tprintf("%3d) %s %s %s", ctr, status,
      unparse_object(player, m->from), mktm(m->date, "D", player)));
  }

  if(!ctr)
    notify(player, "You have no mail.");
}

void do_mailcmd(OBJ *player, char *arg1, char *arg2)
{
  do_mail(player, arg1, arg2, 1);
}

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

  if(!*arg1)
    list_mail(player);
  else if(!*arg2)
    show_mail(player, arg1);
  else if(string_prefix("delete", arg1))
    del_msg(player, arg2);
  else if(string_prefix("protect", arg1))
    do_protect_mail(player, arg2);
  else
    send_mail(player, arg1, arg2, donotify);
}

void write_mail()
{
  FILE *fp;
  OBJ *o;
  MAIL *m;
  int num;

  if((fp = fopen(config.maildb_name, "w")) == NULL)
  { 
    log_error("Mail save failed! Uh oh!");
    return;
  }

  fprintf(fp, "%d\n", num_mail);

  for(o = player_list;o;o = o->next)
  {
    for(m = o->mail, num = 0;m;m = m->next, num++);

    if(!num)
      continue;

    fprintf(fp, "%d\n", o->dbref);
    fprintf(fp, "%d\n", num);
    for(m = o->mail;m;m = m->next)
    {
      fprintf(fp, "%d\n", m->from->dbref);
      fprintf(fp, "%ld\n", m->date);
      fprintf(fp, "%ld\n", m->rdate);
      fprintf(fp, "%d\n", m->flags);
      fprintf(fp, "%s\n", m->message);
    }
  }

  fclose(fp);
}

void read_mail()
{ 
  FILE *fp;
  char buf[1024];
  int total = 0;
  int total_mail;
  int num;
  OBJ *player;
  MAIL *m;

  if((fp = fopen(config.maildb_name, "r")) == NULL)
  { 
    log_error("Couldn't open maildb for reading!");
    return;
  }

  fgets(buf, sizeof(buf), fp);
  total_mail = atoi(buf);

  while(total++ < total_mail)
  {
    fgets(buf, sizeof(buf), fp);
    player = find_object(atoi(buf));
    fgets(buf, sizeof(buf), fp);
    num = atoi(buf);
    while(num--)
    {
      if(!player)
      {
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        continue;
      }
      m = new_mail(player);
      fgets(buf, sizeof(buf), fp);
      if(!(m->from = find_object(atoi(buf))))
      {
        m->message = NULL;
        delete_mail(player, m);        /* Bad, bad, bad */
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        continue;
      }
      fgets(buf, sizeof(buf), fp);
      m->date = atol(buf);
      fgets(buf, sizeof(buf), fp);
      m->rdate = atol(buf);
      fgets(buf, sizeof(buf), fp);
      m->flags = atoi(buf);
      fgets(buf, sizeof(buf), fp);
      *(buf+strlen(buf)-1) = '\0';
      m->message = stack_string_alloc(buf, 1);
    }
  }

  fclose(fp);

  if(!quiet_reboot)
  {
    notify_all2(tprintf("%s total message%s (%s bytes).",
      comma(tprintf("%d", num_mail)), check_plural(num_mail, "", "s"),
      comma(tprintf("%ld", file_size(config.maildb_name)))), NULL);
    flush_all_output();
  }
}

void free_mail(OBJ *player)
{
  MAIL *m, *mnext;

  for(m = player->mail;m;m = mnext)
  {
    mnext = m->next;

    stack_free(m->message);
    stack_free(m);
  }
}

void clear_old_mail()
{ 
  OBJ *o;
  int num_msgs;
  MAIL *m;
  time_t diff;

/* Clears old read mail from the database, using the value set in        *
 * config.c as the age of the mail.  If the age is 0, nothing is deleted *
 * at all.                                                               */

  if(config.max_mail_age == 0)
    return;

  for(o = player_list;o;o = o->next)
  {
    num_msgs = 0;

    for(m = o->mail;m;m = m->next)
      if(m->flags & MF_READ && !(m->flags & MF_PROTECTED))
      {
        diff = now-m->rdate;

        if(diff >= config.max_mail_age)
        {
          delete_mail(o, m);
          num_msgs++;
        }
      }

      if(num_msgs > 0)
      {
        notify(o,
          tprintf("%d of your old, read +mail messages %s deleted.",
          num_msgs, check_plural(num_msgs, "was", "were")));
        log_important(tprintf("Cleared %d message%s from %s.",
          num_msgs, check_plural(num_msgs, "", "s"),
          unparse_object(o, o)));
      }
    }
}

MLIST *find_mlist(char *arg)
{ 
  MLIST *m;

  for(m = mlist_list;m;m = m->next)
    if(!string_compare(m->name, arg))
      break;

  return(m);
}

char is_on_mlist(OBJ *player, MLIST *mlist)
{ 
  int i;

  for(i = 0;mlist->members[i];i++)
    if(mlist->members[i] == player)
      return(1);

  return(0);
}

static MLIST *new_mlist(char *mlist_name)
{ 
  MLIST *mnew;

  mnew = (MLIST *)stack_alloc(sizeof(MLIST), 1, 0);

  mnew->name = stack_string_alloc(mlist_name, 1);
  mnew->members = (OBJ **)stack_alloc(sizeof(OBJ *), 1, 0);

  mnew->members[0] = NULL;
  mnew->last_post = 0;

  if(!mlist_list)
  { 
    mnew->next = NULL;
    mlist_list = mnew;
  }
  else
  { 
    mnew->next = mlist_list;
    mlist_list = mnew;
  }

  return(mnew);
}

static void add_to_mlist(OBJ *player, MLIST *m)
{ 
  int i;

/* Just in case someone's a moron */
  if(is_on_mlist(player, m))
    return;

  for(i = 0;m->members[i];++i);    /* Whole loop */

  if(!m->members)
    m->members = (OBJ **)stack_alloc(sizeof(OBJ *), 1, 0);
  else
    m->members = (OBJ **)stack_realloc(m->members, sizeof(OBJ *)*(i+2));

  m->members[i] = player;
  m->members[i+1] = NULL;
}

static void do_join_mlist(OBJ *player, char *mlist_name)
{ 
  MLIST *m;

  if(!(m = find_mlist(mlist_name)))
  { 
    notify(player,
      tprintf("Mailing list \"%s\" doesn't exist.", mlist_name));
    return;
  }

  if(is_on_mlist(player, m))
  { 
    notify(player,
      tprintf("You're already on the \"%s\" mailing list!", mlist_name));
    return;
  }

  add_to_mlist(player, m);

  notify(player,
    tprintf("You have joined the \"%s\" mailing list.", m->name));
}

static void del_from_mlist(OBJ *player, MLIST *m)
{ 
  int i, j;

  for(i = 0;m->members[i];i++)
    if(m->members[i] == player)
      break;

  if(!m->members[i])
    return;

  for(j = i+1;m->members[j];j++, i++)
    m->members[i] = m->members[j];
  m->members[i] = NULL;

  m->members = (OBJ **)stack_realloc(m->members, sizeof(OBJ *)*j);
}

static void del_mlist(MLIST *mlist)
{
  MLIST *m, *mprev = NULL;

  for(m = mlist_list;m;m = m->next)
  { 
    if(m == mlist)
      break;
    mprev = m;
  }

  if(!m)
    return;

  if(!mprev)
    mlist_list = mlist_list->next;
  else
    mprev->next = m->next;

  stack_free(m->name);
  stack_free(m->members);
  stack_free(m);
}

static void do_leave_mlist(OBJ *player, char *mlist_name)
{
  MLIST *m;

  if(!(m = find_mlist(mlist_name)))
  { 
    notify(player,
      tprintf("Mailing list \"%s\" doesn't exist.", mlist_name));
    return;
  }

  if(!is_on_mlist(player, m))
  { 
    notify(player,
      tprintf("You're not on the \"%s\" mailing list!", mlist_name));
    return;
  }

  del_from_mlist(player, m);

  notify(player,
    tprintf("You have left the \"%s\" mailing list.", mlist_name));

/* See if it should be destroyed */
  if(!m->members[0])
  { 
    del_mlist(m);
    notify(player,
      tprintf("Mailing list \"%s\" has been destroyed.", mlist_name));
  }
}

static void do_create_mlist(OBJ *player, char *mlist_name)
{
  MLIST *mnew;

  strcpy(mlist_name, strip_color(mlist_name));

  if(find_mlist(mlist_name))
  { 
    notify(player,
      tprintf("Mailing list \"%s\" already exists.", mlist_name));
    return;
  }

  if(!string_compare("create", mlist_name) ||
    *mlist_name == '+' || *mlist_name == '-')
  { 
    notify(player,
      tprintf("Sorry, \"%s\" is an illegal name.", mlist_name));
    return;
  }

  mnew = new_mlist(mlist_name);

  add_to_mlist(player, mnew);

  notify(player,
    tprintf("Mailing list \"%s\" has been created.", mlist_name));
}

static void do_del_mlist(OBJ *player, char *mlist_name)
{
  MLIST *m;
  int i;
  char buf[4096];

  if(!(m = find_mlist(mlist_name)))
  {
    notify(player,
      tprintf("Mailing list \"%s\" doesn't exist.", mlist_name));
    return;
  }

  if(!power(player, POW_SECURITY))
  {
    notify(player,
      "A mailing list will be automatically deleted when there are no players on the list.");
    return;
  }

  sprintf(buf, "The mailing list \"%s\" was deleted.", m->name);

  for(i = 0;m->members[i];++i)
    send_mail_as(root_obj, m->members[i], buf, now, 0, MF_NEW);

  del_mlist(m);

  notify(player, buf);
}

static void do_list_mlist(OBJ *player, char *mlist)
{
  MLIST *m;
  int i;

  if(!mlist_list)
  { 
    notify(player, "There aren't any mailing lists.");
    return;
  }

  if(!*mlist)
  { 
    notify(player, tprintf("|+B|.%s.", my_string("-", 49)));
    notify(player, tprintf("|+B|||+W|%s|+B||",
      my_center("Mailing Lists", 49)));
    notify(player, tprintf("|+B||%s|", my_string("-", 49)));
    notify(player, tprintf("|+B|||+W|%s|+B|| |+W|Mbrs |+B|||+W|%s|+B||",
      my_center("Name", 15), my_center("Last Posted To", 26)));
    notify(player, tprintf("|+B||%s|------|%s|",
      my_string("-", 15), my_string("-", 26)));

    for(m = mlist_list;m;m = m->next)
    { 
      for(i = 0;m->members[i];i++);

      notify(player,
        tprintf("|+B|||+W|%c|+B|%s|+B|||+Y| %4d |+B|| |+C|%s |+B||",
        (is_on_mlist(player, m))?'*':' ', my_ljust(m->name, 14), i,
        (!(m->last_post))?my_ljust("NEVER", 24):
        mktm(m->last_post, "D", player)));
    }

    notify(player, tprintf("|+B|`%s'", my_string("-", 49)));
    return;
  }

  if(!(m = find_mlist(mlist)))
  { 
    notify(player,
      tprintf("Mailing list \"%s\" doesn't exist!", mlist));
    return;
  }

  notify(player,
    tprintf("|+W|Members of the \"%s\" mailing list:", m->name));

  for(i = 0;m->members[i];++i)
    notify(player, unparse_object(player, m->members[i]));

  notify(player,
    tprintf("|+W|%d |+B|Total Member%s", i, check_plural(i, "", "s")));
}

static void do_send_to_mlist(OBJ *player, char *arg1, char *arg2)
{
  MLIST *m;
  int i;
  char buf[4096];

  if(!(m = find_mlist(arg1)))
  { 
    notify(player,
      tprintf("Mailing list \"%s\" doesn't exist!", arg1));
    return;
  }

  if(!is_on_mlist(player, m))
  { 
    notify(player,
      tprintf("You're not on the \"%s\" mailing list!", m->name));
    return;
  }

  if(!*arg2)
  { 
    notify(player, "You must include a message!");
    return;
  }

  sprintf(buf, "|+B|Mailing list|+W|: |+Y|%s|n| |+W|-|n| ", m->name);

  if(*arg2 == POSE_TOKEN)
    sprintf(buf+strlen(buf), "%s %s", name(player), arg2+1);
  else if(*arg2 == POSS_TOKEN)
    sprintf(buf+strlen(buf), "%s %s", poss(player), arg2+1);
  else
    strcat(buf, arg2);

  for(i = 0;m->members[i];++i)
    send_mail(player, tprintf("#%d", m->members[i]->dbref), buf, 2);

  notify(player,
    tprintf("Your message was posted to the \"%s\" mailing list.",
    m->name));

  m->last_post = now;
}

void do_mlist(OBJ *player, char *arg1, char *arg2)
{
  int help;
  SUBCOMMAND *sc, commands[] =
  {
    { "create", do_create_mlist, 0 },
    { "delete", do_del_mlist,    0 },
    { "list",   do_list_mlist,   0 },
    { NULL }
  };

  if(*arg1 == '+')
  {
    do_join_mlist(player, arg1+1);
    return;
  }
  if(*arg1 == '-')
  {
    do_leave_mlist(player, arg1+1);
    return;
  }

  if(!(sc = subcommand_match(player, arg1, commands, &help)))
  {
    if(help)
      subcommand_print(player, "+mlist", commands);
    else
      do_send_to_mlist(player, arg1, arg2);
  }
  else
    sc->func(player, arg2);
}

void load_maillists()
{
  char buf[4096];
  char *filename = "db/mlist.db";
  FILE *fp;
  int total, members;
  MLIST *m;

  if(!quiet_reboot)
  {
    notify_all2("Loading maillists...", NULL);
    flush_all_output();
  }

  if(!(fp = fopen(filename, "r")))
  { 
    log_error(tprintf("Couldn't open \"%s\" for reading!", filename));
    return;
  }

  fgets(buf, sizeof(buf), fp);
  total = atoi(buf);

  while(total--)
  { 
    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';

    m = new_mlist(buf);

    fgets(buf, sizeof(buf), fp);
    m->last_post = atol(buf);

    fgets(buf, sizeof(buf), fp);
    members = atoi(buf);

    while(members--)
    { 
      fgets(buf, sizeof(buf), fp);
      add_to_mlist(find_object(atoi(buf)), m);
    }
  }
  fclose(fp);
}

void save_maillists()
{ 
  MLIST *m;
  char *filename = "db/mlist.db";
  FILE *fp;
  int total = 0, members;

  if(!(fp = fopen(filename, "w")))
  { 
    log_error(tprintf("Couldn't open \"%s\" for writing!", filename));
    return;
  }

  for(m = mlist_list;m;m = m->next)
    total++;

  fprintf(fp, "%d\n", total);

  for(m = mlist_list;m;m = m->next)
  { 
    fprintf(fp, "%s\n", m->name);
    fprintf(fp, "%ld\n", m->last_post);

    for(members = 0;m->members[members];members++);

    fprintf(fp, "%d\n", members);

    for(members = 0;m->members[members];members++)
      fprintf(fp, "%d\n", m->members[members]->dbref);
  }
  fclose(fp);
}

void remove_from_mlists(OBJ *player)
{
  MLIST *m;

  for(m = mlist_list;m;m = m->next)
    if(is_on_mlist(player, m))
      del_from_mlist(player, m);
}

void do_dirmail(OBJ *player, char *arg)
{
  OBJ *o;
  int ctr = 0;
  char buf[1000];

  if(!power(player, POW_BROADCAST))
  { 
    notify(player, perm_denied());
    return;
  }

  if(!*arg)
  { 
    notify(player, "You must include a message!");
    return;
  }

  sprintf(buf, "--- DirMail ---  %s", arg);

  for(o = player_list;o;o = o->next)
    if(!is_root(o) && o != player)
      if(Wizard(o))
      {
        ctr++;
        send_mail(player, tprintf("#%d", o->dbref), buf, 0);
      }
  if(!ctr)
    notify(player, "There's no one to send the mail to!");
  else
  {
    notify(player, tprintf("|+W|You mailed every %s with:",
      class_to_name(CLASS_DIR)));
    notify(player, arg);
  }
}

void do_massmail(OBJ *player, char *msg)
{
  OBJ *o;
  char buf[4096];

  if(!power(player, POW_BROADCAST))
  { 
    notify(player, perm_denied());
    return;
  }

  if(!*msg)
  { 
    notify(player, "You must include a message!");
    return;
  }

  for(o = player_list;o;o = o->next)
    if(!is_root(o) && o != player)
    {
      sprintf(buf, "--- MassMail ---  %s", msg);
      send_mail(player, tprintf("#%d", o->dbref), buf, 0);
    }

  notify(player, "|+W|You mailed everyone with:");
  notify(player, msg);
}

void free_mlists(void)
{
  MLIST *m, *mnext;

  for(m = mlist_list;m;m = mnext)
  {
    mnext = m->next;

    stack_free(m->name);
    stack_free(m->members);
    stack_free(m);
  }
}