
#include <h/mh.h>

/*
 *
 * (if SPOP is defined)
 * spop.c -- nmh style mailer to write to a POP subscriber
 *
 * sbboards.c -- nmh style mailer to write to a ZOTnet BBoard
 *
 * $Id$
 */

#ifndef	SPOP

/*
 * This program acts like the MMDF ch_bboards channel: it does local
 * delivery to a ZOTnet BBoard and/or addition re-distribution to other
 * recipients of the BBoard.  This program can function both as a SendMail
 * mailer and an MH .mh_receive file, depending on whether SENDMTS or
 * MHMTS is set.  Currently, the MHMTS version of this program does not do
 * re-distribution.
 *
 * This program should be used ONLY if you have "bboards on" set in your
 * MH configuration, and if you have "mts sendmail" or "mts mh" set as well.
 */

#else

/*
 * This program acts like the MMDF-II ch_pop channel: it does local
 * delivery for non-local users.  These users are known as POP subscribers
 * and use the Post Office Protocol with a POP server in order to access
 * their maildrop.
 */

#endif

#undef DISTRIBUTE

#ifdef SENDMTS
# ifndef SPOP
#  define DISTRIBUTE
# endif	not SPOP
#endif SENDMTS

#include <pwd.h>

#ifndef	SPOP
# include <h/addrsbr.h>
#endif

#include <h/dropsbr.h>
#include <zotnet/bboards/bboards.h>
#include <zotnet/tws/tws.h>
#include <zotnet/mts/mts.h>

#ifndef	SYS5
# include <sysexits.h>
#else SYS5
# define EX_CANTCREAT	1
# define EX_IOERR	1
# define EX_NOINPUT	1
# define EX_NOUSER	1
# define EX_OK		0
# define EX_OSERR	1
# define EX_OSFILE	1
# define EX_UNAVAILABLE	1
# define EX_USAGE	1
#endif

#ifdef DISTRIBUTE
# include <mts/sendmail/smail.h>
#endif

#define	NBB	100

#ifndef	SPOP
# define ENTITY	"bboard"
#else
# define ENTITY	"subscriber"
#endif

static int  bb_fderr;
static int  bb_uid;
static int  bb_gid;

#ifndef	SPOP
static char bb_from[BUFSIZ];
static char bb_head[BUFSIZ];
static char bb_home[BUFSIZ];
static char bb_time[BUFSIZ];
# ifdef	DISTRIBUTE
static char bb_rept[BUFSIZ];
# endif	DISTRIBUTE
#else SPOP
# define bb_head NULL
#endif SPOP

static struct bboard  *bb[NBB];

/*
 *   HACK
 */
#define	adios my_adios

int dst_rcpt ();

/*
 * static prototypes
 */
static localmail();
static arginit();
static int lose();
static int copyfile();
static void adios();


main (int argc, char **argv, char **envp)
{
    int fd;
    char tmpfil[BUFSIZ];

#ifdef LOCALE
	setlocale(LC_ALL, "");
#endif
#ifdef	MHMTS
    if (argc != 5)
	adios (EX_USAGE, NULL, "you lose really big");
#endif	MHMTS
    arginit (argv);

    fflush (stdout);
    discard (stdout);		/* XXX: reference discard to help loader */

    fd = copyfile (fileno (stdin), tmpfil);
    unlink (tmpfil);

    localmail (fd);
#ifdef	DISTRIBUTE
    distribute (fd);
    notify (fd);
#endif	DISTRIBUTE

    exit (EX_OK);
}


