/*
** Copyright 1998 - 2009 Double Precision, Inc.  See COPYING for
** distribution information.
*/


/*
*/
#include	"config.h"
#include	"cgi/cgi.h"
#include	"sqconfig.h"
#include	"sqwebmail.h"
#include	"auth.h"
#include	"maildir.h"
#include	"newmsg.h"
#include	"folder.h"
#include	"filter.h"
#include	"pref.h"
#include	"gpg.h"
#include	"addressbook.h"
#include	"maildir/maildirmisc.h"
#include	"rfc822/rfc822.h"
#include	"rfc2045/rfc2045.h"
#include	"rfc822/rfc2047.h"
#include	"rfc822/encode.h"
#include	"rfc822/rfc822hdr.h"
#include	"msg2html.h"
#include	"gpglib/gpglib.h"
#include	"http11/http11.h"
#include	"htmllibdir.h"
#include	"unicode/unicode.h"
#include	"courierauth.h"

#include	<stdlib.h>
#if HAVE_UNISTD_H
#include	<unistd.h>
#endif
#include	<ctype.h>
#include	<fcntl.h>

#define HASTEXTPLAIN(q) (rfc2045_searchcontenttype((q), "text/plain") != NULL)
/* Also in attachments.c */

extern const char *rfc822_mkdt(time_t);

extern const char *sqwebmail_content_charset;
extern const char *sqwebmail_content_language;

int newdraftfd;
extern const char *sqwebmail_mailboxid;

const char mimemsg[]="This is a MIME-formatted message.  If you see this text it means that your\nmail software cannot handle MIME-formatted messages.\n\n";

char *newmsg_createdraft_do(const char *, const char *, int);

/* Save message in a draft file */

char *newmsg_createdraft(const char *curdraft)
{
	if (curdraft && *curdraft)
	{
	char	*base=maildir_basename(curdraft);
	char	*filename=maildir_find(INBOX "." DRAFTS, base);

		if (filename)
		{
		char	*p=newmsg_createdraft_do(filename, cgi("message"), 0);

			free(filename);
			free(base);
			return (p);
		}
		free(base);
	}
	return (newmsg_createdraft_do(0, cgi("message"), 0));
}

static void create_draftheader_do(const char *hdrname, const char *p,
	int isrfc822addr);

static void create_draftheader(const char *hdrname, const char *p,
			       const char *q, int isrfc822addr)
{
	if (q && *q)	/* Add from address book */
	{
	char	*nick=cgi_multiple("nick", ",");
	char	*s;

		if (nick)
		{
			s=malloc(strlen(p)+strlen(nick)+2);

			if (s)
			{
				strcpy(s, p);
				if (*s && *nick)	strcat(s, ",");
				strcat(s, nick);
				create_draftheader_do(hdrname, s, isrfc822addr);
				free(s);
				free(nick);
				return;
			}
			free(nick);
		}

	}
	create_draftheader_do(hdrname, p, isrfc822addr);
}

#define	ISLWS(c)	((c)=='\t' || (c)=='\r' || (c)=='\n' || (c)==' ')

static void header_wrap(const char *name, const char *hdr,
			char *outbuf, size_t *outcnt)
{
char	*pfix;
size_t	offset=strlen(name);

	*outcnt=0;
	pfix="";

	while (*hdr)
	{
	size_t i;
	size_t spc;

		for (spc=0, i=0; hdr[i]; i++)
		{
			if (i + offset >= 75 && spc)
			{
			        i = spc;
				offset = 0;
				break;
			}

			if (ISLWS(hdr[i]))
			{
				spc = i;
				while (ISLWS(hdr[i+1]))
					++i;
			}
		}

		if (outbuf)
		{
			strcpy(outbuf, pfix);
			outbuf += strlen(pfix);
		}
		*outcnt += strlen(pfix);

		if (outbuf)
		{
		size_t j;
			for (j=0; j < i; j++)
			{
				if (ISLWS(hdr[j]))
				{
					*(outbuf++) = ' ';
					while (ISLWS(hdr[j+1]))
						++j;
				}
				else
					*(outbuf++) = hdr[j];
			}
		}
		*outcnt += i;
		pfix="\n  ";
		hdr += i;
		while (ISLWS(*hdr))
			++hdr;
	}
	if (outbuf)
		*outbuf=0;
	++*outcnt;
}
			
