/*
 *                              mmdfcatmail.c
 *
 *  Due to the behavior of MMDF (at least on SCO Unix), we can't use
 * listserv's /usr/server/catmail program within an aliased pipe.  MMDF strips
 * the first "From  " line in pipes, which listserv requires.  It would
 * probably be best if listserv always used the From: or Sender: rfc822 header.
 *
 * This program is a frontend to catmail.  It reads ahead in a mail stream and
 * picks the sender's address from the rfc822 header and assembles a fake
 * "From addr date" line and sends the whole mess to catmail.  All command line
 * arguments are passed directly to catmail.
 *
 *  To use this facility with MMDF, add lines such as this to your
 * /usr/mmdf/table/alias.list:
 *
 *  listserv: server|/usr/server/mmdfcatmail -r -f
 *  somelist: server|/usr/server/mmdfcatmail -L SOMELIST -f
 *
 * Note that your /usr/mmdf/mmdftailor file must have the "trusted" keyword
 * in the ALIAS entry for the alias.list file or pipes will not work:
 *
 *    ALIAS     table=lalias,   nobypass,       trusted 
 *
 * Here is the don't sue me copyright:
 *
 * Copyright 1993 Mark Diekhans (markd@grizzly.com)
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies.  Mark Diekhans makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */


#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "defs.h"
#include "struct.h"
#include "global.h"

BOOLEAN tty_echo = FALSE; /* -e option off */
FILE *report      = NULL; /* Progress report to the administrator */

extern void extract_address (char *);

#define CATMAIL   "/usr/server/catmail"

/*
 * Maximum number of lines to read looking for "From:" or "Sender:".
 */
#define MAX_HEADER_LINES  2048

/*
 * Type used to save header fields in memory.  Also structure used to
 * keep a list of these header lines.
 */
typedef struct strEntry_t {
    struct strEntry_t *next;
    char               line [1];  /* MUST BE LAST FIELD. */
} strEntry_t;

typedef struct {
    strEntry_t  *head;
    strEntry_t  *tail;
} strList_t;

/*
 * Save a line away in a dynamically allocated structure and added that
 * structure to a list.
 */

void
SaveLine (const char  *line,
          strList_t   *list)
{
    strEntry_t *entry;

    entry = (strEntry_t *) malloc (sizeof (strEntry_t) + (strlen (line)));
    strcpy (entry->line, line);
    entry->next = NULL;

    if (list->tail == NULL) {
        list->head = entry;
    } else {
        list->tail->next = entry;
    }
    list->tail = entry;
}

/*
 * Start the catmail process.  
 */
pid_t
StartCatmail (const char **argv,
              FILE       **toPipe)
{
    int    pipefds [2];
    pid_t  pid;

    *toPipe = NULL;

    if (pipe (pipefds) == 0) {
        *toPipe = fdopen (pipefds [1], "w");
    }
    if (*toPipe == NULL) {
        fprintf (stderr, "open of pipe failed: %s", strerror (errno));
        exit (1);
    }

    pid = fork ();
    if (pid < 0) {
        fprintf (stderr, "fork failed: %s", strerror (errno));
        exit (1);
    }
    if (pid > 0) {
        close (pipefds [0]);
        return pid;
    }

    close (pipefds [1]);
    close (STDIN_FILENO);
    if (dup2 (pipefds [0], STDIN_FILENO) < 0) {
        fprintf (stderr, "dup to stdin failed: %s", strerror (errno));
        exit (1);
    }
 
    argv [0] = strrchr (CATMAIL, '/');
    if (argv [0] == NULL)
        argv [0] = CATMAIL;
    execv (CATMAIL, argv);
    
    fprintf (stderr, "exec of %s failed: %s", CATMAIL, strerror (errno));
    exit (1);
}

/*
 * Do actual work of copying stdin catmail process, filling in the missing
 * "From address Date" line.
 */
main (int          argc,
      const char **argv)
{
    strList_t    headerList;
    strEntry_t  *entry;
    char         line [MAX_LINE + 1];
    char         fromAddress [MAX_LINE];
    char         date [MAX_LINE];
    int          linesRead = 0, waitStat;
    pid_t        pid;
    FILE        *toPipe;

    headerList.head = NULL;
    headerList.tail = NULL;
    fromAddress [0] = '\0';
    date [0] = '\0';

    /*
     * Search for the minimum headers we need and save away lines for later
     * output.
     */
    while (! ((fromAddress [0] != '\0') && (date [0] != '\0'))) {
        if (fgets (line, MAX_LINE, stdin) == NULL)
            break;

        SaveLine (line, &headerList);

        if (strncmp (line, "From:", 5) == 0) {
            strcpy (fromAddress, line + 5); 
            extract_address (fromAddress);
            continue;
        }
        if (strncmp (line, "Sender:", 6) == 0) {
            strcpy (fromAddress, line + 6); 
            extract_address (fromAddress);
            continue;
        }
        if (strncmp (line, "Date:", 5) == 0) {
            strcpy (date, line + 6); 
            continue;
        }
        linesRead++;
        if (linesRead > MAX_HEADER_LINES)
            break;
    }
    if (fromAddress [0] == '\0') {
        fprintf (stderr, "No \"From:\" or \"Sender:\" header found\n");
        exit (1);
    }
    if (date [0] == '\0') {
        fprintf (stderr, "No \"date:\" header found\n");
        exit (1);
    }

    pid = StartCatmail (argv, &toPipe);

    /*
     * Output our madeup from line. (date has "\n").
     */
    fprintf (toPipe, "From %s %s", fromAddress, date);

    entry = headerList.head;
    while (entry != NULL) {
        fputs (entry->line, toPipe);
        entry = entry->next;
    }

    while (fgets (line, MAX_LINE, stdin) != NULL)
        fputs (line, toPipe);

    fclose (toPipe);

    if (waitpid (pid, &waitStat, 0) < 0) {
        fprintf (stderr, "waitpid failed: %s", strerror (errno));
        exit (1);
    }

    if (WIFEXITED (waitStat))
        exit (WEXITSTATUS (waitStat));

    fprintf (stderr, "%s terminated for unknown reasons\n", CATMAIL);
    exit (1);

}

/*
 * The library needs this guy.
 */
int
gexit (int exitcode)
{
    exit (exitcode);
}