static void
localmail (int fd)
{
    int i, md;
    register struct bboard  *bp;

    for (i = 0; bp = bb[i]; i++)
	if (bp->bb_file && *bp->bb_file) {
	    lseek (fd, (off_t)0, 0);
#ifndef	SPOP
	    if ((md = mbx_open (bp->bb_file, bb_uid, bb_gid, BBMODE))
#else	SPOP
	    if ((md = mbx_open (bp->bb_file, bb_uid, bb_gid, POMODE))
#endif	SPOP
		    == NOTOK) {
		lose ("unable to open %s", bp->bb_file);
		continue;
	    }
#ifndef	SPOP
	    if (mbx_init (bp) != NOTOK)
#endif	not SPOP
		mbx_copy (bp->bb_file, md, fd, 1, bb_head, 0);
	    mbx_close (bp->bb_file, md);
	}
}


#ifndef	SPOP
static int
mbx_init (struct bboard *bp)
{
    int	fd, clear;
    register struct bboard  *ip;
    register FILE *fp;

    if ((fd = mbx_Xopen (bp->bb_info, bb_uid, bb_gid, BBMODE, &clear))
	    == NOTOK)
	return lose ("unable to lock and open %s", bp->bb_info);
    if ((fp = fdopen (fd, "w")) == NULL) {
        mbx_close (bp->bb_info, fd);
	return lose ("unable to fdopen %s", bp->bb_info);
    }

    if ((ip = getbbnam (bp->bb_name)) == NULL) {
	lkfclose (fp, bp->bb_info);
	return lose ("unable to get information on BBoard %s", bp->bb_name);
    }
    strcpy (bb_time, dtimenow ());
    sprintf (bb_head, "BBoard-ID: %d\nBB-Posted: %s\n",
	    bp->bb_maxima = ++ip->bb_maxima, bb_time);

    fprintf (fp, "%d\n%s\n", bp->bb_maxima, bb_time);
    lkfclose (fp, bp->bb_info);

    return OK;
}
#endif	not SPOP


#ifdef DISTRIBUTE
static void
distribute (int fd)
{
    int i;
    register struct bboard  *bp;

    for (i = 0; bp = bb[i]; i++)
	if (bp->bb_dist && *bp->bb_dist)
	    break;
    if (bp == NULL)
	return;

    if (dst_init () == NOTOK) {
	dst_lose ();
	return;
    }
    for (i = 0; bp = bb[i]; i++)
	if (bp->bb_dist && *bp->bb_dist)
	    if (dst_adrs (bp) == NOTOK) {
		dst_lose ();
		return;
	    }
    if (dst_text (fd) == NOTOK || dst_end () == NOTOK)
	dst_lose ();
}


static int
dst_init (void)
{
    int retval;

    if (rp_isbad (retval = sm_init (NULL, NULL, 0, 0, 0, 0, 0))
	    || rp_isbad (retval = sm_winit (S_MAIL, bb_from)))
	return lose ("problem initializing SendMail; %s",
		rp_string (retval));

    return OK;
}


static int
dst_adrs (struct bboard *bp)
{
    if (getbbdist (bp, dst_rcpt))
	return lose ("getbbdist failed: %s", getbberr ());

    return OK;
}


static int
dst_rcpt (char *mbox, char *host)
{
    int retval;

    switch (retval = sm_wadr (mbox, host, NULL)) {
	case RP_OK:
	    return OK;

	case RP_NO:
	case RP_USER:
	    lose ("%s@%s: loses; %s", mbox, host, rp_string (retval));
	    return OK;		/* fail-soft */

	default:
	    return lose ("%s@%s: unexpected response; %s",
		    mbox, host, rp_string (retval));
    }
}


static int
dst_text (int fd)
{
    int i, retval;
    char buffer[BUFSIZ];

    if (rp_isbad (retval = sm_waend ()))
	return lose ("problem ending addresses; %s", rp_string (retval));

    lseek (fd, (off_t)0, 0);
    while ((i = read (fd, buffer, sizeof buffer)) > 0)
	if (rp_isbad (retval = sm_wtxt (buffer, i)))
	    return lose ("problem writing text; %s", rp_string (retval));

    return (i != NOTOK ? OK : lose ("error reading from file"));
}


static int
dst_end (void)
{
    int retval;

    switch (retval = sm_wtend ()) {
	case RP_OK:
	    sm_end (OK);
	    return OK;

	case RP_NO:
	case RP_NDEL:
	    return lose ("posting failed; %s", rp_string (retval));

	default:
	    return lose ("unexpected response; %s", rp_string (retval));
    }
}


static void
dst_lose (void)
{
    sm_end (NOTOK);
}


static int
lose (char *fmt, ...)
{
    va_list ap;
    int fd, i;
    char *bp, buffer[BUFSIZ];

    if (bb_fderr == NOTOK) {
	if ((fd = open ("/dev/null", 0)) == NOTOK)
	    adios (EX_OSERR, "/dev/null", "unable to open");
	bb_fderr = copyfile (fd, bb_rept);
    }

    va_start(ap, fmt);
    vsprintf (buffer, fmt, ap);
    va_end(ap);

    bp = buffer;
    bp += strlen (bp);
    bp += strlen (strcpy(bp, "\n"));
    i = bp - buffer;
    if (write (bb_fderr, buffer, i) != i)
	adios (EX_IOERR, bb_rept, "error writing");

    return NOTOK;
}


static void
notify (int fd)
{
    int i;
    char buffer[BUFSIZ];

    if (bb_fderr == NOTOK)
	return;

    if (rp_isbad (sm_init (NULL, NULL, 0, 0, 0, 0, 0))
	    || rp_isbad (sm_winit (S_MAIL, bb_from)))
	goto sm_err;

    switch (sm_wadr (bb_from, NULL, NULL)) {
	case RP_OK:
	    for (i = 0; bb[i]; i++) {
		sprintf (buffer, "local-%s-request", bb[i]->bb_name);
		sm_wadr (buffer, LocalName (), NULL);
	    }
	    break;

	default:
	    goto sm_err;
    }

    if (rp_isbad (sm_waend ()))
	goto sm_err;

    sprintf (buffer,
	    "Date: %s\nFrom: %s\nTo: %s\nSubject: BBoards Failure\n\n",
	    dtimenow (), bb_from, bb_from);
    if (rp_isbad (sm_wtxt (buffer, strlen (buffer))))
	goto sm_err;

    for (i = 0; bb[i]; i++) {
	sprintf (buffer, "BBoard %s\n", bb[i]->bb_name);
	if (rp_isbad (sm_wtxt (buffer, strlen (buffer))))
	    goto sm_err;
    }

    lseek (bb_fderr, (off_t)0, 0);
    while ((i = read (bb_fderr, buffer, sizeof buffer)) > 0)
	if (rp_isbad (sm_wtxt (buffer, i)))
	    goto sm_err;

    strcpy (buffer, "\n------- Forwarded Message\n\n");
    if (rp_isbad (sm_wtxt (buffer, strlen (buffer))) || encap (fd) == NOTOK)
	goto sm_err;
    strcpy (buffer, "\n------- End of Forwarded Message\n\n");
    if (rp_isbad (sm_wtxt (buffer, strlen (buffer))))
	goto sm_err;

    switch (sm_wtend ()) {
	case RP_OK:
	    unlink (bb_rept);
	    sm_end (OK);
	    return;

	default:
    sm_err: ;
	    adios (EX_UNAVAILABLE, NULL,
		    "failed and unable to post advisory, see %s for details",
		    bb_rept);
    }
}


/* very similar to sbr/cpydgst.c */

#define	S1	0
#define	S2	1

#define	output(c)	if (bp >= dp) flush (), *bp++ = c; else *bp++ = c
#define	flush()		if ((j = bp - outbuf) \
				&& rp_isbad (sm_wtxt (outbuf, j))) \
			    return NOTOK; \
			else \
			    bp = outbuf

