
/*
 * forw.c -- forward messages
 *
 * $Id$
 */

#include <h/mh.h>
#include <h/formatsbr.h>
#include <zotnet/tws/tws.h>

#ifndef	MIME
# define MIMEminc(a) (a)
#else
# define MIMEminc(a)  0
#endif

#define	IFORMAT	"digest-issue-%s"
#define	VFORMAT	"digest-volume-%s"

static struct swit switches[] = {
#define	ANNOSW                 0
    { "annotate", 0 },
#define	NANNOSW                1
    { "noannotate", 0 },
#define	DFOLDSW                2
    { "draftfolder +folder", 0 },
#define	DMSGSW                 3
    { "draftmessage msg", 0 },
#define	NDFLDSW                4
    { "nodraftfolder", 0 },
#define	EDITRSW                5
    { "editor editor", 0 },
#define	NEDITSW                6
    { "noedit", 0 },
#define	FILTSW                 7
    { "filter filterfile", 0 },
#define	FORMSW                 8
    { "form formfile", 0 },
#define	FRMTSW                 9
    { "format", 5 },
#define	NFRMTSW               10
    { "noformat", 7 },
#define	INPLSW                11
    { "inplace", 0 },
#define	NINPLSW               12
    { "noinplace", 0 },
#define	MIMESW                13
    { "mime", MIMEminc(-4) },
#define	NMIMESW               14
    { "nomime", MIMEminc(-6) },
#define	DGSTSW                15
    { "digest list", 0 },
#define	ISSUESW               16
    { "issue number", 0 },
#define	VOLUMSW               17
    { "volume number", 0 },
#define	WHATSW                18
    { "whatnowproc program", 0 },
#define	NWHATSW               19
    { "nowhatnowproc", 0 },
#define VERSIONSW             20
    { "version", 0 },
#define	HELPSW                21
    { "help", 4 },
#define	FILESW                22
    { "file file", -4 },		/* interface from msh */
#define	DASHSW                23
    { "dashmunging", -4 },		/* interface to mhl */
#define	NDASHSW               24
    { "nodashmunging", -6 },

#ifdef MHE
#define	BILDSW                25
    { "build", -5 },			/* interface from mhe */
#endif /* MHE */

    { NULL, 0 }
};

static struct swit aqrnl[] = {
#define	NOSW         0
    { "quit", 0 },
#define	YESW         1
    { "replace", 0 },
#define	LISTDSW      2
    { "list", 0 },
#define	REFILSW      3
    { "refile +folder", 0 },
#define NEWSW        4
    { "new", 0 },
    { NULL, 0 }
};

static struct swit aqrl[] = {
    { "quit", 0 },
    { "replace", 0 },
    { "list", 0 },
    { "refile +folder", 0 },
    { NULL, 0 }
};

static char drft[BUFSIZ];

static char delim3[] =
    "\n------------------------------------------------------------\n\n";
static char delim4[] = "\n------------------------------\n\n";


static struct msgs *mp = NULL;		/* used a lot */


/*
 * static prototypes
 */
static int build_form (char *, char *, int, int);
static copy_draft (int, char *, char *, int, int, int);
static void mhl_draft(int, char *, int, int, char *, char *, int);


