/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */

#include "hostenv.h"
#include <stdio.h>
#include <ctype.h>
#include "malloc.h"
#include <pwd.h>
#include <signal.h>
#include <sysexits.h>
#include <varargs.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <mail.h>
#include "ta.h"
#include NDIR_H


#define	PROGNAME	"errormail"
#define	CHANNEL		"error"	/* the default channel name we deliver for */

char *dfltform[] = {
		"From: The Post Office <postmaster>",
		"Subject: email delivery error",
		"Cc: The Postmaster <postmaster>",
		"MIME-Version: 1.0",
		"Content-Type: multipart/report; report-type=delivery-status;",
		"",
		"Processing your mail message caused the following errors:",
		NULL
};

char *progname;

extern void warning();
extern char *optarg;
extern int optind;
extern int emptyline();
extern void prversion(), process();
extern char *strrchr();
extern char *strchr();
extern char *buildboundary();

extern char *mydomain();

#ifndef	MAXPATHLEN
#define	MAXPATHLEN 1024
#endif	/* MAXPATHLEN */

int
main(argc, argv)
	int argc;
	char *argv[];
{
	char *channel, file[MAXPATHLEN+1];
	int errflg, c;
	struct ctldesc *dp;

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
	  signal(SIGINT, wantout);
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
	  signal(SIGHUP, wantout);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
	  signal(SIGTERM, wantout);
	signal(SIGPIPE, SIG_IGN);

	if ((progname = strrchr(argv[0], '/')) == NULL)
	  progname = argv[0];
	else
	  ++progname;
	errflg = 0;
	channel = CHANNEL;
	while ((c = getopt(argc, argv, "c:V")) != EOF) {
	  switch (c) {
	  case 'c':		/* specify channel scanned for */
	    channel = optarg;
	    break;
	  case 'V':
	    prversion(PROGNAME);
	    exit(EX_OK);
	    break;
	  default:
	    ++errflg;
	    break;
	  }
	}
	if (errflg || optind != argc) {
	  fprintf(stderr, "Usage: %s [-V] [-c channel]\n",
		  argv[0]);
	  exit(EX_USAGE);
	}

	while (!getout && fgets(file, sizeof file, stdin)) {
	  if (emptyline(file, sizeof file))
	    break;

	  dp = ctlopen(file, channel, (char *)NULL, &getout, ctlsticky, NULL);
	  if (dp == NULL)
	    continue;
	  process(dp);
	  ctlclose(dp);
	}
	exit(0);
	/* NOTREACHED */
	return 0;
}

#ifndef MALLOC_TRACE
univptr_t
tmalloc(n)
	size_t n;
{
	return emalloc((u_int)n);
}
#endif

void
process(dp)
	struct ctldesc *dp;
{
	char buf[BUFSIZ];
	char **cpp, *mailshare, *mfpath;
	FILE *mfp, *efp;
	int n;
	struct rcpt *rp;
	char *boundarystr = buildboundary("errmail");
	char lastchar;
	int reportcnt = 0;

	/* recipient host field is the error message file name in FORMSDIR */
	/* recipient user field is the address causing the error */

	if (dp->senders == NULL) {
	  /*
	   * If there was no error return address
	   * it might be because this message was
	   * an error message being bounced back.
	   * We do NOT want to bounce this, but
	   * instead just drop it on the floor.
	   */
	  for (rp = dp->recipients; rp != NULL; rp = rp->next)
	    diagnostic(rp, EX_OK, "error bounce dropped");
	  return;
	}

	if ((mfp = mail_open(MSG_RFC822)) == NULL) {
	  for (rp = dp->recipients; rp != NULL; rp = rp->next)
	    diagnostic(rp, EX_TEMPFAIL, "mail_open failure");
	  warning("Cannot open mail file!");
	  return;
	}
	fprintf(mfp, "channel error\n");

	rp = dp->recipients;

	fprintf(mfp, "to %s\n",rp->addr->link->user);

	/* copy To: from error return address */
	fprintf(mfp, "To: <%s>\n", rp->addr->link->user);

	/* copy error message file itself */
	if ((mailshare = getzenv("MAILSHARE")) == NULL)
	  mailshare = MAILSHARE;

	mfpath = emalloc(3 + strlen(mailshare) + strlen(FORMSDIR) +
			 strlen(rp->addr->host));
	sprintf(mfpath, "%s/%s/%s", mailshare, FORMSDIR, rp->addr->host);