static int
encap (int fd)
{
    register int i, state;
    register char *cp, *ep;
    char buffer[BUFSIZ];
    register int j;
    register char *bp, *dp;
    char outbuf[BUFSIZ];

    lseek (fd, (off_t)0, 0);

    dp = (bp = outbuf) + sizeof outbuf;
    for (state = S1; (i = read (fd, buffer, sizeof buffer)) > 0;)
	for (ep = (cp = buffer) + i; cp < ep; cp++) {
	    if (*cp == NULL)
		continue;
	    switch (state) {
		case S1: 
		    if (*cp == '-') {
			output ('-');
			output (' ');
		    }
		    state = S2;	/* fall */

		case S2: 
		    output (*cp);
		    if (*cp == '\n')
			state = S1;
		    break;
	    }
	}

    if (i == NOTOK)
	return NOTOK;
    flush ();

    return OK;
}
#endif	DISTRIBUTE


#ifndef	DISTRIBUTE

static int
lose (char *fmt, ...)
{
    adios (EX_UNAVAILABLE, NULL, fmt, ap);
}
#endif	not DISTRIBUTE


static void
arginit (char **vec)
{
    register int i;
    char addr[BUFSIZ];
    register struct bboard *bp;
    register struct passwd *pw;
#ifdef	MHMTS
    register char *ap;
#endif	MHMTS

    invo_name = r1bindex (*vec++, '/');
    m_foil (NULL);
    mts_init (invo_name);

#ifndef	SPOP
    if ((pw = getpwnam (BBOARDS)) == NULL)
	adios (EX_OSFILE, NULL, "no entry for ~%s", BBOARDS);
#else	SPOP
    if ((pw = getpwnam (POPUID)) == NULL || !setpwinfo (pw, POPDB, 1))
	adios (EX_OSFILE, NULL, "%s", pw ? getbberr () : "POP user-id unknown");
#endif	SPOP

    if (pw->pw_uid != geteuid ())
#ifndef SPOP
    adios (EX_OSERR, NULL, "not running setuid to %s", BBOARDS);
#else	SPOP
    adios (EX_OSERR, NULL, "not running setuid to %s", POPUID);
#endif	SPOP

    bb_uid = pw->pw_uid;
    bb_gid = pw->pw_gid;
#ifndef	SPOP
    strcpy (bb_from, adrsprintf (pw->pw_name, LocalName ()));
    strcpy (bb_home, pw->pw_dir);
#endif	not SPOP

#ifdef	MHMTS
    vec += 3;
#endif	MHMTS
    if (*vec == NULL)
	adios (EX_USAGE, NULL, "usage: %s %s [%s ...]",
		invo_name, ENTITY, ENTITY);

    for (i = 0; *vec; vec++) {
#ifdef	MHMTS
	if (ap = strchr (*vec, '.'))
	    *vec = ++ap;
#endif	MHMTS
	make_lower (addr, *vec);

	if ((bp = getbbnam (addr)) == NULL
		&& (bp = getbbaka (addr)) == NULL)
	    adios (EX_NOUSER, NULL, "no such %s as %s", ENTITY, *vec);
	if ((bb[i++] = getbbcpy (bp)) == NULL)
	    adios (EX_UNAVAILABLE, NULL, "insufficient memory on %s", *vec);

	if (i >= NBB - 1)
	    adios (EX_USAGE, NULL, "too many %ss, starting with %s", ENTITY, *vec);
    }
    bb[i] = NULL;

    umask (0022);

    bb_fderr = NOTOK;
}


static int
copyfile (int qd, char *tmpfil)
{
    int i, fd;
    char buffer[BUFSIZ];

    strcpy (tmpfil, m_tmpfil (invo_name));
    if ((fd = creat (tmpfil, 0600)) == NOTOK)
	adios (EX_CANTCREAT, tmpfil, "unable to create");
    close (fd);
    if ((fd = open (tmpfil, 2)) == NOTOK)
	adios (EX_NOINPUT, tmpfil, "unable to re-open");

    lseek (qd, (off_t)0, 0);
    while ((i = read (qd, buffer, sizeof buffer)) > 0)
	if (write (fd, buffer, i) != i)
	    adios (EX_IOERR, tmpfil, "error writing");
    if (i == NOTOK)
	adios (EX_IOERR, "input", "error reading");

    lseek (fd, (off_t)0, 0);

    return fd;
}


static void
adios (int code, char *what, char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    advertise (what, NULL, fmt, ap);
    va_end(ap);

#ifdef SENDMTS
    done (code);
#endif
#ifdef MHMTS
    done (1);
#endif
}