main (int argc, char **argv)
{
    int msgp = 0, anot = 0, inplace = 0, mime = 0;
    int issue = 0, volume = 0, dashflg = 1;
    int nedit = 0, nwhat = 0, i, in;
    int out, isdf = 0, msgnum;
    char *cp, *cwd, *maildir, *dfolder = NULL;
    char *dmsg = NULL, *digest = NULL, *ed = NULL;
    char *file = NULL, *filter = NULL, *folder = NULL;
    char *form = NULL, buf[100], value[10], **ap;
    char **argp, *arguments[MAXARGS], *msgs[MAXARGS];
    struct stat st;

#ifdef	MHE
    int buildsw = 0;
#endif	/* MHE */

#ifdef LOCALE
	setlocale(LC_ALL, "");
#endif
    invo_name = r1bindex (argv[0], '/');
    if ((cp = m_find (invo_name)) != NULL) {
	ap = brkstring (cp = getcpy (cp), " ", "\n");
	ap = copyip (ap, arguments);
    }
    else
	ap = arguments;
    copyip (argv + 1, ap);
    argp = arguments;

    while (cp = *argp++) {
	if (*cp == '-')
	    switch (smatch (++cp, switches)) {
		case AMBIGSW: 
		    ambigsw (cp, switches);
		    done (1);
		case UNKWNSW: 
		    adios (NULL, "-%s unknown", cp);

		case HELPSW: 
		    sprintf (buf, "%s [+folder] [msgs] [switches]", invo_name);
		    print_help (buf, switches);
		    done (1);
		case VERSIONSW:
		    print_version(invo_name);
		    done (1);

		case ANNOSW: 
		    anot++;
		    continue;
		case NANNOSW: 
		    anot = 0;
		    continue;

		case EDITRSW: 
		    if (!(ed = *argp++) || *ed == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    nedit = 0;
		    continue;
		case NEDITSW:
		    nedit++;
		    continue;

		case WHATSW: 
		    if (!(whatnowproc = *argp++) || *whatnowproc == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    nwhat = 0;
		    continue;
#ifdef MHE
		case BILDSW:
		    buildsw++;	/* fall... */
#endif /* MHE */
		case NWHATSW: 
		    nwhat++;
		    continue;

		case FILESW: 
		    if (file)
			adios (NULL, "only one file at a time!");
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    file = path (cp, TFILE);
		    continue;
		case FILTSW:
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    filter = getcpy (etcpath (cp));
		    mime = 0;
		    continue;
		case FORMSW: 
		    if (!(form = *argp++) || *form == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    continue;

		case FRMTSW:
		    filter = getcpy (etcpath (mhlforward));
		    continue;
		case NFRMTSW:
		    filter = NULL;
		    continue;

		case INPLSW: 
		    inplace++;
		    continue;
		case NINPLSW: 
		    inplace = 0;
		    continue;

		case MIMESW:
#ifdef MIME
		    mime++;
		    filter = NULL;
#endif
		    continue;
		case NMIMESW: 
		    mime = 0;
		    continue;

		case DGSTSW: 
		    if (!(digest = *argp++) || *digest == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    mime = 0;
		    continue;
		case ISSUESW:
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    if ((issue = atoi (cp)) < 1)
			adios (NULL, "bad argument %s %s", argp[-2], cp);
		    continue;
		case VOLUMSW:
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    if ((volume = atoi (cp)) < 1)
			adios (NULL, "bad argument %s %s", argp[-2], cp);
		    continue;

		case DFOLDSW: 
		    if (dfolder)
			adios (NULL, "only one draft folder at a time!");
		    if (!(cp = *argp++) || *cp == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
				    *cp != '@' ? TFOLDER : TSUBCWF);
		    continue;
		case DMSGSW:
		    if (dmsg)
			adios (NULL, "only one draft message at a time!");
		    if (!(dmsg = *argp++) || *dmsg == '-')
			adios (NULL, "missing argument to %s", argp[-2]);
		    continue;
		case NDFLDSW: 
		    dfolder = NULL;
		    isdf = NOTOK;
		    continue;

		case DASHSW: 
		    dashflg++;
		    continue;
		case NDASHSW: 
		    dashflg = 0;
		    continue;
	    }
	if (*cp == '+' || *cp == '@') {
	    if (folder)
		adios (NULL, "only one folder at a time!");
	    else
		folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
	}
	else
	    msgs[msgp++] = cp;
    }

    cwd = getcpy (pwd ());

    if (!m_find ("path"))
	free (path ("./", TFOLDER));
    if (file && (msgp || folder))
	adios (NULL, "can't mix files and folders/msgs");

try_it_again: ;
#ifndef MHE
    strcpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf));
    if (stat (drft, &st) != NOTOK) {
#else	/* MHE */
    strcpy (drft, buildsw ? m_maildir ("draft")
			  : m_draft (dfolder, NULL, NOUSE, &isdf));
    if (!buildsw && stat (drft, &st) != NOTOK) {
#endif	/* MHE */
	printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
	for (i = LISTDSW; i != YESW;) {
	    if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
		done (1);
	    switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
		case NOSW: 
		    done (0);
		case NEWSW: 
		    dmsg = NULL;
		    goto try_it_again;
		case YESW: 
		    break;
		case LISTDSW: 
		    showfile (++argp, drft);
		    break;
		case REFILSW: 
		    if (refile (++argp, drft) == 0)
			i = YESW;
		    break;
		default: 
		    advise (NULL, "say what?");
		    break;
	    }
	}
    }

    if (file) {
	anot = 0;
	goto go_to_it;
    }

    if (!msgp)
	msgs[msgp++] = "cur";
    if (!folder)
	folder = m_getfolder ();
    maildir = m_maildir (folder);

    if (chdir (maildir) == NOTOK)
	adios (maildir, "unable to change directory to");
    if (!(mp = m_gmsg (folder)))
	adios (NULL, "unable to read folder %s", folder);
    if (mp->hghmsg == 0)
	adios (NULL, "no messages in %s", folder);

    for (msgnum = 0; msgnum < msgp; msgnum++)
	if (!m_convert (mp, msgs[msgnum]))
	    done (1);
    m_setseq (mp);

go_to_it: ;
    if (filter && access (filter, R_OK) == NOTOK)
	adios (filter, "unable to read");

    if (digest) {
	if (issue == 0) {
	    sprintf (buf, IFORMAT, digest);
	    if (volume == 0
		    && (cp = m_find (buf))
		    && ((issue = atoi (cp)) < 0))
		issue = 0;
	    issue++;
	}
	if (volume == 0)
	    sprintf (buf, VFORMAT, digest);
	    if ((cp = m_find (buf)) == NULL || (volume = atoi (cp)) <= 0)
		volume = 1;
	if (!form)
	    form = digestcomps;
	in = build_form (form, digest, volume, issue);
    }
    else
	if (form) {
	    if ((in = open (etcpath (form), 0)) == NOTOK)
		adios (form, "unable to open form file");
	}
	else {
	    if ((in = open (etcpath (forwcomps), 0)) == NOTOK)
		adios (forwcomps, "unable to open default components file");
	    form = forwcomps;
	}

    if ((out = creat (drft, m_gmprot ())) == NOTOK)
	adios (drft, "unable to create");

    cpydata (in, out, form, drft);
    close (in);

    if (file) {
	if ((in = open (file, 0)) == NOTOK)
	    adios (file, "unable to open");
	cpydata (in, out, file, drft);
	close (in);
	close (out);
	goto edit_it;
    }

    if (filter)
	mhl_draft (out, digest, volume, issue, drft, filter, dashflg);
    else
	copy_draft (out, digest, drft, volume, issue, mime);
    close (out);

    if (digest) {
	sprintf (buf, IFORMAT, digest);
	sprintf (value, "%d", issue);
	m_replace (buf, getcpy (value));
	sprintf (buf, VFORMAT, digest);
	sprintf (value, "%d", volume);
	m_replace (buf, getcpy (value));
    }

    m_replace (pfolder, folder);
    if (mp->lowsel != mp->curmsg)
	m_setcur (mp, mp->lowsel);
    m_sync (mp);
    m_update ();

edit_it: ;
    if (nwhat)
	done (0);
    what_now (ed, nedit, NOUSE, drft, NULL, 0, mp,
	anot ? "Forwarded" : NULL, inplace, cwd);
    done (1);
}


static void
mhl_draft  (int out, char *digest, int volume, int issue,
            char *file, char *filter, int dashflg)
{
    pid_t child_id;
    int i, msgnum, pd[2];
    char *vec[MAXARGS];
    char buf1[BUFSIZ];
    char buf2[BUFSIZ];
    
    if (pipe (pd) == NOTOK)
	adios ("pipe", "unable to create");

    vec[0] = r1bindex (mhlproc, '/');

    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
	sleep (5);
    switch (child_id) {
	case NOTOK: 
	    adios ("fork", "unable to");

	case OK: 
	    close (pd[0]);
	    dup2 (pd[1], 1);
	    close (pd[1]);

	    i = 1;
	    vec[i++] = "-forwall";
	    vec[i++] = "-form";
	    vec[i++] = filter;
	    if (digest) {
		vec[i++] = "-digest";
		vec[i++] = digest;
		vec[i++] = "-issue";
		sprintf(buf1, "%d", issue); vec[i++] = buf1;
		vec[i++] = "-volume";
		sprintf(buf2, "%d", volume); vec[i++] = buf2;
	    }
	    vec[i++] = dashflg ? "-dashmunging" : "-nodashmunging";
	    if (mp->numsel >= MAXARGS - i)
		adios (NULL, "more than %d messages for %s exec",
			vec[0], MAXARGS - i);
	    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
		if (mp->msgstats[msgnum] & SELECTED)
		    vec[i++] = getcpy (m_name (msgnum));
	    vec[i] = NULL;

	    execvp (mhlproc, vec);
	    fprintf (stderr, "unable to exec ");
	    perror (mhlproc);
	    _exit (-1);

	default: 
	    close (pd[1]);
	    cpydata (pd[0], out, vec[0], file);
	    close (pd[0]);
	    pidXwait(child_id, mhlproc);
	    break;
    }
}


static
copy_draft (int out, char *digest, char *file, int volume, int issue, int mime)
{
    int fd,i, msgcnt, msgnum;
    register char *bp, *msgnam;
    char buffer[BUFSIZ];

#ifdef MIME
    if (mime) {
	sprintf (buffer, "#forw [] +%s", mp->foldpath);
	write (out, buffer, strlen (buffer));
	for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
	    if (mp->msgstats[msgnum] & SELECTED) {
		sprintf (buffer, " %s", m_name (msgnum));
		write (out, buffer, strlen (buffer));
	    }
	write (out, "\n", 1);

	return;
    }
#endif /* MIME */

    msgcnt = 1;
    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
	if (mp->msgstats[msgnum] & SELECTED) {
	    if (digest)
		strcpy (buffer, msgnum == mp->lowsel ? delim3 : delim4);
	    else {
		strcpy (bp = buffer, "\n-------"), bp += strlen (bp);
		if (msgnum == mp->lowsel)
		    sprintf (bp, " Forwarded Message%s", mp->numsel > 1 ? "s" : "");
		else
		    sprintf (bp, " Message %d", msgcnt);
		bp += strlen (bp);
		strcpy (bp, "\n\n");
	    }
	    write (out, buffer, strlen (buffer));

	    if ((fd = open (msgnam = m_name (msgnum), 0)) == NOTOK) {
		admonish (msgnam, "unable to read message");
		continue;
	    }
	    cpydgst (fd, out, msgnam, file);
	    close (fd);

	    msgcnt++;
	}

    if (digest)
	strcpy (buffer, delim4);
    else
	sprintf (buffer, "\n------- End of Forwarded Message%s\n\n",
		mp->numsel > 1 ? "s" : "");
    write (out, buffer, strlen (buffer));

    if (digest) {
	sprintf (buffer, "End of %s Digest [Volume %d Issue %d]\n", digest, volume, issue);
	i = strlen (buffer);
	for (bp = buffer + i; i > 1; i--)
	    *bp++ = '*';
	*bp++ = '\n';
	*bp = 0;
	write (out, buffer, strlen (buffer));
    }
}


static int
build_form (char *form, char *digest, int volume, int issue)
{
    int	in;
    int fmtsize;
    register char *nfs;
    char *line, tmpfil[BUFSIZ];
    register FILE *tmp;
    register struct comp *cptr;
    struct format *fmt;
    int dat[5];

    nfs = new_fs (form, NULL, NULL);
    fmtsize = strlen (nfs) + 256;
    fmt_compile (nfs, &fmt);

    FINDCOMP (cptr, "digest");
    if (cptr)
	cptr->c_text = digest;
    FINDCOMP (cptr, "date");
    if (cptr)
	cptr->c_text = getcpy(dtimenow ());

    dat[0] = issue;
    dat[1] = volume;
    dat[2] = 0;
    dat[3] = fmtsize;
    dat[4] = 0;

    strcpy (tmpfil, m_tmpfil (invo_name));
    if ((tmp = fopen (tmpfil, "w+")) == NULL)
	adios (tmpfil, "unable to create");
    unlink (tmpfil);
    if ((in = dup (fileno (tmp))) == NOTOK)
	adios ("dup", "unable to");

    if ((line = malloc ((unsigned) fmtsize)) == NULL)
	adios (NULL, "unable to allocate format line storage");
    fmtscan (fmt, line, fmtsize, dat);
    fputs (line, tmp);
    free (line);
    if (fclose (tmp))
	adios (tmpfil, "error writing");

    lseek (in, (off_t) 0, 0);
    return in;
}
