/* $Id: reply.c,v 1.59 2015/09/29 13:17:42 onoe Exp $ */

/*-
 * Copyright (c) 1998-2001 Atsushi Onoe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <utime.h>

#include "cue.h"

#ifndef MALIAS
int (*malias_completion)(char *candidate, void (*callback)(void *arg, char *entry), void *arg) = NULL;
#endif

static struct clist **
repl_split_addr(ccbuf_t *buf, struct clist **cc)
{
	const char *p, *ep;
	cbuf_t abuf;

	p = buf->ptr;
	if (p == NULL)
		return cc;
	ep = p + buf->len;

	while (p < ep) {
		while (*p == ' ' || *p == '\t' || *p == '\n')
			p++;
		p = message_parse_addr(p, ep, (ccbuf_t *)&abuf);
		if (abuf.len > 2
		&&  strncmp(":;", abuf.ptr + abuf.len - 2, 2) == 0)
			continue;
		if (abuf.ptr != NULL) {
			*cc = malloc(sizeof(**cc));
			(*cc)->buf = abuf;
			cc = &(*cc)->next;
			*cc = NULL;
		}
	}
	return cc;
}

static void
repl_collect_addr(struct filedb *fdb, struct clist **to, struct clist **cc)
{
	int i;
	struct header *hdr;
	ccbuf_t *cp;

	cp = &fdb->hdr_val[HT_REPLYTO];
	if (cp->ptr == NULL)
		cp = &fdb->hdr_val[HT_FROM];
	(void)repl_split_addr(cp, to);

	for (i = 0, hdr = fdb->hdr; i < fdb->hdrs; i++, hdr++) {
		switch (hdr->type->type) {
		case HT_TO:
		case HT_CC:
			cc = repl_split_addr(&hdr->val, cc);
			break;
		default:
			break;
		}
	}
}

static void
repl_yank(struct state *state, int mode, FILE *fp)
{
	int i, skip, pmore, more;
	struct filedb *fdb;
	ccbuf_t *cb;
	char *quote, *quote2;

	fdb = state->message;
	if (mode == COMP_REPLORIG) {
		fprintf(fp, "----- Original Message -----\n");
		skip = fdb->skip_lines;
		quote = quote2 = "";
	} else {
		skip = fdb->hdr_lines;
		quote = "> ";
		quote2 = ">";
	}
	for (i = 0, pmore = 0; (cb = fdb_read(fdb, i)) != NULL; i++) {
		if (i < skip)
			continue;
		more = fdb_ismore(fdb, i);
		if (!pmore)
			fprintf(fp, "%s", *CP(cb) != '>' ? quote : quote2);
		print_jis(fp, "%.*s%s", CL(cb), CP(cb), (more ? "" : "\n"));
		pmore = more;
	}
}

static void
draft_mailer(FILE *fp)
{
	extern char version[], dev[];

	fprintf(fp, "X-Mailer: %s", version);
	if (dev[0])
		fprintf(fp, " (%s)", dev);
	fprintf(fp, "\n");
}

static void
anno_mod(struct state *state, char *draft_path, char *afile, char *msgid, char *anno)
{
	struct clist *cc, *ncc, **ccp;
	struct filedb *fdb;
	struct folder *fl;
	struct msginfo *msg;
	int i, n;
	FILE *ifp, *ofp;
	char tpath[MAXPATH];
	char buf[CHARBLOCK];
	char *p;
	int w;
	time_t now;
	struct tm *tm;
	char zofs;
	time_t zoff;
	struct stat stbuf;
	struct utimbuf utm;
	static char *wnm[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	static char *mnm[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
			      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

	if ((fdb = fdb_open(draft_path)) == NULL)
		return;
	if (state->nomime)
		fdb->flags |= FDB_NOMIME;
	/*XXX*/
	cc = NULL;
	ccp = &cc;
	ccp = repl_split_addr(&fdb->hdr_val[HT_TO], ccp);
	ccp = repl_split_addr(&fdb->hdr_val[HT_CC], ccp);
	if ((ifp = fopen(afile, "r")) == NULL)
		return;
	snprintf(tpath, sizeof(tpath), "%s.tmp", afile);
	if ((ofp = fopen(tpath, "w")) == NULL) {
		fclose(ifp);
		return;
	}
	(void)fstat(fileno(ifp), &stbuf);
	(void)fchmod(fileno(ofp), stbuf.st_mode & 0777);
	while (fgets(buf, sizeof(buf), ifp)) {
		if (buf[0] == '\n')
			break;
		if (msgid && strncasecmp(buf, "Message-Id:", 11) == 0) {
			for (p = buf + 11; *p; p++)
				if (*p != ' ' && *p != '\t')
					break;
			w = strlen(msgid);
			if (strncmp(p, msgid, w) != 0 || p[w] != '\n')
				break;
			msgid = NULL;
		}
		fputs(buf, ofp);
	}
	if (msgid) {
		/* not found or not match */
		fclose(ifp);
		fclose(ofp);
		(void)unlink(tpath);
		return;
	}
	time(&now);
	tm = localtime(&now);
	zoff = 9*60;	/*XXX: timezone */
	if (zoff < 0) {
		zofs = '-';
		zoff = -zoff;
	} else
		zofs = '+';
	fprintf(ofp, "%s: %s, %02d %s %04d %02d:%02d:%02d %c%02d%02d\n",
		anno,
		wnm[tm->tm_wday], tm->tm_mday, mnm[tm->tm_mon],
		tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec,
		zofs, (int)zoff / 60, (int)zoff % 60);
	fprintf(ofp, "%s: ", anno);
	w = strlen(anno) + 2;
	for (; cc; cc = ncc) {
		ncc = cc->next;
		fprintf(ofp, "%.*s", cc->buf.len, cc->buf.ptr);
		w += cc->buf.len + 2;
		if (!ncc)
			fprintf(ofp, "\n");
		else if (w + ncc->buf.len + 2 >= LINE_WIDTH) {
			fprintf(ofp, ",\n\t");
			w = 8;
		} else
			fprintf(ofp, ", ");
		free(cc);
	}
	putc('\n', ofp);
	while (fgets(buf, sizeof(buf), ifp)) {
		fputs(buf, ofp);
	}
	fclose(ifp);
	fclose(ofp);
	utm.actime = stbuf.st_atime;
	utm.modtime = stbuf.st_mtime;
	(void)utime(tpath, &utm);

	if ((p = strrchr(afile, '/'))) {
		snprintf(buf, sizeof(buf), "%.*s/#%s",
		    (int)(p - afile), afile, p + 1);
		(void)unlink(buf);
		if (link(afile, buf) == 0)
			(void)rename(tpath, afile);
		snprintf(buf, sizeof(buf), "+%.*s", (int)(p - afile), afile);
		n = strtoul(p + 1, NULL, 10);
		if ((fl = folder_open(buf, 1))) {
			for (i = 0, msg = fl->msg; i < fl->nmsg; i++, msg++) {
				if (msg->num == n) {
					if (msg->scan.ptr) {
						free(msg->scan.ptr);
						msg->scan.ptr = NULL;
						msg->scan.len = 0;
					}
					break;
				}
			}
		}
	}
}

