/*

   A C based replacement for the following parts of my server:
   multi-fwd
   game-forw
   body.awk
   forw-body.awk
   good-subject
   p-Can-Fwd
   return.awk
   msg-fwd.sh

   But not one for:
   Pre-proc
   run

   This code is almost certainly Soalris specific!
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "main.h"

extern int errno;
char *games;
char *game;
char *subject;
char *home;
char error_msg[1024];
char preply[LEN];
char player[LEN];
char cplayer[LEN + 1];
FILE *rp, *mailfile, *xrefile;

int
hstrincmp (const char *s1, const char *s2, const int n)
{
  char c1, c2;
  int i;

  i = 0;
  do
  {
    c1 = tolower (*s1++);
    c2 = tolower (*s2++);
    if (c1 != c2)
      return c1 - c2;
    i++;
  }
  while ((c1) && (i < n));
  if (i == n)
    return (0);

  if (c2)
    return (1);			/* different string lengths */

  return 0;
}

void
my_abort (char *error, int code)
{
  fprintf (stderr, "%s", error);
  exit (code);
}

void
copy_reply (char *dst, char *line, int num)
{
  int i, j;

  /* BUG: only deals with trivial cases! */
  for (i = num, j = 0; line[i]; i++, j++)
  {
    dst[j] = line[i];
  }
  dst[j] = '\0';
}

void
copy_subject (char *line)
{
  int i, j;
  char *dst;

  dst = subject;
  for (i = 9, j = 0; line[i]; i++, j++)
  {
    dst[j] = line[i];
  }
  dst[j] = '\0';
}

int
next_game (char *this_game)
{
  int i;

  this_game[0] = '\0';
  for (i = 0; games[i] != ' ' && games[i]; i++)
    this_game[i] = games[i];
  this_game[i] = '\0';
  games += ++i;
  if (this_game[0])
  {
    return (1);
  }
  else
  {
    return (0);
  }
}

char *
match_game (void)
{
  unsigned int i;
  char *game;

  game = (char *) malloc (sizeof (char) * LEN);

  for (; next_game (game);)
  {
    i = strlen (game);
    if (!strncmp (game, subject, i))
    {
      return (game);
    }
  }
  free (game);
  return (NULL);
}


void
open_races_players (void)
{
  char rpfile[1024];

  rpfile[0] = '\0';

  if (strlen (home) + strlen (game) + 15 > 1024)
  {
    my_abort ("path to races:players buffer overflow\n", 1);
  }
  strcat (rpfile, home);
  strcat (rpfile, "/");
  strcat (rpfile, game);
  strcat (rpfile, "/races:players");
  rp = fopen (rpfile, "r");
  if (!rp)
  {
    sprintf (error_msg,"%s open failed\n%s\n", rpfile, strerror (errno));
    my_abort (error_msg, RPOPEN);
  }
}

int
match_type (void)
{
  for (; subject[0] == ' '; subject++);
  if (!hstrincmp (subject, ":fwd:", 4))
  {
    subject += 5;
    return (FWD);
  }

  if (!hstrincmp (subject, ":lastorders:", 11))
  {
    subject += 12;
    return (LASTORDERS);
  }

  if (!hstrincmp (subject, ":lastreport:", 11))
  {
    subject += 12;
    return (LASTREPORT);
  }

  if (!hstrincmp (subject, ":forwardlist:", 12))
  {
    subject += 13;
    return (FWDLIST);
  }

  if (!hstrincmp (subject, "orders for ", 10))
  {
    subject += 11;
    return (ORDERS);
  }

  sprintf (error_msg, "Unrecognized message type '%s'\n", subject);
  my_abort (error_msg, UNKNOWNSUBJECT);
  assert (0);
  return (0);
}

