/* :ts=4
 * Implement the Usenet ihave/sendme control messages,
 * as per RFC 1036 (nee 850).
 *
 *	$Log$
 */

#include <stdio.h>
#include <ctype.h>
#ifdef unix
# include <sys/types.h>
#endif /* unix */

#include "libc.h"
#include "news.h"
#include "config.h"
#include "headers.h"
#include "article.h"
#include "history.h"
#include "fgetmfs.h"
#include "msgs.h"
#include "transmit.h"

#ifndef SENDMEDISTR
#define SENDMEDISTR "sendme"	/* kludge: distinguished dist for sendmes */
#endif
#ifndef IHAVEDISTR
#define IHAVEDISTR "ihave"	/* kludge: distinguished dist for ihaves */
#endif
#ifndef AVEARTSIZE
#define AVEARTSIZE 3000
#endif

#define PROTO_IHAVE 0
#define PROTO_SENDME 1

/* static forwards */
FORWARD void doproto(), procmsgids(), procbodymsgids();
FORWARD statust faketrans();

/*
 * Read message-IDs from args or control message body,
 * look them up in history, post a sendme to to.remotesys (via the batcher
 * to avoid deadlock) consisting of the message-IDs not in history.
 * The "posting" consists of transmitting to a system matching
 * "to.remotesys" in sys, which had better have the I flag on.
 * ihave message-ID-list remotesys	generate a sendme from message-ID-list
 */
void
ihave(args, art)
char *args;
struct article *art;
{
	doproto(args, art, IHAVEDISTR, PROTO_IHAVE);
}

/*
 * Read message-IDs from args or control message body,
 * transmit the corresponding articles to a system matching
 * "to.remotesys/sendme" in sys, which will typically name a batch file.
 * sendme message-ID-list remotesys	send articles named to remotesys
 */
void
sendme(args, art)
char *args;
struct article *art;
{
	doproto(args, art, SENDMEDISTR, PROTO_SENDME);
}

static void
doproto(args, art, distr, proto)
char *args;
register struct article *art;
char *distr;
int proto;
{
	register char *argscp = skipsp(args), *remotesys;

	if (*argscp == '\n' || *argscp == '\0')	/* no args */
		return;

	argscp = strsave(argscp);

	/* dig out the remote system name */
	remotesys = strrchr(argscp, ' ');	
	if (remotesys == NULL)			/* no msg-ids in command */
		remotesys = argscp;
	else {
		remotesys = argscp + strlen(argscp) - 1;	/* last byte */
		while (isascii(*remotesys) && isspace(*remotesys))
			*remotesys-- = '\0';	/* back up to non-whitespace */
		remotesys = strrchr(argscp, ' ');
		if (remotesys == NULL)		/* no msg-ids in command */
			remotesys = argscp;
		else
			*remotesys++ = '\0';	/* separate msg-ids & sys name */
	}
	if (strcmp(remotesys, hostname()) != 0)	/* remotesys may not be me */
		if (remotesys != argscp)	/* msg-ids in command */
			procmsgids(art, argscp, remotesys, distr, proto);
		else
			procbodymsgids(art, remotesys, distr, proto);
	free(argscp);
}

/*
 * Process a list of message-ids in msgidln: look them up
 * and "transmit" the articles to to.remotesys.
 */
static void
procmsgids(art, msgidln, remotesys, distr, proto)
struct article *art;
char *msgidln, *remotesys, *distr;
int proto;
{
	char *cpmsgid = strsave(skipsp(msgidln));
	register char *msgid = cpmsgid, *endmsgid;
	register int save, sendit;

	for (; *msgid != '\n' && *msgid != '\0'; msgid = skipsp(endmsgid)) {
		for (endmsgid = msgid; *endmsgid != '\0' &&
		    isascii(*endmsgid) && !isspace(*endmsgid); ++endmsgid)
			;			/* skip msgid */

		save = *endmsgid;
		*endmsgid = '\0';		/* terminate msgid at whitespace */
		if (proto == PROTO_IHAVE)
			sendit = !alreadyseen(msgid);	/* sendme from remotesys */
		else
			sendit = alreadyseen(msgid);	/* sendme to remotesys */
		if (sendit)
			art->a_status |= faketrans(msgid, remotesys, distr, proto);
		*endmsgid = save;
	}
	free(cpmsgid);
}

static void
procbodymsgids(art, remotesys, distr, proto)
register struct article *art;
char *remotesys, *distr; 
int proto;
{
	register FILE *arttext;

	arttext = fopenwclex(art->a_tmpf, "r");
	if (arttext != NULL) {
		char *line;

		while ((line = fgetms(arttext)) != NULL && *line != '\n')
			nnfree(&line);		/* skip header */
		if (line != NULL) {		/* article body exists */
			nnfree(&line);		/* toss blank separating line */
			while ((line = fgetms(arttext)) != NULL) {
				procmsgids(art, line, remotesys, distr, proto);
				nnfree(&line);
			}
		}
		(void) nfclose(arttext);
	}
}

/*
 * Fake up a minimal article struct for msgid using group to.remotesys and
 * distribution distr, then invoke transmit().  If there is a history
 * entry for msgid, supply the list of file names too.
 * Generate a log entry for each message-id transmitted.
 */
static statust
faketrans(msgid, remotesys, distr, proto)
register char *msgid, *remotesys;
char *distr;
int proto;
{
	struct article fakeart;
	register char *ng;
	register char *histent;
	register struct article *fap = &fakeart;
	time_t now;
	statust status = ST_OKAY;

	ng = nemalloc((unsigned)(strlen("to.") + strlen(remotesys) + SIZENUL));
	(void) strcpy(ng, "to.");
	(void) strcat(ng, remotesys);

	artinit(fap);

	fap->h.h_ngs = ng;
	fap->h.h_distr = (distr != NULL? distr: ng);
	fap->h.h_msgid = msgid;
	fap->h.h_path = hostname();
	fap->a_charswritten = AVEARTSIZE;
	histent = gethistory(msgid);
	if (histent == NULL || (fap->a_files = findfiles(histent)) == NULL)
		fap->a_files = "no.such.article!";

	timestamp(stdout, &now);	/* start log line */
	if (printf(" %s %c %s", sendersite(nullify(fap->h.h_path)),
	    (proto == PROTO_IHAVE? 'i': 's'), fap->h.h_msgid) == EOF)
		fulldisk(fap, "stdout");
	transmit(fap, "");	/* write on batch file; write sys name on stdout */
	(void) putchar('\n');		/* end log line */
	status |= fap->a_status;	/* pass back failure writing batch file */

	fap->h.h_ngs = NULL;
	fap->h.h_distr = NULL;
	fap->h.h_msgid = NULL;
	fap->h.h_path = NULL;
	fap->a_files = NULL;
	artfree(fap);

	free(ng);
	return status;
}