void
exec_anno(struct state *state, char *draft_path, size_t draft_pathlen)
{
	char *suf, *p;
	FILE *fp;
	char buf[CHARBLOCK];
	char *anno, *file, *msgid;
	char **pp;
	int len;

	suf = draft_path + strlen(draft_path);
	len = draft_pathlen - strlen(draft_path);
	if (strlcpy(suf, SUF_INFO, len) >= len)
		return;
	if ((fp = fopen(draft_path, "r")) == NULL)
		return;
	anno = file = msgid = NULL;
	while (fgets(buf, sizeof(buf), fp)) {
		if (strncasecmp(buf, "Anno:", 5) == 0) {
			pp = &anno;
			len = 5;
		} else if (strncasecmp(buf, "File:", 5) == 0) {
			pp = &file;
			len = 5;
		} else if (strncasecmp(buf, "Message-Id:", 11) == 0) {
			pp = &msgid;
			len = 11;
		} else
			continue;
		if ((p = strchr(buf + len, '\n')))
			*p = '\0';
		for (p = buf + len; *p; p++)
			if (*p != ' ' && *p != '\t')
				break;
		*pp = strdup(p);
	}
	fclose(fp);
	if (anno == NULL || file == NULL || msgid == NULL) {
		goto end;
	}
	*suf = '\0';
	anno_mod(state, draft_path, file, msgid, anno);

  end:
	if (anno)
		free(anno);
	if (file)
		free(file);
	if (msgid)
		free(msgid);
}

