#include "getline.h"
#include "control.h"
#include "stralloc.h"
#include "readwrite.h"
#include "substdio.h"
#include "subfd.h"
#include "qqtalk.h"
#include "quote.h"
#include "open.h"
#include "str.h"
#include "datetime.h"
#include "date822fmt.h"
#include "now.h"
#include "fmt.h"
#include "conf-mailhome.h"

void die_nomem()
{
  substdio_putsflush(subfderr,"maildirbounce: fatal: out of memory\n");
  _exit(111);
}
void die_open(fn)
char *fn;
{
  substdio_puts(subfderr,"maildirbounce: fatal: unable to open ");
  substdio_puts(subfderr,fn);
  substdio_puts(subfderr,"\n");
  substdio_flush(subfderr);
  _exit(111);
}
void die_unlink(fn)
char *fn;
{
  substdio_puts(subfderr,"maildirbounce: fatal: unable to unlink ");
  substdio_puts(subfderr,fn);
  substdio_puts(subfderr,"\n");
  substdio_flush(subfderr);
  _exit(111);
}
void die_read()
{
  substdio_putsflush(subfderr,"maildirbounce: fatal: unable to read\n");
  _exit(111);
}
void die_control()
{
  substdio_putsflush(subfderr,"maildirbounce: fatal: unable to read controls\n");
  _exit(111);
}
void die_qq()
{
  substdio_putsflush(subfderr,"maildirbounce: fatal: unable to invoke qmail-queue\n");
  _exit(111);
}
void die_qqx()
{
  substdio_putsflush(subfderr,"maildirbounce: fatal: message not accepted by qmail-queue\n");
  _exit(111);
}

char num[FMT_ULONG];
unsigned long qp;

stralloc me = {0};
int meok;
stralloc bouncefrom = {0};
stralloc bouncehost = {0};
stralloc doublebounceto = {0};
stralloc doublebouncehost = {0};

stralloc line = {0};
stralloc sender = {0};
stralloc quoted = {0};
stralloc recipient = {0};

substdio ss;
char buf[1024];
char copybuf[1024];
char datestr[DATE822FMT];

int bounce(fn,fd,why)
char *fn;
int fd;
stralloc *why; /* must end with \n; must not contain \n\n */
{
  struct qqtalk qqt;
  int match;
  int r;
  char *bouncesender;
  char *bouncerecip;
  struct datetime dt;

  substdio_fdbuf(&ss,read,fd,buf,sizeof(buf));

  if (getline2(&ss,&line,&match,'\n') == -1) die_read();
  if (!match) return 0;
  if (line.len < 16) return 0;
  if (str_diffn(line.s,"Return-Path: <",14)) return 0;
  if (line.s[line.len - 2] != '>') return 0;
  if (line.s[line.len - 1] != '\n') return 0;
  if (!stralloc_copyb(&sender,line.s + 14,line.len - 16)) die_nomem();
  if (byte_chr(sender.s,sender.len,'\0') < sender.len) return 0;
  if (!stralloc_0(&sender)) die_nomem();

  if (getline2(&ss,&line,&match,'\n') == -1) die_read();
  if (!match) return 0;
  if (line.len < 15) return 0;
  if (str_diffn(line.s,"Delivered-To: ",14)) return 0;
  if (line.s[line.len - 1] != '\n') return 0;
  if (!stralloc_copyb(&recipient,line.s + 14,line.len - 15)) die_nomem();

  if (!str_diff(sender.s,"#@[]")) return 2;

  if (qqtalk_open(&qqt,1) == -1) die_qq();
  qp = qqtalk_qp(&qqt);

  if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
  else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }

  qqtalk_puts(&qqt,"Date: ");
  datetime_tai(&dt,now());
  qqtalk_put(&qqt,datestr,date822fmt(datestr,&dt));
  qqtalk_puts(&qqt,"From: ");
  while (!quote(&quoted,&bouncefrom)) die_nomem();
  qqtalk_put(&qqt,quoted.s,quoted.len);
  qqtalk_puts(&qqt,"@");
  qqtalk_put(&qqt,bouncehost.s,bouncehost.len);
  qqtalk_puts(&qqt,"\nTo: ");
  while (!quote2(&quoted,bouncerecip)) die_nomem();
  qqtalk_put(&qqt,quoted.s,quoted.len);

  qqtalk_puts(&qqt,"\n\
Subject: failure notice\n\
\n\
Hi. This is the maildirbounce program at ");
  qqtalk_put(&qqt,bouncehost.s,bouncehost.len);

  qqtalk_puts(&qqt,*sender.s ? ".\n\
I'm afraid I wasn't able to deliver your message to the following address.\n\
This is a permanent error; I've given up. Sorry it didn't work out.\n\
\n\
" : ".\n\
I tried to deliver a bounce message to this address, but the bounce bounced!\n\
\n\
");

  qqtalk_puts(&qqt,"<");
  qqtalk_put(&qqt,recipient.s,recipient.len);
  qqtalk_puts(&qqt,">:\n");
  qqtalk_put(&qqt,why->s,why->len);
  qqtalk_puts(&qqt,"\n");

  qqtalk_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n");
  qqtalk_puts(&qqt,"Return-Path: <");
  while (!quote2(&quoted,sender.s)) die_nomem();
  qqtalk_put(&qqt,quoted.s,quoted.len);
  qqtalk_puts(&qqt,">\n");

  while ((r = substdio_get(&ss,copybuf,sizeof(copybuf))) > 0)
    qqtalk_put(&qqt,copybuf,r);
  if (r == -1) die_read();
  close(fd);

  qqtalk_from(&qqt,bouncesender);
  qqtalk_to(&qqt,bouncerecip);
  if (qqtalk_close(&qqt)) die_qqx();

  return 1;
}