void
verify_fwd (void)
{
  char ffrom[LEN], to[MAXNUMFWD][LEN],toto[MAXNUMFWD][LEN*2+1];	/* hack, what if 101 players? */
  char line[1024];
  int auth[MAXNUMFWD];
  int j,k,l,m,flag;

  memset (ffrom, 0, sizeof (char) * LEN);
  memset (to, 0, sizeof (char) * LEN * MAXNUMFWD);
  memset (toto, 0, sizeof (char) * MAXNUMFWD * (LEN*2+1));

  for (j = 0; (subject[j] != ':') && (subject[j]); j++)
  {
    ffrom[j] = subject[j];
  }
  if (!subject[j])
  {
    sprintf (error_msg, "%s OK, FWD OK, '%s' not understood (: missing?)\n", game, ffrom);
    my_abort (error_msg, FWDNOSEND);
  }
  subject += strlen(ffrom) + 1;
  fprintf (stderr, "%s:FWD:%s to %s\n", game, ffrom, subject);
  /* Need to actually do work here */
  /* Now need to deal with
     1) Authorized to forward
     2) Do forward
  */
  /* Build a list of desired forward to parties */
  l=k=0;
  for (;subject[0];subject++)
  {
    if (subject[0]== ' ') /* Next player */
    {
      k++;
      l=0;
    }
    else
    {
      to[k][l++]=subject[0];
    }
  }
  if (to[k][0]) k++; /* last fwd token didn't end with a space */
  if (k==0)
  {
    sprintf (error_msg, "%s OK, FWD OK, from %s OK, no to?\n",game,ffrom);
    my_abort (error_msg,FWDNOTO);
  }
  /* OK, we know from whom and to whom, now check to see if legal */
  /* k==number of attempted fwds */
  open_x_ref();
  memset (auth,0,MAXNUMFWD*sizeof(int));
  for (m=0;m<k;m++)
  {
    sprintf(toto[m],"%s:%s",ffrom,to[m]);
  }
  for (;;)
  {
    fgets (line, 1024, xrefile);
    if (feof (xrefile))
    {
      fclose(xrefile);
      break;
    }
    if (!strncmp (line,ffrom,strlen(ffrom)))
    { /* Names are vaguely similar (note that there is no : in the check */
      line[strlen(line)-1]='\0'; /* Fix \n */
      for (m=0;m<k;m++)
      {
	if (!strcmp(line,toto[m]))
	  auth[m]++; /* Allowed */
      }
    }
  }
  /* OK, auth vector built */
  /* BUG: Should open authresponse file here too. */
  for (m=0,flag=0;m<k;m++)
  {
    if (auth[m])
      fprintf (stderr,"%s OK\n",toto[m]);
    else
    {
      fprintf (stderr,"%s NO\n",toto[m]);
      flag++;
    }
  }
  if (flag)
  {
    /* Should send authreponse file here */
  }

  /* Authorized to forwrd, now do it! */


}

void
open_x_ref (void)
{
  char xref[1024];

  if (strlen (home) + strlen (game) + 8 > 1024)
  {
    my_abort ("path to X-ref buffer overflow\n", 1);
  }
  sprintf (xref,"%s/%s/X-ref",home,game);
  xrefile = fopen (xref, "r");
  if (!xrefile)
  {
    sprintf (error_msg, "%s open failed\n%s\n", xref, strerror (errno));
    my_abort (error_msg, XREFOPEN);
  }
}

/* BUG: Last orders preply should be dealt with correctly */
void
lastorders (void)
{
  char cmd[1024],line[1024];
  FILE *scratch,*order;
  int code,plen;

  fprintf (stderr, "%s:lastorders:%s to %s\n", game, player, preply);
  /* Should do authorization here */
  sprintf (cmd,"scan +%s-Turns | grep -w %s | tail -1 |awk '{print $1}'> /tmp/%s",game,player,game);
  code=system(cmd);
  if (code)
  {
    sprintf (error_msg, "system bombed: %d\n", code);
    my_abort (error_msg, SYSTEMFAIL);
  }
  sprintf (cmd,"/tmp/%s",game);
  scratch=fopen(cmd,"r");
  if (scratch==NULL)
  {
    sprintf (error_msg, "%s open failed\n%s\n", cmd, strerror (errno));
    my_abort (error_msg, LASTORDEROPEN);
  }
  open_player_message ();
  plen = strlen (cplayer);
  fprintf (mailfile, "Subject: %s lastoders for %s\n\n", game,player);
  fgets(line,1024,scratch);
  fclose(scratch);
  unlink(cmd);
  sprintf (cmd,"%s/Mail/%s-Turns/%s",home,game,line);
  order=fopen (cmd,"r");
  if (order==NULL)
  {
    sprintf (error_msg,"%s not opened\n%s",cmd,strerror (errno));
    my_abort (error_msg,LASTORDERFILEOPEN);
  }
  for (;;)
  {
    fgets (line,1024,order);
    if (feof(order))
    {
      fclose(order);
      fclose(mailfile);
      send_mailfile (preply,UCBMAIL);
      break;
    }
    fprintf (mailfile,"%s",line);
  }
}