void
compose(struct state *state)
{
	struct folder *fl;
	FILE *fp;
	char path[MAXPATH];
	struct stat stbuf;
	int n;
	char buf[CHARBLOCK];
	char *ofol;

	conf_update(state);
	ofol = state->folder->name.ptr;
	if ((fl = folder_open(DRAFT_FOLDER, 1)) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s does not exist", DRAFT_FOLDER);
		return;
	}
	if (fl->nmsg)
		n = fl->msg[fl->nmsg-1].num;
	else
		n = 0;
	do {
		snprintf(path, sizeof(path), "%s/%d", fl->name.ptr + 1, ++n);
	} while (stat(path, &stbuf) == 0);

	if ((fp = fopen(path, "w")) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s: %s", path, strerror(errno));
		return;
	}
	strlcpy(state->status, "To: ", sizeof(state->status));
	buf[0] = '\0';
	if (edit_stline(state, buf, sizeof(buf), malias_completion) == NULL) {
  quit:
		fclose(fp);
		(void)unlink(path);
		strlcpy(state->status, "Quit", sizeof(state->status));
		return;
	}
#ifdef MALIAS
	malias_expand(buf, sizeof(buf));
#endif
	print_jis(fp, "To: %s\n", buf);
	if (state->config.bcc)
		fprintf(fp, "Bcc: %s\n", state->config.bcc);
	strlcpy(state->status, "Subject: ", sizeof(state->status));
	buf[0] = '\0';
	if (edit_stline(state, buf, sizeof(buf), NULL) == NULL)
		goto quit;
	print_jis(fp, "Subject: %s\n", buf);
	draft_mailer(fp);
	fprintf(fp, "Mime-Version: 1.0\n");
	fprintf(fp, "\n");
	fclose(fp);

	strlcat(path, SUF_INFO, sizeof(path));
	(void)unlink(path);	/* no info is provided */

	state->folder = fl = folder_open(DRAFT_FOLDER, 1);
	fl->pos = fl->nmsg - 1;
	message_open(state, 1);
	if (state->message == NULL) {
		strlcpy(state->status, "Message not found", sizeof(state->status));
		return;
	}
	state->status[0] = '\0';
	if (mpart_edit(state, 0)) {
		folder_purge(state, state->message->msgnum);
		fdb_purge(state->message->name);
		state->message = NULL;
		message_open(state, 1);	/* reopen */
	} else {
		exec_del(fl, fl->pos);
		state->folder = folder_open(ofol, 1);
	}
}