static void create_draftheader_do(const char *hdrname, const char *p,
	int isrfc822addr)
{
char	*s, *t;
size_t	l;

	if (!*p)	return;

	if (!isrfc822addr)
	{
		s=rfc2047_encode_str(p, sqwebmail_content_charset,
				     rfc2047_qp_allow_any);
	}
	else
	{
		s=rfc2047_encode_header_tobuf("to", p,
					      sqwebmail_content_charset);
	}

	header_wrap(hdrname, s, NULL, &l);
	if (l)
	{
		if (!(t=malloc(l))) enomem();
		header_wrap(hdrname, s, t, &l);
		if (*t)
		{
			free(s);
			s = t;
		}
		else
			free(t);
	}

	if (!s)
	{
		close(newdraftfd);
		enomem();
	}
	maildir_writemsgstr(newdraftfd, hdrname);
	maildir_writemsgstr(newdraftfd, s);
	maildir_writemsgstr(newdraftfd, "\n");
	free(s);
}

void newmsg_create_multipart(int newdraftfd, const char *charset,
			const char *multipart_boundary)
{
	maildir_writemsgstr(newdraftfd,
		"Mime-version: 1.0\n"
		"Content-Type: multipart/mixed; boundary=\"");
	maildir_writemsgstr(newdraftfd, multipart_boundary);
	maildir_writemsgstr(newdraftfd, "\"; charset=\"");
	maildir_writemsgstr(newdraftfd, charset);
	maildir_writemsgstr(newdraftfd, 
					"\"\n\n");

	maildir_writemsgstr(newdraftfd, mimemsg);
}


static char	*newmsg_multipart_boundary(FILE *, const char *);
static void newmsg_copy_attachments(struct rfc2045 *, FILE *, const char *);

void newmsg_copy_nonmime_headers(FILE *fp)
{
char	*header, *value;
char	*q;

	while ((header=maildir_readheader(fp, &value, 1)) != NULL)
	{
		if (strcmp(header, "mime-version") == 0 ||
			strncmp(header, "content-", 8) == 0)	continue;

		/* Fluff - capitalize header names */

		for (q=header; *q; q++)
		{
			for (*q=toupper(*q); *q; q++)
				if (*q == '-')	break;
			if (!*q)
				break;
		}

		maildir_writemsgstr(newdraftfd, header);
		maildir_writemsgstr(newdraftfd, ": ");
		maildir_writemsgstr(newdraftfd, value);
		maildir_writemsgstr(newdraftfd, "\n");
	}
}

void newmsg_copy_content_headers(FILE *fp)
{
char	*header, *value;
char	*q;

	while ((header=maildir_readheader(fp, &value, 1)) != NULL)
	{
		if (strncmp(header, "content-", 8)) continue;

		for (q=header; *q; q++)
		{
			for (*q=toupper(*q); *q; q++)
				if (*q == '-')	break;
			if (!*q)
				break;
		}

		maildir_writemsgstr(newdraftfd, header);
		maildir_writemsgstr(newdraftfd, ": ");
		maildir_writemsgstr(newdraftfd, value);
		maildir_writemsgstr(newdraftfd, "\n");
	}
}

void wrap_text_init(struct wrap_info *uw,
		    const char *output_chset,
		    void (*output_func)(const char *p, size_t l, void *arg),
		    void *arg)
{
	memset(uw, 0, sizeof(*uw));
	uw->output_func=output_func;
	uw->output_chset=output_chset;
	uw->arg=arg;
}

static void do_save_u_line(struct wrap_info *uw,
			   const unicode_char *uc,
			   size_t ucsize,
			   int flowed)
{
	char *cbuf;
	size_t csize;

	libmail_u_convert_handle_t h=
		libmail_u_convert_fromu_init(uw->output_chset,
					     &cbuf,
					     &csize,
					     0);

	if (h)
	{
		if (ucsize)
		{
			if (uc[0] == ' ')
				libmail_u_convert_uc(h, uc, 1);
			/* Space stuff */

			libmail_u_convert_uc(h, uc, ucsize);
		}
		if (flowed)
		{
			unicode_char spc=' ';
			libmail_u_convert_uc(h, &spc, 1);
		}

		{
			unicode_char nl='\n';
			libmail_u_convert_uc(h, &nl, 1);
		}

		if (libmail_u_convert_deinit(h, NULL))
			cbuf=NULL;
	}
	else
		cbuf=NULL;

	if (cbuf)
	{
		(*uw->output_func)(cbuf, csize, uw->arg);
		free(cbuf);
	}
}

static void flush_line(struct wrap_info *uw, int flowed)
{
	do_save_u_line(uw, uw->uc + uw->line_start,
		       uw->word_start - uw->line_start, flowed);

	uw->line_start=uw->word_start;
	uw->line_width=0;
}

static void add_word(struct wrap_info *uw)
{
	if (uw->line_start < uw->word_start &&
	    uw->line_width + uw->word_width > MYLINESIZE)
		flush_line(uw, 1);

	uw->line_width += uw->word_width;

	uw->word_start=uw->cur_index;
	uw->word_width=0;
}