void 
lastreport (void)
{
  fprintf (stderr, "%s:lastreport:%s to %s\n", game, player, preply);
  /* Need to actually do work here */
  /* Do 
    unzip
    mailer
  */

}

/* Push out orders for player */
/* BUG: system(), also Pre-proc functionality missing! */
void
orders (void)
{
  char cmd[1024];
  int code;

  fprintf (stderr, "%s:orders:%s to %s\n", game, player, preply);
  /* Need to actually do work here */
  sprintf (cmd, "refile +%s-turns", game);
  /* BUG!!!!!! How do I do this smarter? */
  code = system (cmd);
  if (code)
  {
    sprintf (error_msg, "system bombed: %d\n", code);
    my_abort (error_msg, SYSTEMFAIL);
  }
  (void)putenv ("ORDERS=1"); /* Hack until we get Pre-proc implemented */
}

/* Generate the forwarding list for the player */
/* BUG: Authorized email address stuff not implemented */
void
fwdlist (void)
{
  unsigned int plen;
  char line[1024];

  fprintf (stderr, "%s:forwardlist:%s to %s\n", game, player, preply);

  open_x_ref ();
  open_player_message ();
  plen = strlen (cplayer);
  fprintf (mailfile, "Subject: %s Forwarding list\n\n", game);
  for (;;)
  {
    fgets (line, 1024, xrefile);
    if (feof (xrefile))
    {
      fprintf (mailfile, "\n\n");
      fclose (mailfile);
      fclose (xrefile);
      send_mailfile (preply,UCBMAIL);
      break;
    }
    if (!strncmp (cplayer, line, plen))
    {
      fprintf (mailfile, "%s\n", line);
    }
  }
}


void
open_player_message (void)
{
  /* BUG- should use real temp files! */
  mailfile = fopen ("/tmp/me", "w");
  if (mailfile == NULL)
  {
    sprintf (error_msg, "%s open failed, code %d text:\n%s\n", "/tmp/me", errno, strerror (errno));
    my_abort (error_msg, MAILOPEN);
  }
  /* Deal with players with multiple returns this (ugly) way */
  if (strchr (preply, (int) ' '))
    fprintf (mailfile, "To: %s\n", preply);
  else
    fprintf (mailfile, "To: %s <%s>\n", player, preply);
}

void
send_mailfile (const char *recip, int flag)
{
  int code;
  char cmd[201];

  if (flag==MAIL)
  {
    assert (strlen(recip)<180);
    sprintf (cmd,"/bin/mail %s < /tmp/me",recip);
  }
  else if (flag==UCBMAIL)
  {
    sprintf (cmd,"/usr/ucb/mail -r zeus@cs.utk.edu -t x < /tmp/me 2>/dev/null");
  }
  else
    assert(NULL);
  code = system (cmd);
  if (code)
  {
    sprintf (error_msg, "system bombed: %d\n", code);
    my_abort (error_msg, SYSTEMFAIL);
  }
  unlink ("/tmp/me");
}