void
reply(struct state *state, int mode)
{
	struct folder *fl;
	FILE *fp;
	char path[MAXPATH];
	struct stat stbuf;
	int i, n;
	struct header *hdr;
	ccbuf_t *mid, *dat, *subj, *cb, from;
	struct clist *to, *nto, *cc, *ncc, *pcc;
	struct filedb *fdb;
	struct filedb *ofdb;
	int w;
	char *ofol;

	conf_update(state);
	ofol = state->folder->name.ptr;

	if (strcmp(ofol, DRAFT_FOLDER) == 0) {
		strlcpy(state->status, "Cannot reply draft message", sizeof(state->status));
		return;
	}

	/* retrieve information from original mail */
	message_open(state, 0);
	if ((fdb = state->message) == NULL) {
		strlcpy(state->status, "No current message", sizeof(state->status));
		return;
	}
	ofdb = fdb;
	for (;;) {
		mid = &fdb->hdr_val[HT_MSGID];
		if (mid->ptr != NULL)
			break;
		if (fdb->uppart != NULL)
			fdb = fdb->uppart;
		if (ofdb == fdb) {
			strlcpy(state->status, "No Message ID", sizeof(state->status));
			return;
		}
	}
	dat = &fdb->hdr_val[HT_DATE];
	subj = &fdb->hdr_val[HT_SUBJECT];
	to = cc = NULL;
	repl_collect_addr(fdb, &to, &cc);

	/* strip me & to */
	for (ncc = cc, pcc = NULL; ncc; ) {
		if (conf_myaddr(state, (ccbuf_t *)&ncc->buf)) {
  stripme:
			if (pcc == NULL) {
				ncc = cc->next;
				free(cc);
				cc = ncc;
			} else {
				pcc->next = ncc->next;
				free(ncc);
				ncc = pcc->next;
			}
			continue;
		}
		for (nto = to; nto; nto = nto->next) {
			if (ncc->buf.len == nto->buf.len
			&&  strncasecmp(nto->buf.ptr, ncc->buf.ptr,
				    nto->buf.len) == 0)
				goto stripme;
		}
		pcc = ncc;
		ncc = ncc->next;
	}

	if ((fl = folder_open(DRAFT_FOLDER, 1)) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s does not exist", DRAFT_FOLDER);
		return;
	}
	if (fl->nmsg)
		n = fl->msg[fl->nmsg-1].num;
	else
		n = 0;
	do {
		snprintf(path, sizeof(path), "%s/%d", fl->name.ptr + 1, ++n);
	} while (stat(path, &stbuf) == 0);

	if ((fp = fopen(path, "w")) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s: %s", path, strerror(errno));
		return;
	}
	cb = &fdb->hdr_val[HT_FROM];
	(void)message_parse_addr(CP(cb), CE(cb), &from);
	if (from.ptr != NULL && conf_myaddr(state, &from)) {
		for (i = 0, hdr = fdb->hdr; i < fdb->hdrs; i++, hdr++) {
			switch (hdr->type->type) {
			case HT_TO:
			case HT_CC:
			case HT_REPLYTO:
				fprintf(fp, "%.*s\n", hdr->dbuf.len, hdr->dbuf.ptr);
				break;
			default:
				break;
			}
		}
		for (; to; to = ncc) {
			ncc = to->next;
			free(to);
		}
		for (; cc; cc = ncc) {
			ncc = cc->next;
			free(cc);
		}
	}
	if (to) {
		fprintf(fp, "To: ");
		w = 4;
		for (; to; to = ncc) {
			ncc = to->next;
			fprintf(fp, "%.*s", to->buf.len, to->buf.ptr);
			w += to->buf.len + 2;
			if (!ncc)
				fprintf(fp, "\n");
			else if (w + ncc->buf.len + 2 >= LINE_WIDTH) {
				fprintf(fp, ",\n\t");
				w = 8;
			} else
				fprintf(fp, ", ");
			free(to);
		}
	}
	if (cc) {
		fprintf(fp, "Cc: ");
		w = 4;
		for (; cc; cc = ncc) {
			ncc = cc->next;
			fprintf(fp, "%.*s", cc->buf.len, cc->buf.ptr);
			w += cc->buf.len + 2;
			if (!ncc)
				fprintf(fp, "\n");
			else if (w + ncc->buf.len + 2 >= LINE_WIDTH) {
				fprintf(fp, ",\n\t");
				w = 8;
			} else
				fprintf(fp, ", ");
			free(cc);
		}
	}
	if (state->config.bcc)
		print_jis(fp, "Bcc: %s\n", state->config.bcc);
	fprintf(fp, "Subject: ");
	if (subj->len < 3 || strncasecmp(subj->ptr, "Re:", 3) != 0)
		fprintf(fp, "Re: ");
	if (subj->ptr)
		print_jis(fp, "%.*s", subj->len, subj->ptr);
	putc('\n', fp);
	if (mid->ptr) {
		if (dat->ptr)
			fprintf(fp, "In-Reply-To: Your message of \"%.*s\"\n\t%.*s\n", dat->len, dat->ptr, mid->len, mid->ptr);
		fprintf(fp, "References: %.*s\n", mid->len, mid->ptr);
	}
	draft_mailer(fp);
	fprintf(fp, "Mime-Version: 1.0\n");
	fprintf(fp, "\n");
	if (mode == COMP_REPLYANK || mode == COMP_REPLORIG) {
		repl_yank(state, mode, fp);
	} else if (mode == COMP_REPLYMARK) {
		reply_mark(state, fp);
	}
	fclose(fp);

	strlcat(path, SUF_INFO, sizeof(path));
	fp = fopen(path, "w");
	fprintf(fp, "Anno: Replied\n");
	fprintf(fp, "File: %s\n", fdb->name);
	fprintf(fp, "Message-Id: %.*s\n", mid->len, mid->ptr);
	fclose(fp);

	state->folder = fl = folder_open(DRAFT_FOLDER, 1);
	fl->pos = fl->nmsg - 1;
	message_open(state, 1);
	if (state->message == NULL) {
		strlcpy(state->status, "Message not found", sizeof(state->status));
		return;
	}
	state->status[0] = '\0';
	if (mpart_edit(state, 0)) {
		folder_purge(state, state->message->msgnum);
		fdb_purge(state->message->name);
	} else {
		exec_del(fl, fl->pos);
		state->folder = folder_open(ofol, 1);
	}
	state->message = NULL;
	message_open(state, 1);	/* reopen */
}

