/*
 * $Id: ezmlm-send.c,v 1.1 2025-01-22 11:21:28+05:30 Cprogrammer Exp mbhangui $
 */
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "stralloc.h"
#include "byte.h"
#include "subfd.h"
#include "strerr.h"
#include "error.h"
#include "qmail.h"
#include "env.h"
#include "lock.h"
#include "sig.h"
#include "open.h"
#include "getln.h"
#include "case.h"
#include "scan.h"
#include "str.h"
#include "fmt.h"
#include "substdio.h"
#include "getconf.h"
#include "constmap.h"

#define FATAL "ezmlm-send: fatal: "

int             rename(const char *oldpath, const char *newpath);

stralloc        fnadir, fnaf, fnsub, line, sublist, mailinglist, outlocal,
				outhost, headerremove, headeradd, num, mydtline;
int             flagarchived, fdarchive, flagsublist;
unsigned long   msgnum;
char            archivebuf[1024], inbuf[1024], outbuf[1], buf0[256],
				numnewbuf[16], strnum[FMT_ULONG];
substdio        ssarchive, ssin, ssout, ssnumnew;
substdio        ss0 = SUBSTDIO_FDBUF((ssize_t(*)(int, char *, size_t)) read, 0, buf0, sizeof (buf0));
struct constmap headerremovemap;
struct qmail    qq;

void
die_usage()
{
	strerr_die1x(100, "ezmlm-send: usage: ezmlm-send dir");
}

void
die_nomem()
{
	strerr_die2x(111, FATAL, "out of memory");
}

ssize_t
mywrite(int fd, const char *buf, size_t len)
{
	qmail_put(&qq, buf, len);
	return len;
}

void
die_archive()
{
	strerr_die4sys(111, FATAL, "unable to write to ", fnaf.s, ": ");
}

void
die_numnew()
{
	strerr_die2sys(111, FATAL, "unable to create numnew: ");
}

void
put(char *buf, int len)
{
	qmail_put(&qq, buf, len);
	if (flagarchived)
		if (substdio_put(&ssarchive, buf, len) == -1)
			die_archive();
}

void
ezputs(char *buf)
{
	qmail_puts(&qq, buf);
	if (flagarchived)
		if (substdio_puts(&ssarchive, buf) == -1)
			die_archive();
}

int
sublistmatch(char *sender)
{
	int             i;
	int             j;

	j = str_len(sender);
	if (j < sublist.len)
		return 0;

	i = byte_rchr(sublist.s, sublist.len, '@');
	if (i == sublist.len)
		return 1;

	if (byte_diff(sublist.s, i, sender))
		return 0;
	if (case_diffb(sublist.s + i, sublist.len - i, sender + j - (sublist.len - i)))
		return 0;

	return 1;
}

void
numwrite()
{
	int             fd;

	fd = open_trunc("numnew");
	if (fd == -1)
		die_numnew();
	substdio_fdbuf(&ssnumnew, (ssize_t(*)(int, char *, size_t)) write, fd, numnewbuf, sizeof (numnewbuf));
	if (substdio_put(&ssnumnew, strnum, fmt_ulong(strnum, msgnum)) == -1)
		die_numnew();
	if (substdio_puts(&ssnumnew, "\n") == -1)
		die_numnew();
	if (substdio_flush(&ssnumnew) == -1)
		die_numnew();
	if (fsync(fd) == -1)
		die_numnew();
	if (close(fd) == -1)
		die_numnew();			/* NFS stupidity */
	if (rename("numnew", "num") == -1)
		strerr_die2sys(111, FATAL, "unable to move numnew to num: ");
}