	if ((efp = fopen(mfpath, "r")) != NULL) {
	  int inhdr = 1;
	  buf[sizeof(buf)-1] = 0;
	  while (fgets(buf,sizeof(buf)-1,efp) != NULL) {
	    if (strncmp(buf,"HDR",3)==0) {
	      fputs(buf+4,mfp);
	    } else if (strncmp(buf,"SUB",3)==0) {
	      fputs(buf+4,mfp);
	    } else {
	      if (inhdr) {
		inhdr = 0;
		fprintf(mfp,"MIME-Version: 1.0\n");
		fprintf(mfp,"Content-Type: multipart/report; report-type=delivery-status;\n");
		fprintf(mfp,"\tboundary=\"%s\"\n\n\n",boundarystr);
		fprintf(mfp, "--%s\n", boundarystr);
		fprintf(mfp, "Content-Type: text/plain\n");
	      }
	      fputs(buf,mfp);
	    }
	  } /* ... while() ends.. */
	  fclose(efp);
	} else {
	  for (cpp = &dfltform[0]; *cpp != NULL; ++cpp)
	    if (*cpp == 0) {
	      fprintf(mfp, "\tboundary=\"%s\"\n\n\n",boundarystr);
	      fprintf(mfp, "--%s\n", boundarystr);
	      fprintf(mfp, "Content-Type: text/plain\n");
	    } else
	      fprintf(mfp, "%s\n", *cpp);
	}
	/* print out errors in standard format */
	fputc('\n', mfp);
	for (rp = dp->recipients; rp != NULL; rp = rp->next) {
	  /* If not prohibited, print it! */
	  if ((rp->dsnflags & _DSN_NOTIFY_NEVER) == 0) {
	    fprintf(mfp, "error: %s: %s\n",
		    rp->addr->host, rp->addr->user);
	    ++reportcnt;
	  }
	}

	/* Did we report anything ? */
	if (reportcnt == 0){
	  /* No, throw it away and ack success. */
	  mail_abort(mfp);
	  diagnostic(rp, EX_OK, NULL);
	  return;
	}

	fprintf(mfp, "\n--%s\n", boundarystr);
	fprintf(mfp, "Content-Type: message/delivery-status\n\n");

	/* Print out errors in IETF-NOTARY format as well! */

	fprintf(mfp, "Original-MTS-Type: INET\n");
	if (dp->envid != NULL)
	  fprintf(mfp, "Original-Envelope-Id: %s\n",dp->envid);
	fprintf(mfp, "Final-MTS-Type: INET\n");
	fprintf(mfp, "Final-MTA: %s\n\n", mydomain() );

	for (rp = dp->recipients; rp != NULL; rp = rp->next) {
	  /* If not prohibited, print it! */
	  if ((rp->dsnflags & _DSN_NOTIFY_NEVER) != 0)
	    continue;

	  fprintf(mfp, "Rcpt: %s\n", rp->addr->user);
	  fprintf(mfp, "Action: failed\n");
	  fprintf(mfp, "Status: 500 (%s)\n", rp->addr->host );
	  if (rp->orcpt != NULL)
	    fprintf(mfp, "Original-Rcpt: %s\n",rp->orcpt);
	  fprintf(mfp, "\n");
	}

	fprintf(mfp, "--%s\n", boundarystr);
	fprintf(mfp, "Content-Type: message/rfc822\n\n");

	/* Skip over the delivery envelope lines! */

	rp = dp->recipients;
	/* copy original message file */
	/* seek to message body -- try it anyway */
	lseek(dp->msgfd, dp->msgbodyoffset, 0 /* L_SET -- usually */);
	/* write the (new) headers with local "Received:"-line.. */
	writeheaders(rp,mfp,"\n",0,0);

	/* If the DSN RET=NO is in effect, don't copy the msg body! */
	if ((rp->dsnflags & _DSN_RET_NO) == 0) {

	  /* Copy out the rest with somewhat more efficient method */
	  lastchar = 0;
	  while ((n = read(dp->msgfd, buf, sizeof(buf))) > 0) {
	    fwrite(buf, sizeof buf[0], n, mfp);
	    lastchar = buf[n-1];
	  }
	  if (lastchar != '\n')
	    fputs("\n",mfp);
	}

	fprintf(mfp, "--%s--\n", boundarystr);
	if (ferror(mfp)) {
	  mail_abort(mfp);
	  n = EX_IOERR;
	} else if (mail_close(mfp) == EOF)
	  n = EX_IOERR;
	else
	  n = EX_OK;
	for (rp = dp->recipients; rp != NULL; rp = rp->next)
	  diagnostic(rp, n, (char *)NULL);
}