void
reply_mark(struct state *state, FILE *fp)
{
	struct folder *ofl;
	struct filedb *edb;
	struct msginfo *msg;
	char path[MAXPATH];
	int n;

	ofl = state->folder;

	for (n = 0, msg = ofl->msg; n < ofl->nmsg; n++, msg++) {
		if (msg->mark != MARK_MARK)
			continue;
		snprintf(path, sizeof(path), "%s/%d", ofl->name.ptr + 1,
		    msg->num);
		if ((edb = fdb_open(path)) == NULL) {
			snprintf(state->status, sizeof(state->status),
			    "%s: Open failed", path);
			return;
		}

	    {
		ccbuf_t *subj;
		subj = &edb->hdr_val[HT_SUBJECT];
		if (subj->ptr)
			print_jis(fp, "In subject: %.*s\n",
				subj->len, subj->ptr);
	    }

	    {
		int i, pmore, more;
		ccbuf_t *cb;
		for (i = 0, pmore = 0; (cb = fdb_read(edb, i)) != NULL; i++) {
			if (i < edb->hdr_lines)
				continue;
			more = fdb_ismore(edb, i);
			print_jis(fp, "%s%.*s%s", (pmore ? "" : "> "),
				CL(cb), CP(cb), (more ? "" : "\n"));
			pmore = more;
		}
	    }
		fprintf(fp, "\n");
	}
}