stralloc why = {0};
stralloc fn = {0};

void main()
{
  int match;
  int fd;
  int r;

  if (!stralloc_copys(&fn,CONF_MAILHOME)) die_nomem();
  if (!stralloc_cats(&fn,"/control/me")) die_nomem();
  r = control_readline(&me,fn.s);
  if (r == -1) die_control();
  if (r == 0) if (!stralloc_copys(&me,"me")) die_nomem();

  if (!stralloc_copys(&fn,CONF_MAILHOME)) die_nomem();
  if (!stralloc_cats(&fn,"/control/bouncefrom")) die_nomem();
  r = control_readline(&bouncefrom,fn.s);
  if (r == -1) die_control();
  if (r == 0) if (!stralloc_copys(&bouncefrom,"MAILER-DAEMON")) die_nomem();

  if (!stralloc_copys(&fn,CONF_MAILHOME)) die_nomem();
  if (!stralloc_cats(&fn,"/control/bouncehost")) die_nomem();
  r = control_readline(&bouncehost,fn.s);
  if (r == -1) die_control();
  if (r == 0) if (!stralloc_copy(&bouncehost,&me)) die_nomem();

  if (!stralloc_copys(&fn,CONF_MAILHOME)) die_nomem();
  if (!stralloc_cats(&fn,"/control/doublebouncehost")) die_nomem();
  r = control_readline(&doublebouncehost,fn.s);
  if (r == -1) die_control();
  if (r == 0) if (!stralloc_copy(&doublebouncehost,&me)) die_nomem();

  if (!stralloc_copys(&fn,CONF_MAILHOME)) die_nomem();
  if (!stralloc_cats(&fn,"/control/doublebounceto")) die_nomem();
  r = control_readline(&doublebounceto,fn.s);
  if (r == -1) die_control();
  if (r == 0) if (!stralloc_copys(&doublebounceto,"postmaster")) die_nomem();

  if (!stralloc_cats(&doublebounceto,"@")) die_nomem();
  if (!stralloc_cat(&doublebounceto,&doublebouncehost)) die_nomem();
  if (!stralloc_0(&doublebounceto)) die_nomem();

  for (;;) {
    if (getline2(subfdin,&fn,&match,'\0') == -1) die_read();
    if (!match) break;
    if (getline2(subfdin,&why,&match,'\n') == -1) die_read();
    if (!match) break;
    fd = open_read(fn.s);
    if (fd == -1) die_open(fn.s);

    r = bounce(fn.s,fd,&why);
    switch(r) {
      case 2:
	substdio_puts(subfderr,"maildirbounce: triple bounce, discarding ");
	break;
      case 1:
	substdio_puts(subfderr,"maildirbounce: qp ");
	substdio_puts(subfderr,num,fmt_ulong(num,qp));
	substdio_puts(subfderr," ");
	break;
      default:
	substdio_puts(subfderr,"maildirbounce: unrecognized format in ");
	break;
    }
    substdio_puts(subfderr,fn.s);
    substdio_puts(subfderr,"\n");
    substdio_flush(subfderr);

    if ((r == 2) || (r == 1))
      if (unlink(fn.s) == -1)
	die_unlink(fn.s);
  }
  _exit(0);
}