static int do_save_u_process_lb(int type, void *arg)
{
	struct wrap_info *uw=(struct wrap_info *)arg;

	if (uw->cur_index >= uw->ucsize)
		enomem();

	if (type != UNICODE_LB_NONE)
	{
		add_word(uw);
		if (type == UNICODE_LB_MANDATORY)
			flush_line(uw, 0);
	}


	if (uw->word_width >= MYLINESIZE &&
	    uw->cur_index > 0 &&
	    unicode_grapheme_break(uw->uc[uw->cur_index-1],
				   uw->uc[uw->cur_index]))
		add_word(uw);

	uw->word_width += unicode_wcwidth(uw->uc[uw->cur_index]);
	++uw->cur_index;
	return 0;
}

static void do_wrap_u_line(struct wrap_info *uw,
			   const unicode_char *uc,
			   size_t ucsize)
{
	unicode_lb_info_t lb;

	while (ucsize && uc[ucsize-1] == ' ')
		--ucsize;

	uw->uc=uc;
	uw->ucsize=ucsize;
	uw->cur_index=0;
	uw->word_start=0;
	uw->word_width=0;

	uw->line_start=0;
	uw->line_width=0;
	if ((lb=unicode_lb_init(do_save_u_process_lb, uw)) != NULL)
	{
		unicode_lb_set_opts(lb,
				    UNICODE_LB_OPT_PRBREAK
				    | UNICODE_LB_OPT_SYBREAK);
		unicode_lb_next_cnt(lb, uc, ucsize);
		unicode_lb_end(lb);
		add_word(uw);
		flush_line(uw, 0);
	}
}

static void save_textplain(const char *p, size_t l, void *dummy)
{
	maildir_writemsg(newdraftfd, p, l);
}

void wrap_text(struct wrap_info *uw,
	       const char *newmsg,
	       size_t newmsg_size)
{
	size_t i=0, j;

	while (i < newmsg_size)
	{
		unicode_char *uc;
		size_t ucsize;
		libmail_u_convert_handle_t h;

		j=i;

		while (i<newmsg_size && newmsg[i] != '\n')
			++i;

		h=libmail_u_convert_tou_init(sqwebmail_content_charset,
					     &uc, &ucsize, 0);

		if (h)
		{
			libmail_u_convert(h, newmsg+j, i-j);

			if (libmail_u_convert_deinit(h, NULL))
				uc=NULL;
		}
		else
		{
			uc=NULL;
		}

		if (uc)
		{
			size_t i, j;

			/* Get rid of any CRs that sneak in */

			for (i=j=0; i<ucsize; ++i)
			{
				if (uc[i] == '\r')
					continue;

				uc[j]=uc[i];
				++j;
			}

			if (j && *uc == '>')
				do_save_u_line(uw, uc, j, 0);
			else
				do_wrap_u_line(uw, uc, j);

			free(uc);
		}

		if (i < newmsg_size)
			++i;
	}
}

static void convert_text2html(const char *p, size_t l, void *arg)
{
	struct msg2html_textplain_info *info=
		(struct msg2html_textplain_info *)arg;

	msg2html_textplain(info, p, l);
}

static char *mkurl(const char *url, void *dummy)
{
	char *buf=malloc(strlen(url)*2+100);

	if (!buf)
		return NULL;

	/* msg2html guarantees that the characters in url are "safe" */

	sprintf(buf, "<a href=\"%s\">%s</a>", url, url);
	return buf;
}