void
forward(struct state *state, int mpart)
{
	struct folder *fl;
	FILE *fp;
	char path[MAXPATH];
	char buf[CHARBLOCK];
	struct stat stbuf;
	int n;
	ccbuf_t *mid, *dat, *subj, *cb;
	struct filedb *fdb, *ofdb;
	char *ofol;

	conf_update(state);
	ofol = state->folder->name.ptr;
	if (state->nomime) {
		strlcpy(state->status, "Skip MIME Analysis mode", sizeof(state->status));
		return;
	}

	if (strcmp(ofol, DRAFT_FOLDER) == 0) {
		strlcpy(state->status, "Cannot forward draft message", sizeof(state->status));
		return;
	}

	/* retrieve information from original mail */
	message_open(state, 0);
	if ((fdb = state->message) == NULL) {
		strlcpy(state->status, "No current message", sizeof(state->status));
		return;
	}
	ofdb = fdb;
	for (;;) {
		mid = &fdb->hdr_val[HT_MSGID];
		if (mid->ptr != NULL)
			break;
		if (fdb->uppart != NULL)
			fdb = fdb->uppart;
		if (ofdb == fdb) {
			strlcpy(state->status, "No Message ID", sizeof(state->status));
			return;
		}
	}
	dat = &fdb->hdr_val[HT_DATE];
	subj = &fdb->hdr_val[HT_SUBJECT];

	if ((fl = folder_open(DRAFT_FOLDER, 1)) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s does not exist", DRAFT_FOLDER);
		return;
	}
	if (fl->nmsg)
		n = fl->msg[fl->nmsg-1].num;
	else
		n = 0;
	do {
		snprintf(path, sizeof(path), "%s/%d", fl->name.ptr + 1, ++n);
	} while (stat(path, &stbuf) == 0);

	if ((fp = fopen(path, "w")) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s: %s", path, strerror(errno));
		return;
	}
	strlcpy(state->status, "To: ", sizeof(state->status));
	buf[0] = '\0';
	if (edit_stline(state, buf, sizeof(buf), malias_completion) == NULL) {
		fclose(fp);
		(void)unlink(path);
		strlcpy(state->status, "Quit", sizeof(state->status));
		return;
	}
#ifdef MALIAS
	malias_expand(buf, sizeof(buf));
#endif
	print_jis(fp, "To: %s\n", buf);
	if (state->config.bcc)
		print_jis(fp, "Bcc: %s\n", state->config.bcc);
	print_jis(fp, "Subject: Fw: %.*s\n", subj->len, subj->ptr);
	draft_mailer(fp);
	fprintf(fp, "Mime-Version: 1.0\n");
	fprintf(fp, "\n");
	if (!mpart) {
		fprintf(fp, "----- Forwarded Message -----\n");
		for (n = ofdb->skip_lines; (cb = fdb_read(ofdb, n)) != NULL;
		    n++) {
			print_jis(fp, "%.*s%s", CL(cb), CP(cb),
			    (fdb_ismore(ofdb, n) ? "" : "\n"));
		}
	}
	fclose(fp);

	strlcat(path, SUF_INFO, sizeof(path));
	fp = fopen(path, "w");
	fprintf(fp, "Anno: Forwarded\n");
	fprintf(fp, "File: %s\n", fdb->name);
	fprintf(fp, "Message-Id: %.*s\n", mid->len, mid->ptr);
	fclose(fp);

	state->folder = fl = folder_open(DRAFT_FOLDER, 1);
	fl->pos = fl->nmsg - 1;
	message_open(state, 1);
	if (mpart && mpart_multi(state) == 0) {
		strlcpy(state->status, "Multipart failed", sizeof(state->status));
		goto err;
	}
	folder_purge(state, state->message->msgnum);
	fdb_purge(state->message->name);
	state->message = NULL;
	message_open(state, 1);	/* reopen */
	message_parseall(state->message);
	if (mpart) {
		if (mpart_append(state, "Message/Rfc822", fdb->name) == 0) {
			strlcpy(state->status, "Append failed", sizeof(state->status));
  err:
			exec_del(fl, fl->pos);
			state->folder = folder_open(ofol, 1);
			return;
		}
	}
	folder_purge(state, state->message->msgnum);
	fdb_purge(state->message->name);
	state->message = NULL;
	message_open(state, 1);	/* reopen */
	if (state->message == NULL) {
		strlcpy(state->status, "Message not found", sizeof(state->status));
		return;
	}
	message_parseall(state->message);
	(void)mpart_edit(state, 0);
	folder_purge(state, state->message->msgnum);
	fdb_purge(state->message->name);
	state->message = NULL;
	message_open(state, 1);	/* reopen */
	if (state->message == NULL) {
		strlcpy(state->status, "Message not found", sizeof(state->status));
		return;
	}
	state->status[0] = '\0';
}