int
parse_subject (void)
{
  int l, type, j, matched;
  unsigned int plen;
  char rpline[1024];

  game = match_game ();
  if (!game)
  {
    return (0);
  }

  l = strlen (game);
  subject += l;
  type = match_type ();

  if ((type > 0) && (type != FWD))
  {				/* Get player token and verifiy it exists! */
    open_races_players ();

    memset (player, 0, sizeof (char) * LEN);

    matched = 0;
    for (j = 0; subject[j]; j++)
    {
      player[j] = subject[j];
    }
    player[j] = '\0';
    strcpy (cplayer, player);
    strcat (cplayer, ":");
    plen = strlen (cplayer);

    /* Read through rp (races:players) until we match player */
    for (;;)
    {
      fgets (rpline, 1024, rp);
      if (feof (rp))
      {
	fclose (rp);
	break;
      }
      if (!strncmp (cplayer, rpline, plen))
      {
	matched = 1;
	fclose (rp);
	/* Should do auth stuff here! */
	if (type != ORDERS)
	{
	  strcpy (preply, rpline + plen);	/* Force reply to registered @ */
	  plen = strlen (preply) - 1;
	  if (preply[plen] == '\n')
	  {
	    preply[plen] = '\0';
	  }
	}
	break;
      }
    }
    if (matched == 0)
    {
      char err_tmp[20];

      switch (type)
      {
      case 0:
	assert (NULL);
	break;
      case FWD:
	assert (NULL);
	break;
      case LASTORDERS:
	strcpy (err_tmp, "lastorders");
	break;
      case LASTREPORT:
	strcpy (err_tmp, "lastreport");
	break;
      case ORDERS:
	strcpy (err_tmp, "orders");
	break;
      case FWDLIST:
	strcpy (err_tmp, "forwardlist");
	break;
      default:
	strcpy (err_tmp, "UNKNOWN!");
	break;
      }

      sprintf (error_msg, "Game %s OK Type %s OK player ?%s? puke\n",
	       game, err_tmp, player);
      my_abort (error_msg, UNKNOWNPLAYER);
    }
  }
  return(type);
}



int
main (int argc, char *argv[])
{
  FILE *msg;
  char line[LEN];
  int inheader, s, game_matched;
  char from[] = "From:", rsubject[] = "Subject:", replyto[] = "Reply-To:";
  char replyto2[] = "Reply-to:";
  char *ss;

  if (argc != 2)
  {
    fprintf (stderr, "Usage: %s file\n", argv[0]);
    exit (1);
  }

  msg = fopen (argv[1], "r");

  if (msg == NULL)
  {
    ss = strerror (errno);
    fprintf (stderr, "fopen of %s failed\n%s\n", argv[1], ss);
    exit (1);
  }

  inheader = 1;
  preply[0] = '\0';

  games = getenv ("GAMES");
  if (games == NULL)
  {
    my_abort ("GAMES not set!\n", GAMESNOTSET);
  }
  home = getenv ("HOME");
  if (home == NULL)
  {
    my_abort ("HOME not set!\n", HOMENOTSET);
  }
  game_matched = 0;
  subject = (char *) malloc (sizeof (char) * LEN);

  (void) umask ((mode_t) 027);

  /* Valid File, opened OK! */
  for (;;)
  {
    fgets (line, LEN, msg);	/* fgets reads in LEN-1 chars */
    if (feof (msg))
    {
      fclose (msg);
      break;
    }
    s = strlen (line);
    if (line[--s] == '\n')
      line[s] = '\0';

    if (inheader)
    {
      if (line[0])
      {
	if ((line[0] != ' ') && (line[0] != '\t'))
	{			/* Not a continuation line */
	  if ((!strncmp (from, line, 5)) && (!preply[0]))
	  {
	    copy_reply (preply, line, 6);
	  }
	  if ((!strncmp (replyto, line, 9)) || (!strncmp (replyto2, line, 9)))
	  {
	    copy_reply (preply, line, 10);
	  }
	  if (!strncmp (rsubject, line, 8))
	  {
	    copy_subject (line);
	    game_matched = parse_subject ();
	  }
	}
      }
      else
      {				/* Separation line between headers and body */
	inheader = 0;
      }
    }
    else
    {
      /* How do we deal with being in the body? */
      switch (game_matched)
      {
      case 0:
        assert (NULL);
        break;
      case FWD:
        verify_fwd ();
        break;
      case LASTORDERS:
        lastorders ();
        break;
      case LASTREPORT:
        lastreport ();
        break;
      case ORDERS:
        orders ();
        break;
      case FWDLIST:
        fwdlist ();
        break;
      }
    }
  }
  /*printf ("%s\n%s\n%d\n",preply, subject,game_matched); */
  return (0);
}