char *newmsg_createdraft_do(const char *curdraft, const char *newmsg,
	int keepheader)
{
char	*draftfilename;
FILE	*fp=0;
char	*multipart_boundary;
const char *content_type;
const char *content_transfer_encoding;
const char *charset;
unsigned long prev_size=0;
off_t	transferencodingpos;
off_t	transferencoding2pos;
int is_newevent=strcmp(cgi("form"), "newevent") == 0;
struct	rfc2045	*rfcp;
int has_attachments=0;
size_t newmsg_size;
char *sig, *footer;

/*
** Trim extra newlines.
*/
	newmsg_size=strlen(newmsg);

	while (newmsg_size && newmsg[newmsg_size-1] == '\n')
		--newmsg_size;

/* We're on the 'new event' screen */

 	if (curdraft)	/* Reuse a draft filename */
		newdraftfd=maildir_recreatemsg(INBOX "." DRAFTS, curdraft, &draftfilename);
	else
		newdraftfd=maildir_createmsg(INBOX "." DRAFTS, 0, &draftfilename);
	if (newdraftfd < 0)	enomem();

	pref_wikifmt=0;
	if (strcmp(cgi("textformat"), "wiki") == 0)
		pref_wikifmt=1;
	pref_update();

	fp=NULL;
	if (curdraft)
	{
	int	x=maildir_safeopen(curdraft, O_RDONLY, 0);

		if (x >= 0)
			if ((fp=fdopen(x, "r")) == 0)
				close(x);
	}

	if (fp)
	{
	char	*header, *value;
	struct	stat	stat_buf;

		if (fstat(fileno(fp), &stat_buf))
		{
			fclose(fp);
			enomem();
		}
		prev_size=stat_buf.st_size;

		while ((header=maildir_readheader(fp, &value, 1)) != NULL)
		{
			if (keepheader == NEWMSG_SQISPELL)
			{
				if (strcasecmp(header, "mime-version") == 0 ||
				    strncasecmp(header, "content-", 8) == 0)
					continue;
			}
			else if (keepheader == NEWMSG_PCP)
			{
				if (strcasecmp(header, "mime-version") == 0 ||
				    strncasecmp(header, "content-", 8) == 0 ||
				    strcasecmp(header, "date") == 0 ||
				    strcasecmp(header, "from") == 0 ||
				    strcasecmp(header, "subject") == 0)
					continue;
			}
			else
			{
				if (strcmp(header, "in-reply-to") &&
					strcmp(header, "references") &&
					strncmp(header, "x-", 2))	continue;
				/* Do not discard these headers */
			}

			if (strcasecmp(header, "x-sqwebmail-wikifmt") == 0)
				continue;

			maildir_writemsgstr(newdraftfd, header);
			maildir_writemsgstr(newdraftfd, ": ");
			maildir_writemsgstr(newdraftfd, value);
			maildir_writemsgstr(newdraftfd, "\n");
		}
	}
	else if (is_newevent)
		maildir_writemsgstr(newdraftfd, "X-Event: 1\n");

	if (!keepheader
	    || keepheader == NEWMSG_PCP)
	/* Coming back from msg edit, set headers */
	{
	const	char *p=cgi("headerfrom");

		if (!*p)	p=pref_from;
		if (!p || !*p || auth_getoptionenvint("wbnochangingfrom"))
			p=login_fromhdr();

		create_draftheader("From: ", p, 0, 1);

		if (!pref_from || strcmp(p, pref_from))
			pref_setfrom(p);

/* sam ????
	create_draftheader("In-Reply-To: ", cgi("headerin-reply-to"));
*/
		if (!is_newevent)
		{
#if 0
			{
				FILE *fp;
				fp=fopen("/tmp/pid", "w");
				fprintf(fp, "%d", getpid());
				fclose(fp);
				sleep(10);
			}
#endif

			create_draftheader("To: ", cgi("headerto"),
					   cgi("addressbook_to"), 1);
			create_draftheader("Cc: ", cgi("headercc"),
					   cgi("addressbook_cc"), 1);
			create_draftheader("Bcc: ", cgi("headerbcc"),
					   cgi("addressbook_bcc"), 1);
			create_draftheader("Reply-To: ", cgi("headerreply-to"), 0, 1);
		}
	}

	if (pref_wikifmt)
		create_draftheader("x-sqwebmail-wikifmt: ", "1", 0, 0);

	if (!keepheader || keepheader == NEWMSG_PCP)
	{
	time_t	t;

		create_draftheader("Subject: ", cgi("headersubject"), 0, 0);

		time(&t);
		create_draftheader("Date: ", rfc822_mkdate(t), 0, 0);
	}

	/* If the message has attachments, calculate multipart boundary */

	rfcp=NULL;

	if (fp)
	{
		rfcp=rfc2045_fromfp(fp);
		if (!rfcp)
		{
			close(newdraftfd);
			fclose(fp);
			enomem();
		}
	}

	multipart_boundary=newmsg_multipart_boundary(fp, newmsg);

	if (rfcp && rfcp->firstpart &&
	    strcmp((rfc2045_mimeinfo(rfcp, &content_type,
			      &content_transfer_encoding, &charset),
		    content_type), "multipart/mixed") == 0)
	{
		has_attachments=1;
		newmsg_create_multipart(newdraftfd,
			sqwebmail_content_charset, multipart_boundary);

		maildir_writemsgstr(newdraftfd, "--");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd,"\n");
	}
	else
	{
		maildir_writemsgstr(newdraftfd, "Mime-Version: 1.0\n");
	}

	if (pref_wikifmt)
	{
		++multipart_boundary[strlen(multipart_boundary)-1];

		maildir_writemsgstr(newdraftfd,
				    "Content-Type: multipart/alternative;"
				    " boundary=\"");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "\"\n"
				    "\n"
				    "\n"
				    "--");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "\n");
	}

	maildir_writemsgstr(newdraftfd,
			    "Content-Type: text/plain; format=flowed; delsp=yes;"
			    " charset=\"");
	maildir_writemsgstr(newdraftfd, sqwebmail_content_charset);
	maildir_writemsgstr(newdraftfd, "\"\n");

	maildir_writemsgstr(newdraftfd, "Content-Transfer-Encoding: ");
	transferencoding2pos=transferencodingpos=writebufpos;
	maildir_writemsgstr(newdraftfd, "7bit\n\n");

	/*	maildir_writemsgstr(newdraftfd, "\n"); */

	sig=pref_getsig();
	footer=pref_getfile(http11_open_langfile(get_templatedir(),
						 sqwebmail_content_language,
						 "footer"));

	while (newmsg_size &&
	       (newmsg[newmsg_size-1] == '\r' ||
		newmsg[newmsg_size-1] == '\n'))
		--newmsg_size;

	{
		struct wrap_info uw;

		wrap_text_init(&uw, sqwebmail_content_charset,
			       save_textplain, NULL);

		wrap_text(&uw, newmsg, newmsg_size);

		if ((sig && *sig) || (footer && *footer))
		{
			static const unicode_char sig_line[]={'-', '-', ' '};

			do_save_u_line(&uw, sig_line, 0, 0);
			do_save_u_line(&uw, sig_line, 3, 0);
		}

		if (sig && *sig)
			wrap_text(&uw, sig, strlen(sig));

		if (footer && *footer)
		{
			do_save_u_line(&uw, NULL, 0, 0);
			maildir_writemsg(newdraftfd, footer, strlen(footer));
		}

	}

	if (pref_wikifmt)
	{
		struct msg2html_textplain_info *info;

		maildir_writemsgstr(newdraftfd, "\n"
				    "--");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "\n"
				    "Content-Type: text/html; charset=\"");
		maildir_writemsgstr(newdraftfd, sqwebmail_content_charset);
		maildir_writemsgstr(newdraftfd, "\"\n"
				    "Content-Transfer-Encoding: ");
		transferencoding2pos=writebufpos;
		maildir_writemsgstr(newdraftfd, "7bit\n\n");

		info=msg2html_textplain_start(sqwebmail_content_charset,
					      sqwebmail_content_charset,
					      1,
					      1,
					      0,
					      mkurl, NULL,
					      NULL,
					      NULL,
					      1,
					      save_textplain,
					      NULL);

		if (info)
		{
			struct wrap_info uw;

			wrap_text_init(&uw, sqwebmail_content_charset,
				       convert_text2html, info);

			wrap_text(&uw, newmsg, newmsg_size);
			msg2html_textplain_end(info);
		}

		if ((sig && *sig) || (footer && *footer))
			save_textplain("<hr />\n", 7, NULL);

		if (sig && *sig)
		{

			info=msg2html_textplain_start(sqwebmail_content_charset,
						      sqwebmail_content_charset,
						      1,
						      1,
						      0,
						      mkurl, NULL,
						      NULL,
						      NULL,
						      1,
						      save_textplain,
						      NULL);

			if (info)
			{
				struct wrap_info uw;

				wrap_text_init(&uw, sqwebmail_content_charset,
					       convert_text2html, info);

				wrap_text(&uw, sig, strlen(sig));
				msg2html_textplain_end(info);
			}
		}

		if (footer && *footer)
		{
			save_textplain("<br />\n", 7, NULL);

			info=msg2html_textplain_start(sqwebmail_content_charset,
						      sqwebmail_content_charset,
						      1,
						      1,
						      0,
						      mkurl, NULL,
						      NULL,
						      NULL,
						      1,
						      save_textplain,
						      NULL);

			if (info)
			{
				msg2html_textplain(info, footer,
						   strlen(footer));
				msg2html_textplain_end(info);
			}
		}

		maildir_writemsgstr(newdraftfd, "\n"
				    "--");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "--\n");
		--multipart_boundary[strlen(multipart_boundary)-1];

	}

	if (sig)
		free(sig);

	if (footer)
		free(footer);

	if ( multipart_boundary && rfcp && has_attachments)
	{
		newmsg_copy_attachments(rfcp, fp, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "\n--");
		maildir_writemsgstr(newdraftfd, multipart_boundary);
		maildir_writemsgstr(newdraftfd, "--\n");
		free(multipart_boundary);
	}
	if (fp)	fclose(fp);
	if (rfcp)
		rfc2045_free(rfcp);

	if ( maildir_writemsg_flush(newdraftfd) == 0 && writebuf8bit)
	{
		if (lseek(newdraftfd, transferencodingpos, SEEK_SET) < 0 ||
			write(newdraftfd, "8", 1) != 1 ||
		    lseek(newdraftfd, transferencoding2pos, SEEK_SET) < 0 ||
			write(newdraftfd, "8", 1) != 1)
		{
			close(newdraftfd);
			enomem();
		}
	}

	if ( maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, -1, prev_size))
		cgi_put("error", "quota");

	return(draftfilename);
}