void
forward_mark(struct state *state)
{
	struct folder *fl, *ofl;
	FILE *fp;
	struct msginfo *msg;
	char path[MAXPATH];
	char buf[CHARBLOCK];
	struct stat stbuf;
	int n;

	conf_update(state);
	ofl = state->folder;

	if ((fl = folder_open(DRAFT_FOLDER, 1)) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s does not exist", DRAFT_FOLDER);
		return;
	}
	if (fl->nmsg)
		n = fl->msg[fl->nmsg-1].num;
	else
		n = 0;
	do {
		snprintf(path, sizeof(path), "%s/%d", fl->name.ptr + 1, ++n);
	} while (stat(path, &stbuf) == 0);

	if ((fp = fopen(path, "w")) == NULL) {
		snprintf(state->status, sizeof(state->status),
		    "%s: %s", path, strerror(errno));
		return;
	}
	strlcpy(state->status, "To: ", sizeof(state->status));
	buf[0] = '\0';
	if (edit_stline(state, buf, sizeof(buf), malias_completion) == NULL) {
  quit:
		fclose(fp);
		(void)unlink(path);
		strlcpy(state->status, "Quit", sizeof(state->status));
		return;
	}
#ifdef MALIAS
	malias_expand(buf, sizeof(buf));
#endif
	print_jis(fp, "To: %s\n", buf);
	if (state->config.bcc)
		fprintf(fp, "Bcc: %s\n", state->config.bcc);
	strlcpy(state->status, "Subject: ", sizeof(state->status));
	strlcpy(buf, "Fw: ", sizeof(buf));
	if (edit_stline(state, buf, sizeof(buf), NULL) == NULL)
		goto quit;
	print_jis(fp, "Subject: %s\n", buf);
	draft_mailer(fp);
	fprintf(fp, "Mime-Version: 1.0\n");
	fprintf(fp, "\n");
	fclose(fp);

	strlcat(path, SUF_INFO, sizeof(path));
	(void)unlink(path);

	state->folder = fl = folder_open(DRAFT_FOLDER, 1);
	fl->pos = fl->nmsg - 1;
	message_open(state, 1);
	if (mpart_multi(state) == 0) {
		strlcpy(state->status, "Multipart failed", sizeof(state->status));
		goto err;
	}
	folder_purge(state, state->message->msgnum);
	fdb_purge(state->message->name);
	state->message = NULL;
	message_open(state, 1);	/* reopen */
	message_parseall(state->message);
	for (n = 0, msg = ofl->msg; n < ofl->nmsg; n++, msg++) {
		if (msg->mark != MARK_MARK)
			continue;
		snprintf(path, sizeof(path), "%s/%d", ofl->name.ptr + 1,
		    msg->num);
		if (mpart_append(state, "Message/Rfc822", path) == 0) {
			strlcpy(state->status, "Append failed", sizeof(state->status));
  err:
			exec_del(fl, fl->pos);
			state->folder = folder_open(ofl->name.ptr, 1);
			return;
		}
		folder_purge(state, state->message->msgnum);
		fdb_purge(state->message->name);
		state->message = NULL;
		message_open(state, 1);	/* reopen */
		if (state->message == NULL) {
			strlcpy(state->status, "Message not found", sizeof(state->status));
			return;
		}
		message_parseall(state->message);
	}
	(void)mpart_edit(state, 0);
	folder_purge(state, state->message->msgnum);
	fdb_purge(state->message->name);
	state->message = NULL;
	message_open(state, 1);	/* reopen */
	if (state->message == NULL) {
		strlcpy(state->status, "Message not found", sizeof(state->status));
		return;
	}
	state->status[0] = '\0';
}