int
main(int argc, char **argv)
{
	int             fd;
	char           *dir;
	int             fdlock;
	char           *sender;
	int             flagmlwasthere;
	int             match;
	int             i;
	char            ch;
	int             flaginheader;
	int             flagbadfield;

	umask(022);
	sig_pipeignore();

	dir = argv[1];
	if (!dir)
		die_usage();

	sender = env_get("SENDER");

	if (chdir(dir) == -1)
		strerr_die4sys(111, FATAL, "unable to switch to ", dir, ": ");

	fdlock = open_append("lock");
	if (fdlock == -1)
		strerr_die4sys(111, FATAL, "unable to open ", dir, "/lock: ");
	if (lock_ex(fdlock) == -1)
		strerr_die4sys(111, FATAL, "unable to obtain ", dir, "/lock: ");

	if (qmail_open(&qq) == -1)
		strerr_die2sys(111, FATAL, "unable to run qmail-queue: ");

	flagarchived = getconf_line(&line, "archived", 0, FATAL, dir);

	getconf_line(&num, "num", 1, FATAL, dir);
	if (!stralloc_0(&num))
		die_nomem();
	scan_ulong(num.s, &msgnum);
	++msgnum;

	getconf_line(&outhost, "outhost", 1, FATAL, dir);
	getconf_line(&outlocal, "outlocal", 1, FATAL, dir);
	getconf_line(&mailinglist, "mailinglist", 1, FATAL, dir);
	flagsublist = getconf_line(&sublist, "sublist", 0, FATAL, dir);

	getconf(&headerremove, "headerremove", 1, FATAL, dir);
	constmap_init(&headerremovemap, headerremove.s, headerremove.len, 0);

	getconf(&headeradd, "headeradd", 1, FATAL, dir);
	for (i = 0; i < headeradd.len; ++i)
		if (!headeradd.s[i])
			headeradd.s[i] = '\n';

	if (!stralloc_copys(&mydtline, "Delivered-To: mailing list "))
		die_nomem();
	if (!stralloc_catb(&mydtline, outlocal.s, outlocal.len))
		die_nomem();
	if (!stralloc_cats(&mydtline, "@"))
		die_nomem();
	if (!stralloc_catb(&mydtline, outhost.s, outhost.len))
		die_nomem();
	if (!stralloc_cats(&mydtline, "\n"))
		die_nomem();

	if (sender) {
		if (!*sender)
			strerr_die2x(100, FATAL, "I don't distribute bounce messages (#5.7.2)");
		if (str_equal(sender, "#@[]"))
			strerr_die2x(100, FATAL, "I don't distribute bounce messages (#5.7.2)");
		if (flagsublist)
			if (!sublistmatch(sender))
				strerr_die2x(100, FATAL, "this message is not from my parent list (#5.7.2)");
	}

	if (flagarchived) {
		if (!stralloc_copys(&fnadir, "archive/"))
			die_nomem();
		if (!stralloc_catb(&fnadir, strnum, fmt_ulong(strnum, msgnum / 100)))
			die_nomem();
		if (!stralloc_copy(&fnaf, &fnadir))
			die_nomem();
		if (!stralloc_cats(&fnaf, "/"))
			die_nomem();
		if (!stralloc_catb(&fnaf, strnum, fmt_uint0(strnum, (unsigned int) (msgnum % 100), 2)))
			die_nomem();
		if (!stralloc_0(&fnadir))
			die_nomem();
		if (!stralloc_0(&fnaf))
			die_nomem();

		if (mkdir(fnadir.s, 0755) == -1)
			if (errno != error_exist)
				strerr_die4sys(111, FATAL, "unable to create ", fnadir.s, ": ");
		fdarchive = open_trunc(fnaf.s);
		if (fdarchive == -1)
			strerr_die4sys(111, FATAL, "unable to write ", fnaf.s, ": ");

		substdio_fdbuf(&ssarchive, (ssize_t(*)(int, char *, size_t)) write, fdarchive, archivebuf, sizeof (archivebuf));
	}

	if (!flagsublist) {
		ezputs("Mailing-List: ");
		put(mailinglist.s, mailinglist.len);
		ezputs("\n");
	}
	put(headeradd.s, headeradd.len);
	put(mydtline.s, mydtline.len);

	flagmlwasthere = 0;
	flaginheader = 1;
	flagbadfield = 0;

	for (;;) {
		if (getln(&ss0, &line, &match, '\n') == -1)
			strerr_die2sys(111, FATAL, "unable to read input: ");

		if (flaginheader && match) {
			if (line.len == 1)
				flaginheader = 0;
			if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
				flagbadfield = 0;
				if (constmap(&headerremovemap, line.s, byte_chr(line.s, line.len, ':')))
					flagbadfield = 1;
				if (case_startb(line.s, line.len, "mailing-list:"))
					flagmlwasthere = 1;
				if (line.len == mydtline.len)
					if (!byte_diff(line.s, line.len, mydtline.s))
						strerr_die2x(100, FATAL, "this message is looping: it already has my Delivered-To line (#5.4.6)");
			}
		}

		if (!(flaginheader && flagbadfield))
			put(line.s, line.len);

		if (!match)
			break;
	}

	if (flagsublist)
		if (!flagmlwasthere)
			strerr_die2x(100, FATAL, "sublist messages must have Mailing-List (#5.7.2)");
	if (!flagsublist)
		if (flagmlwasthere)
			strerr_die2x(100, FATAL, "message already has Mailing-List (#5.7.2)");

	if (flagarchived) {
		if (substdio_flush(&ssarchive) == -1)
			die_archive();
		if (fsync(fdarchive) == -1)
			die_archive();
		if (fchmod(fdarchive, 0744) == -1)
			die_archive();
		if (close(fdarchive) == -1)
			die_archive();		/* NFS stupidity */
	}

	numwrite();

	if (!stralloc_copy(&line, &outlocal))
		die_nomem();
	if (!stralloc_cats(&line, "-return-"))
		die_nomem();
	if (!stralloc_catb(&line, strnum, fmt_ulong(strnum, msgnum)))
		die_nomem();
	if (!stralloc_cats(&line, "-@"))
		die_nomem();
	if (!stralloc_cat(&line, &outhost))
		die_nomem();
	if (!stralloc_cats(&line, "-@[]"))
		die_nomem();
	if (!stralloc_0(&line))
		die_nomem();

	qmail_from(&qq, line.s);

	for (i = 0; i < 53; ++i) {
		ch = 64 + i;
		if (!stralloc_copys(&fnsub, "subscribers/"))
			die_nomem();
		if (!stralloc_catb(&fnsub, &ch, 1))
			strerr_die2x(111, FATAL, "out of memory");
		if (!stralloc_0(&fnsub))
			strerr_die2x(111, FATAL, "out of memory");
		fd = open_read(fnsub.s);
		if (fd == -1) {
			if (errno != error_noent)
				strerr_die4sys(111, FATAL, "unable to read ", fnsub.s, ": ");
		} else {
			substdio_fdbuf(&ssin, (ssize_t(*)(int, char *, size_t)) read, fd, inbuf, sizeof (inbuf));
			substdio_fdbuf(&ssout, (ssize_t(*)(int, char *, size_t)) mywrite, -1, outbuf, sizeof (outbuf));
			if (substdio_copy(&ssout, &ssin) != 0)
				strerr_die4sys(111, FATAL, "unable to read ", fnsub.s, ": ");
			close(fd);
		}
	}

	switch (qmail_close(&qq)) {
	case 0:
		strnum[fmt_ulong(strnum, qmail_qp(&qq))] = 0;
		strerr_die2x(0, "ezmlm-send: info: qp ", strnum);
	default:
		--msgnum;
		numwrite();
		strerr_die2x(111, FATAL, "temporary qmail-queue error");
	}
	return 0;
}

/*
 * $Log: ezmlm-send.c,v $
 * Revision 1.1  2025-01-22 11:21:28+05:30  Cprogrammer
 * Fixes for gcc14
 *
 */