static void sentmsg_copy(FILE *f, struct rfc2045 *p)
{
	off_t start_pos, end_pos, start_body;
	char buf[BUFSIZ];
	int n;
	off_t   dummy;

        rfc2045_mimepos(p, &start_pos, &end_pos, &start_body, &dummy, &dummy);
        if (fseek(f, start_pos, SEEK_SET) == -1)
        {
                fclose(f);
                close(newdraftfd);
                enomem();
        }

        while (start_pos < end_pos)
        {
        int     cnt=sizeof(buf);

                if (cnt > end_pos - start_pos)
                        cnt=end_pos - start_pos;

                if ((n=fread(buf, 1, cnt, f)) <= 0)
                {
                        fclose(f);      
                        close(newdraftfd);
                        enomem();
                }

                maildir_writemsg(newdraftfd, buf, n);
                start_pos += n;
        }
}


/* Create message in the sent folder */

static void header_uc(char *h)
{
	while (*h)
	{
		*h=toupper( (int)(unsigned char) *h);
		while (*h)
		{
			if (*h++ == '-')	break;
		}
	}
}

struct lookup_buffers {
	struct lookup_buffers *next;
	char *buf;
	char *buf2;
	} ;

static int lookup_addressbook_do(const char *header, const char *value,
	struct lookup_buffers **lookup_buffer_list)
{
	struct	rfc822t *t;
	struct	rfc822a *a;
	int	i;
	char	*newbuf;
	struct lookup_buffers *ptr;
	int	expanded=0;

	t=rfc822t_alloc_new(value, NULL, NULL);
	if (!t)	enomem();
	a=rfc822a_alloc(t);
	if (!a)
	{
		rfc822t_free(t);
		enomem();
	}

	for (i=0; i<a->naddrs; i++)
	{
		char	*p;
		char	*s;
		const	char *q;
		struct lookup_buffers *r;

		if (a->addrs[i].tokens == 0)
			continue;
		if (a->addrs[i].name)
			continue;	/* Can't be a nickname */

		p=rfc822_getaddr(a, i);
		if (!p)
		{
			rfc822a_free(a);
			rfc822t_free(t);
			free(p);
			return (-1);
		}

		for (ptr= *lookup_buffer_list; ptr; ptr=ptr->next)
			if (strcmp(ptr->buf2, p) == 0)
				break;

		if (ptr)	/* Address book loop */
		{
		int	j;

			for (j=i+1; j<a->naddrs; j++)
				a->addrs[j-1]=a->addrs[j];
			--a->naddrs;
			--i;
			free(p);
			continue;
		}

		s=rfc822_display_addr_str_tobuf(p, "utf-8");

		if (s == NULL || (q=ab_find(s)) == 0)
		{
			if (s)
				free(s);
			free(p);
			continue;
		}
		free(s);

		r=malloc(sizeof(struct lookup_buffers));
		if (r)	r->buf=r->buf2=0;

		if (!r || !(r->buf=strdup(q)) || !(r->buf2=strdup(p)))
		{
			free(p);
			if (r && r->buf)	free(r->buf);
			if (r)	free(r);
			rfc822a_free(a);
			rfc822t_free(t);
			return (-1);
		}
		free(p);
		r->next= *lookup_buffer_list;
		*lookup_buffer_list=r;
		a->addrs[i].tokens->next=0;
		a->addrs[i].tokens->token=0;
		a->addrs[i].tokens->ptr=r->buf;
		a->addrs[i].tokens->len=strlen(r->buf);
		expanded=1;
	}

	newbuf=rfc822_getaddrs_wrap(a, 70);
	rfc822a_free(a);
	rfc822t_free(t);
	if (!newbuf)	return (-1);

	if (expanded)	/* Look through the address book again */
	{
	int	rc=lookup_addressbook_do(header, newbuf, lookup_buffer_list);

		free(newbuf);
		return (rc);
	}

	create_draftheader_do(header, newbuf, 1);
	free(newbuf);
	return (0);
}

static void lookup_addressbook(const char *header, const char *value)
{
	struct lookup_buffers *lookup_buffer_list=0;
	int	rc;
	char *header_cpy;
	char *value_cpy;
	/*
	** header & value may be pointing to buffer allocated by
	** maildir_readheader.
	** lookup_addressbook_do may call it again.
	*/

	header_cpy=strdup(header);
	if (!header_cpy)
		enomem();

	value_cpy=strdup(value);
	if (!value_cpy)
	{
		free(header_cpy);
		enomem();
	}

	rc=lookup_addressbook_do(header_cpy, value_cpy, &lookup_buffer_list);
	free(header_cpy);

	while (lookup_buffer_list)
	{
	struct lookup_buffers *p=lookup_buffer_list;

		lookup_buffer_list=p->next;
		free(p->buf);
		free(p->buf2);
		free(p);
	}
	if (rc)	enomem();
}

char *newmsg_createsentmsg(const char *draftname, int *isgpgerr)
{
char	*filename=maildir_find(INBOX "." DRAFTS, draftname);
FILE	*fp;
char	*sentname;
char	*header, *value;
struct	rfc2045 *rfcp;
int	x;

	*isgpgerr=0;
 
	if (!filename)	return (0);

	fp=0;

	x=maildir_safeopen(filename, O_RDONLY, 0);
	if (x >= 0)
		if ((fp=fdopen(x, "r")) == 0)
			close(x);

	if (fp == 0)
	{
		free(filename);
		enomem();
	}

	rfcp=rfc2045_fromfp(fp);
	if (!rfcp || fseek(fp, 0L, SEEK_SET) < 0)
	{
		fclose(fp);
		close(newdraftfd);
		enomem();
	}

	newdraftfd=maildir_createmsg(INBOX "." SENT, 0, &sentname);
	if (newdraftfd < 0)
	{
		rfc2045_free(rfcp);
		free(filename);
		fclose(fp);
		enomem();
	}
	/* First, copy all headers except X- headers */

	while ((header=maildir_readheader(fp, &value, 1)) != 0)
	{
		if (strncmp(header, "x-", 2) == 0)	continue;
		header_uc(header);
		if (rfc822hdr_namecmp(header, "To") == 0)
		{
			lookup_addressbook("To: ", value);
			continue;
		}

		if (rfc822hdr_namecmp(header, "Cc") == 0)
		{
			lookup_addressbook("Cc: ", value);
			continue;
		}

		if (rfc822hdr_namecmp(header, "Bcc") == 0)
		{
			lookup_addressbook("Bcc: ", value);
			continue;
		}

		maildir_writemsgstr(newdraftfd, header);
		maildir_writemsgstr(newdraftfd, ": ");
		maildir_writemsgstr(newdraftfd, value);
		maildir_writemsgstr(newdraftfd, "\n");
	}
	if (auth_getoptionenvint("wbusexsender"))
	{
		maildir_writemsgstr(newdraftfd, "X-Sender: ");
		maildir_writemsgstr(newdraftfd, login_returnaddr());
		maildir_writemsgstr(newdraftfd, "\n");
	}

	maildir_writemsgstr(newdraftfd, "\n");

	{
		off_t start_pos, end_pos, start_body;
		char buf[BUFSIZ];
		int n;
		off_t   dummy;
		
		rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body,
				&dummy, &dummy);

		if (fseek(fp, start_body, SEEK_SET) == -1)
		{
			fclose(fp);
			close(newdraftfd);
			enomem();
		}

		while (start_body < end_pos)
		{
			int     cnt=sizeof(buf);

			if (cnt > end_pos - start_pos)
				cnt=end_pos - start_pos;

			if ((n=fread(buf, 1, cnt, fp)) <= 0)
			{
				fclose(fp);      
				close(newdraftfd);
				enomem();
			}

			maildir_writemsg(newdraftfd, buf, n);
			start_body += n;
		}
	}


	if ( maildir_writemsg_flush(newdraftfd))
	{
		free(sentname);
		return (0);
	}

#if 0
	if (writebuf8bit)
	{
		if (lseek(newdraftfd, transferencodingpos, SEEK_SET) < 0 ||
			write(newdraftfd, "8", 1) != 1)
		{
			free(sentname);
			return (0);
		}
	}
#endif

	if ( maildir_writemsg_flush(newdraftfd))
	{
		maildir_closemsg(newdraftfd, INBOX "." SENT, sentname, 0, 0);
		free(sentname);
		return (0);
	}

	if (libmail_gpg_has_gpg(GPGDIR) == 0)
	{
		char dosign= *cgi("sign");
		char doencrypt= *cgi("encrypt");
		const char *signkey= cgi("signkey");
		char *encryptkeys=cgi_multiple("encryptkey", " ");

		if (!encryptkeys)
			enomem();

		if (gpgbadarg(encryptkeys) || !*encryptkeys)
		{
			free(encryptkeys);
			encryptkeys=0;
		}

		if (gpgbadarg(signkey) || !*signkey)
		{
			signkey=0;
		}

		if (!encryptkeys)
			doencrypt=0;

		if (!signkey)
			dosign=0;

		if (lseek(newdraftfd, 0L, SEEK_SET) < 0)
		{
			maildir_closemsg(newdraftfd, INBOX "." SENT,
					 sentname, 0, 0);
			free(sentname);
			return (0);
		}

		if (!dosign)
			signkey=0;
		if (!doencrypt)
			encryptkeys=0;

		if (dosign || doencrypt)
		{
			/*
			** What we do is create another draft, then substitute
			** it for newdraftfd/sentname.  Sneaky.
			*/

			char *newnewsentname;
			int newnewdraftfd=maildir_createmsg(INBOX "." SENT, 0,
							    &newnewsentname);

			if (newnewdraftfd < 0)
			{
				maildir_closemsg(newdraftfd, INBOX "." SENT,
						 sentname, 0, 0);
				free(sentname);
				free(encryptkeys);
				return (0);
			}

			if (gpgdomsg(newdraftfd, newnewdraftfd,
				     signkey, encryptkeys))
			{
				maildir_closemsg(newnewdraftfd, INBOX "." SENT,
						 newnewsentname, 0, 0);
				free(newnewsentname);
				maildir_closemsg(newdraftfd, INBOX "." SENT,
						 sentname, 0, 0);
				free(sentname);
				free(encryptkeys);
				*isgpgerr=1;
				return (0);
			}

			maildir_closemsg(newdraftfd, INBOX "." SENT, sentname, 0, 0);
			free(sentname);
			sentname=newnewsentname;
			newdraftfd=newnewdraftfd;

		}
		free(encryptkeys);
	}

	if ( maildir_closemsg(newdraftfd, INBOX "." SENT, sentname, 1, 0))
	{
		free(sentname);
		return (0);
	}
	return (sentname);
}

/* ---------------------------------------------------------------------- */

/* Create a potential multipart boundary separator tag */

char *multipart_boundary_create()
{
char	pidbuf[MAXLONGSIZE];
char	timebuf[MAXLONGSIZE];
time_t	t;
char	cntbuf[MAXLONGSIZE];
static unsigned long cnt=0;
char	*p;

	sprintf(pidbuf, "%lu", (unsigned long)getpid());
	time(&t);
	sprintf(timebuf, "%lu", (unsigned long)t);
	sprintf(cntbuf, "%lu", cnt++);
	p=malloc(strlen(pidbuf)+strlen(timebuf) +strlen(cntbuf)+20);
	sprintf(p, "=_%s_%s_%s_000", cntbuf, pidbuf, timebuf);
	return (p);
}

/* Search for the boundary tag in a string buffer - this is the new message
** we're creating.  We should really look for the tag at the beginning of the
** line, however, the text is not yet linewrapped, besides, why make your
** life hard?
*/

int multipart_boundary_checks(const char *boundary, const char *msg)
{
size_t	boundarylen=strlen(boundary);

	while (*msg)
	{
		if (msg[0] == '-' && msg[1] == '-' && msg[2] != '-' &&
			strncasecmp(msg+2, boundary, boundarylen) == 0)
				return (-1);
		++msg;
	}
	return (0);
}

/* Again, just look for it at the beginning of the line -- why make your
** life hard? */

int multipart_boundary_checkf(const char *boundary, FILE *f)
{
size_t	boundarylen=strlen(boundary);
const char *line;

	if (fseek(f, 0L, SEEK_SET) == -1)
	{
		fclose(f);
		close(newdraftfd);
		enomem();
	}

	while ((line=maildir_readline(f)) != 0)
		if (line[0] == '-' && line[1] == '-' &&
			strncasecmp(line+2, boundary, boundarylen) == 0)
				return (-1);
	return (0);
}

/* ---------------------------------------------------------------------- */

/* Copy existing attachments into the new draft message */

/* multipart_boundary - determine if current draft has attachments */

static char	*newmsg_multipart_boundary(FILE *f, const char *msg)
{
	char	*p=0;

	do
	{
		if (p)	free(p);
		p=multipart_boundary_create();
	} while (multipart_boundary_checks(p, msg)
		 || (f && multipart_boundary_checkf(p, f)));
	return (p);
}

static void	newmsg_copy_attachments(struct rfc2045 *rfcp,
					FILE *f, const char *boundary)
{
struct	rfc2045 *p;
int	foundtextplain=0;

	for (p=rfcp->firstpart; p; p=p->next)
	{
		if (p->isdummy)	continue;
		if (!foundtextplain && HASTEXTPLAIN(p))
		{	/* Previous version of this message */

			foundtextplain=1;
			continue;
		}
		maildir_writemsgstr(newdraftfd, "\n--");
		maildir_writemsgstr(newdraftfd, boundary);
		maildir_writemsgstr(newdraftfd, "\n");
		sentmsg_copy(f, p);	/* Reuse some code */
	}
}
