diff options
Diffstat (limited to 'rfc2045/rfc2045reply.c')
| -rw-r--r-- | rfc2045/rfc2045reply.c | 1667 | 
1 files changed, 1667 insertions, 0 deletions
| diff --git a/rfc2045/rfc2045reply.c b/rfc2045/rfc2045reply.c new file mode 100644 index 0000000..0692fb0 --- /dev/null +++ b/rfc2045/rfc2045reply.c @@ -0,0 +1,1667 @@ +/* +** Copyright 2000-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + +#include "rfc2045_config.h" +#include	"rfc2045.h" +#include	"rfc2045src.h" +#include	"rfc3676parser.h" +#include	"rfc822/rfc2047.h" +#include	"rfc2045charset.h" +#include	"rfc822/rfc822.h" +#include	"unicode/unicode.h" +#include	<stdio.h> +#include	<unistd.h> +#include	<stdlib.h> +#include	<string.h> +#include	<ctype.h> + + +extern void rfc2045_enomem(); + +static int mkreply(struct rfc2045_mkreplyinfo *); +static int mkforward(struct rfc2045_mkreplyinfo *); + +static void mksalutation_datefmt(const char *fmt_start, +				 const char *fmt_end, +				 const char *date, +				 void (*callback_func)(const char *, +						       size_t, void *), +				 void *callback_arg) +{ +	time_t t; + +	if (!fmt_start) +	{ +		fmt_start="%a, %d %b %Y %H:%M:%S %z"; +		fmt_end=fmt_start + strlen(fmt_start); +	} + +	if ((t=rfc822_parsedt(date))) +	{ +		struct tm tmbuf; + +		if (localtime_r(&t, &tmbuf)) +		{ +			char *fmtstr=malloc(fmt_end-fmt_start + 1); + +			if (fmtstr) +			{ +				char fmtbuf[1024]; + +				memcpy(fmtstr, fmt_start, fmt_end - fmt_start); +				fmtstr[fmt_end-fmt_start]=0; + +				fmtbuf[strftime(fmtbuf, +						sizeof(fmtbuf)-1, +						fmtstr, &tmbuf)]=0; + +				free(fmtstr); +				(*callback_func)(fmtbuf, strlen(fmtbuf), +						 callback_arg); +				return; +			} +		} +	} +	(*callback_func)(date, strlen(date), callback_arg); +} + +static void mksalutation_cb(const char *salutation_template, +			    const char *newsgroup, +			    const char *message_id, +			    const char *newsgroups, +			    const char *sender_addr, +			    const char *sender_name, +			    const char *date, +			    const char *subject, + +			    void (*callback_func)(const char *, size_t, void *), +			    void *callback_arg) +{ +	const char *p; + +	for (p=salutation_template; *p; p++) +	{ +		const char *fmt_start=0, *fmt_end=0; + +		if (*p != '%' || p[1] == '\0') +		{ +			(*callback_func)( p, 1, callback_arg ); +			continue; +		} + +		++p; + +		if (*p == '{') +		{ +			fmt_start= ++p; + +			while (*p) +			{ +				if (*p == '}') +				{ +					fmt_end=p; +					++p; +					break; +				} +			} + +			if (!fmt_end) +				continue; +		} + +#define CBSTR(s) s, strlen(s), callback_arg + +		switch (*p)	{ +		case 'n': +			(*callback_func)("\n", 1, callback_arg ); +			continue; +		case 'C': +			(*callback_func)(CBSTR(newsgroup)); +			break; +		case 'i': +			(*callback_func)(CBSTR(message_id)); +			break; +		case 'N': +			(*callback_func)(CBSTR(newsgroups)); +			break; +		case 'f': +			(*callback_func)(CBSTR(sender_addr)); +			break; +		case 'F': +			(*callback_func)(CBSTR(sender_name)); +			break; +		case 'd': +			mksalutation_datefmt(fmt_start, +					     fmt_end, +					     date, +					     callback_func, callback_arg); +			break; +		case 's': +			(*callback_func)(CBSTR(subject)); +			break; +		default: +			(*callback_func)(p, 1, callback_arg); +			break; +		} +#undef CBSTR + +	} +} + +static void mksal_count(const char *str, +			size_t cnt, +			void *arg) +{ +	*(size_t *)arg += cnt; +} + +static void mksal_save(const char *str, +		       size_t cnt, +		       void *arg) +{ +	if (cnt) +		memcpy(*(char **)arg, str, cnt); + +	*(char **)arg += cnt; +} + +static char *mksalutation(const char *salutation_template, +			  const char *newsgroup, +			  const char *message_id, +			  const char *newsgroups, +			  const char *sender_addr, +			  const char *sender_name, +			  const char *date, +			  const char *subject, +			  const char *charset) +{ +	size_t cnt; +	char *p, *q; + +	char *subj_decoded=rfc822_display_hdrvalue_tobuf("subject", subject, +							 charset, NULL, NULL); + +	if (!subj_decoded) +		return NULL; + +	cnt=1; + +	mksalutation_cb(salutation_template, +			newsgroup, +			message_id, +			newsgroups, +			sender_addr, +			sender_name, +			date, +			subj_decoded, +			mksal_count, &cnt); + +	p=q=malloc(cnt); + +	if (!p) +	{ +		free(subj_decoded); +		return NULL; +	} + +	mksalutation_cb(salutation_template, +			newsgroup, +			message_id, +			newsgroups, +			sender_addr, +			sender_name, +			date, +			subj_decoded, +			mksal_save, &q); +	*q=0; + +	free(subj_decoded); +	return p; +} + + +int rfc2045_makereply(struct rfc2045_mkreplyinfo *ri) +{ +	if (strcmp(ri->replymode, "forward") == 0 +	    || strcmp(ri->replymode, "forwardatt") == 0) +		return (mkforward(ri)); + +	return (mkreply(ri)); +} + +struct replyinfostruct { + +	struct rfc2045_mkreplyinfo *ri; +	rfc3676_parser_t parser; + +	size_t quote_level_adjust; +	size_t quote_level; +	int start_line; +	int isflowed; +	size_t trailing_spaces; +	libmail_u_convert_handle_t u_handle; + +}; + +/* +** Pass original content to the RFC 3676 parser +*/ + +static int quotereply(const char *p, size_t l, void *voidptr) +{ +	struct replyinfostruct *ris=(struct replyinfostruct *)voidptr; + +	return rfc3676parser(ris->parser, p, l); +} + +/* +** Push formatted reply downstream. +*/ + +static int output_reply(const char *ptr, size_t cnt, void *arg) +{ +	struct replyinfostruct *s=(struct replyinfostruct *)arg; + +	(*s->ri->write_func)(ptr, cnt, s->ri->voidarg); +	return 0; +} + +/* +** RFC 3676 parser: Start of a new line in the reply. +*/ +static int reply_begin(size_t quote_level, +		       void *arg) +{ +	struct replyinfostruct *s=(struct replyinfostruct *)arg; +	unicode_char quoteChar='>'; + +	/* +	** Save quote level, begin conversion from unicode to the native +	** charset. +	*/ +	s->quote_level=quote_level+s->quote_level_adjust; + +	s->u_handle=libmail_u_convert_init(libmail_u_ucs4_native, +					   s->ri->charset, +					   output_reply, +					   s); + +	/* +	** Emit quoting indentation, if any. +	*/ +	s->start_line=1; +	s->trailing_spaces=0; + +	if (s->u_handle) +	{ +		size_t i; + +		for (i=0; i<s->quote_level; i++) +		{ +			libmail_u_convert_uc(s->u_handle, "eChar, 1); +		} +	} +	return 0; +} + +/* +** RFC 3676: (possibly partial) contents of a deflowed line, as unicode. +*/ + +static int reply_contents(const unicode_char *txt, +			  size_t txt_size, +			  void *arg) +{ +	unicode_char spaceChar=' '; +	size_t nonspc_cnt; + +	struct replyinfostruct *s=(struct replyinfostruct *)arg; + +	if (!s->u_handle || txt_size == 0) +		return 0; + +	/* +	** Space-stuff the initial character. +	*/ + +	if (s->start_line) +	{ +		if (!s->isflowed) +		{ +			/* +			** If the original content is not flowed, the rfc3676 +			** parser does not parse the number of > quote +			** characters and does not set the quote level. +			*/ + +			if ((s->quote_level > 0 && *txt != '>') || *txt == ' ') +				libmail_u_convert_uc(s->u_handle, +						     &spaceChar, 1); + +		} +		else +		{ +			if (s->quote_level > 0 || *txt == ' ' || *txt == '>') +				libmail_u_convert_uc(s->u_handle, +						     &spaceChar, 1); +		} +		s->start_line=0; +	} + +	/* +	** Trim any trailing spaces from the RFC 3676 parsed content. +	*/ + +	for (nonspc_cnt=txt_size; nonspc_cnt > 0; --nonspc_cnt) +		if (txt[nonspc_cnt-1] != ' ') +			break; + +	/* +	** If the contents are not totally whitespace, it's ok now to emit +	** any accumulated whitespace from previous content. +	*/ + +	if (nonspc_cnt) +	{ +		while (s->trailing_spaces) +		{ +			libmail_u_convert_uc(s->u_handle, &spaceChar, 1); +			--s->trailing_spaces; +		} + +		libmail_u_convert_uc(s->u_handle, txt, nonspc_cnt); +	} + +	s->trailing_spaces += txt_size - nonspc_cnt; +	return 0; +} + +static int reply_end(void *arg) +{ +	unicode_char newLine='\n'; +	struct replyinfostruct *s=(struct replyinfostruct *)arg; + +	libmail_u_convert_uc(s->u_handle, &newLine, 1); + +	libmail_u_convert_deinit(s->u_handle, NULL); +	return 0; +} + +/* +** RFC 3676 parser: flowed line break. Replicate it. +*/ +static int reply_wrap(void *arg) +{ +	unicode_char spaceChar=' '; +	struct replyinfostruct *s=(struct replyinfostruct *)arg; + +	/* +	** It's safe to preserve trailing spaces on flowed lines. +	*/ + +	while (s->trailing_spaces) +	{ +		libmail_u_convert_uc(s->u_handle, &spaceChar, 1); +		--s->trailing_spaces; +	} + +	libmail_u_convert_uc(s->u_handle, &spaceChar, 1); +	reply_end(s); +	reply_begin(s->quote_level-s->quote_level_adjust, s); +	/* Undo the adjustment in reply_begin() */ + +	return 0; +} + +static void reformat(struct rfc2045_mkreplyinfo *ri, struct rfc2045 *rfc, +		     size_t adjustLevel) +{ +	struct replyinfostruct ris; + +	struct rfc3676_parser_info info; +	int conv_err; + +	ris.ri=ri; +	ris.quote_level_adjust=adjustLevel; + +	memset(&info, 0, sizeof(info)); + +	info.charset=ri->charset; +	ris.isflowed=info.isflowed=rfc2045_isflowed(rfc); +	info.isdelsp=rfc2045_isdelsp(rfc); + +	info.line_begin=reply_begin; +	info.line_contents=reply_contents; +	info.line_flowed_notify=reply_wrap; +	info.line_end=reply_end; +	info.arg=&ris; + +	if ((ris.parser=rfc3676parser_init(&info)) != NULL) +	{ +		rfc2045_decodetextmimesection(ri->src, rfc, +					      ri->charset, +					      &conv_err, +					      quotereply, +					      &ris); +		rfc3676parser_deinit(ris.parser, NULL); +	} +} + +static void	replybody(struct rfc2045_mkreplyinfo *ri, struct rfc2045 *rfc) +{ +	rfc=rfc2045_searchcontenttype(rfc, "text/plain"); + +	if (!rfc) +		return; + +	reformat(ri, rfc, 1); +} + +static void writes(struct rfc2045_mkreplyinfo *ri, const char *c) +{ +	(*ri->write_func)(c, strlen(c), ri->voidarg); +} + +static void forwardbody(struct rfc2045_mkreplyinfo *ri, long nbytes) +{ +	char	buf[BUFSIZ]; +	ssize_t i; + +	while ((i=nbytes > sizeof(buf) ? sizeof(buf):nbytes) > 0 && +	       (i=SRC_READ(ri->src, buf, i)) > 0) +	{ +		nbytes -= i; +		(*ri->write_func)(buf, i, ri->voidarg); +	} +} + +/* +** Format a forward +*/ +static int mkforward(struct rfc2045_mkreplyinfo *ri) +{ +	off_t	start_pos, end_pos, start_body; +	off_t	dummy; + +	char	*header, *value; +	char	*subject=0; + +	char	*boundary=0; + +	struct rfc2045headerinfo *hi; + +	struct	rfc2045 *textplain_content; +	struct	rfc2045 *first_attachment; +	int attachment_is_message_rfc822; + +	/* +	** Use the original message's subject to set the subject of the +	** forward message. +	*/ + +	hi=rfc2045header_start(ri->src, ri->rfc2045partp); + +	if (!hi) +		return (-1); + +	for (;;) +	{ +		if (rfc2045header_get(hi, &header, &value, 0)) +		{ +			rfc2045header_end(hi); +			return (-1); +		} + +		if (!header) +			break; +		if (strcmp(header, "subject") == 0) +		{ +			if (subject)	free(subject); + +			subject=strdup(value); +			if (!subject) +			{ +				rfc2045header_end(hi); +				return (-1); +			} +		} +	} + +	rfc2045header_end(hi); + +	writes(ri, "Subject: "); + +	if (ri->subject) +	{ +		/* +		** ... unless the caller overrides it. +		*/ + +		writes(ri, ri->subject); +	} +	else if (subject) +	{ +		char	*s=rfc822_coresubj_keepblobs(subject); + +		if (!s) +			return (-1); + +		writes(ri, s); +		free(s); +		writes(ri, " (fwd)"); +	} +	writes(ri, "\nMime-Version: 1.0\n"); + +	/* +	** To assemble a forward template, two things are needed: +	** +	** 1. The original message, as text/plain. +	** +	** 2. Any attachments in the original message. +	**    A. The attachments get either copied to the forward message, or +	**    B. The original message is attached as a single message/rfc822 +	**       entity. +	** +	** 2b is always produced by "forwardatt". If a suitable text/plain +	** part of the original message could not be found, 2b is also +	** produced even by "forward". +	*/ + +	textplain_content=NULL; + +	attachment_is_message_rfc822=0; +	first_attachment=NULL; + +	{ +		const char *content_type, *dummy; + +		struct rfc2045 *top_part=ri->rfc2045partp; + +		rfc2045_mimeinfo(top_part, +				 &content_type, &dummy, &dummy); + +		if (strcmp(content_type, "multipart/signed") == 0) +		{ +			struct rfc2045 *p=top_part->firstpart; + +			if (p && p->isdummy) +				p=p->next; + +			if (p) +			{ +				top_part=p; +				rfc2045_mimeinfo(top_part, +						 &content_type, &dummy, &dummy); +			} +		} +		else if (strcmp(content_type, "multipart/x-mimegpg") == 0) +		{ +			struct rfc2045 *p=top_part->firstpart; + +			if (p && p->isdummy) +				p=p->next; + +			if (p) +			{ +				const char *part_ct; + +				rfc2045_mimeinfo(p, +						 &part_ct, &dummy, &dummy); + +				if (strcmp(part_ct, "text/x-gpg-output") == 0 +				    && p->next) +				{ +					top_part=p->next; +					rfc2045_mimeinfo(top_part, +							 &content_type, +							 &dummy, &dummy); +				} +			} +		} + +		if (strcmp(content_type, "text/plain") == 0) +		{ +			textplain_content=top_part; +		} +		else if (strcmp(content_type, "multipart/alternative") == 0) +		{ +			textplain_content= +				rfc2045_searchcontenttype(top_part, +							  "text/plain"); +		} +		else if (strcmp(content_type, "multipart/mixed") == 0) +		{ +			struct rfc2045 *p=top_part->firstpart; + +			if (p->isdummy) +				p=p->next; + +			textplain_content= +				rfc2045_searchcontenttype(p, "text/plain"); + +			/* +			** If the first part contained a suitable text/plain, +			** any remaining MIME parts become attachments that +			** get copied to the forward message. +			*/ +			if (textplain_content) +				first_attachment=p->next; +		} + +		if (strcmp(ri->replymode, "forwardatt") == 0 || +		    textplain_content == NULL) +		{ +			/* +			** Copy the entire message as the sole message/rfc822 +			** attachment in the forward message. +			*/ +			textplain_content=NULL; +			first_attachment=top_part; +			attachment_is_message_rfc822=1; +		} +	} + +	boundary=strdup(rfc2045_mk_boundary(ri->rfc2045partp, ri->src)); +	if (!boundary) +	{ +		if (subject)	free(subject); +		return (-1); +	} + +	if (first_attachment) +	{ +		writes(ri, "Content-Type: multipart/mixed; boundary=\""); +		writes(ri, boundary); +		writes(ri, "\"\n\n"); +		writes(ri, RFC2045MIMEMSG); +		writes(ri, "\n--"); +		writes(ri, boundary); +		writes(ri, "\n"); +	} + +	if (ri->content_set_charset) +	{ +		(*ri->content_set_charset)(ri->voidarg); +	} +	else +	{ +		writes(ri, "Content-Type: text/plain; format=flowed; delsp=yes; charset=\""); +		writes(ri, ri->charset); +		writes(ri, "\"\n"); +	} + +	writes(ri, "Content-Transfer-Encoding: 8bit\n\n"); +	if (ri->content_specify) +		(*ri->content_specify)(ri->voidarg); + +	writes(ri, "\n"); +	if (ri->writesig_func) +		(*ri->writesig_func)(ri->voidarg); +	writes(ri, "\n"); + +	if (ri->forwardsep) +	{ +		writes(ri, ri->forwardsep); +		writes(ri, "\n"); +	} + +	if (textplain_content) +	{ +		/* Copy original headers. */ +		 +		hi=rfc2045header_start(ri->src, ri->rfc2045partp); +		for (;;) +		{ +			if (rfc2045header_get(hi, &header, &value, +					      RFC2045H_NOLC|RFC2045H_KEEPNL)) +			{ +				rfc2045header_end(hi); +				break; +			} +			if (!header) +				break; +			if (strcasecmp(header, "subject") == 0 || +			    strcasecmp(header, "from") == 0 || +			    strcasecmp(header, "to") == 0 || +			    strcasecmp(header, "cc") == 0 || +			    strcasecmp(header, "date") == 0 || +			    strcasecmp(header, "message-id") == 0 || +			    strcasecmp(header, "resent-from") == 0 || +			    strcasecmp(header, "resent-to") == 0 || +			    strcasecmp(header, "resent-cc") == 0 || +			    strcasecmp(header, "resent-date") == 0 || +			    strcasecmp(header, "resent-message-id") == 0) +			{ +				if (subject) free(subject); + +				subject=rfc822_display_hdrvalue_tobuf(header, +								      value, +								      ri->charset, +								      NULL, +								      NULL); + +				if (subject) +				{ +					(*ri->write_func)(header, +							  strlen(header), +							  ri->voidarg); +					(*ri->write_func)(": ", 2, ri->voidarg); +					(*ri->write_func)(subject, +							  strlen(subject), +							  ri->voidarg); +					(*ri->write_func)("\n", 1, ri->voidarg); +				} +			} +		} +		rfc2045header_end(hi); +		(*ri->write_func)("\n", 1, ri->voidarg); + +		reformat(ri, textplain_content, 0); +	} + +	if (first_attachment) +	{ +		/* +		** There are attachments to copy +		*/ + +		if (attachment_is_message_rfc822) +		{ +			/* Copy everything as a message/rfc822 */ + +			writes(ri, "\n--"); +			writes(ri, boundary); +			writes(ri, "\nContent-Type: message/rfc822\n"); + +			if (ri->forwarddescr) +			{ +				char *p=rfc2047_encode_str(ri->forwarddescr, +							   ri->charset ? +							   ri->charset +							   : "iso-8859-1", +							   rfc2047_qp_allow_any +							   ); + +				writes(ri, "Content-Description: "); +				writes(ri, p ? p:""); +				free(p); +				writes(ri, "\n"); +			} + +			writes(ri, "\n"); + +			rfc2045_mimepos(first_attachment, &start_pos, &end_pos, +					&start_body, +					&dummy, &dummy); +			 +			if (SRC_SEEK(ri->src, start_pos) == (off_t)-1) +			{ +				if (subject) free(subject); +				free(boundary); +				return -1; +			} +			forwardbody(ri, end_pos - start_pos); +		} +		else +		{ +			/* Copy over individual attachments, one by one */ + +			for (; first_attachment; +			     first_attachment=first_attachment->next) +			{ +				writes(ri, "\n--"); +				writes(ri, boundary); +				writes(ri, "\n"); + +				rfc2045_mimepos(first_attachment, &start_pos, +						&end_pos, +						&start_body, +						&dummy, &dummy); +			 +				if (SRC_SEEK(ri->src, start_pos) == (off_t)-1) +				{ +					if (subject) free(subject); +					free(boundary); +					return -1; +				} + +				forwardbody(ri, end_pos - start_pos); +			} +		} + +		writes(ri, "\n--"); +		writes(ri, boundary); +		writes(ri, "--\n"); +	} + +	if (subject) free(subject); +	free(boundary); +	return (0); +} + + +static int writereferences(struct rfc2045_mkreplyinfo *ri, +			    const char *oldref, const char *oldmsgid) +{ +char	*buf=malloc((oldref ? strlen(oldref):0) +		+ (oldmsgid ? strlen(oldmsgid):0)+2); +char	*p, *q; +struct	rfc822t *tp; +struct	rfc822a *ap; +int	i; + +	if (!buf) +		return (-1); + +	/* Create new references header */ +	*buf=0; +	if (oldref)	strcat(buf, oldref); +	if (oldref && oldmsgid)	strcat(buf, " "); +	if (oldmsgid)	strcat(buf, oldmsgid); + +	/* Do wrapping the RIGHT way, by +	** RFC822 parsing the References: header +	*/ + +	if ((tp=rfc822t_alloc_new(buf, NULL, NULL)) == 0 || +		(ap=rfc822a_alloc(tp)) == 0) +	{ +		free(buf); +		if (tp) +			rfc822t_free(tp); +		return (-1); +	} + +	/* Keep only the last 20 message IDs */ + +	i=0; +	if (ap->naddrs > 20)	i=ap->naddrs-20; +	p=""; +	while (i < ap->naddrs) +	{ +		q=rfc822_gettok(ap->addrs[i].tokens); +		if (!q) +		{ +			rfc822a_free(ap); +			rfc822t_free(tp); +			free(buf); +			return (-1); +		} + +		writes(ri, p); +		writes(ri, "<"); +		writes(ri, q); +		writes(ri, ">\n"); +		p="            "; +		free(q); +		i++; +	} +	rfc822a_free(ap); +	rfc822t_free(tp); +	free(buf); +	return (0); +} + +static char *mlcheck(struct rfc2045_mkreplyinfo *ri, const char *); + +static int replydsn(struct rfc2045_mkreplyinfo *); +static int replyfeedback(struct rfc2045_mkreplyinfo *); + +static int mkreply(struct rfc2045_mkreplyinfo *ri) +{ +	char	*oldtocc, *oldfrom, *oldreplyto, *oldtolist; +	char	*subject; +	char	*oldmsgid; +	char	*oldreferences; +	char	*oldenvelope; +	char	*header, *value; +	char	*date; +	char	*newsgroup; +	char	*newsgroups; + +	char	*whowrote; +	off_t	start_pos, end_pos, start_body, dummy; +	int errflag=0; +	char	*boundary; +	char	*dsn_report_type; +	int	(*dsn_report_gen)(struct rfc2045_mkreplyinfo *); + +	struct rfc2045headerinfo *hi; + +	oldtocc=0; +	oldtolist=0; +	oldfrom=0; +	oldreplyto=0; +	subject=0; +	oldmsgid=0; +	oldreferences=0; +	oldenvelope=0; +	whowrote=0; +	newsgroup=0; +	newsgroups=0; +	date=0; + +	rfc2045_mimepos(ri->rfc2045partp, &start_pos, &end_pos, &start_body, +		&dummy, &dummy); + +	hi=rfc2045header_start(ri->src, ri->rfc2045partp); + +	if (!hi) +		return (-1); + +#define BLOWUP { \ +		if (whowrote) free(whowrote); \ +		if (subject) free(subject); \ +		if (oldmsgid) free(oldmsgid); \ +		if (oldreferences)	free(oldreferences); \ +		if (oldtocc) free(oldtocc); \ +		if (oldtolist) free(oldtolist); \ +		if (oldfrom) free(oldfrom); \ +		if (oldreplyto)	free(oldreplyto); \ +		if (oldenvelope) free(oldenvelope); \ +		if (newsgroup) free(newsgroup); \ +		if (newsgroups) free(newsgroups); \ +		if (date) free(date); \ +		rfc2045header_end(hi); \ +		return (-1); \ +	} + +	for (;;) +	{ +		if (rfc2045header_get(hi, &header, &value, 0)) +		{ +			BLOWUP; +			return (-1); +		} + +		if (!header) +			break; + +		if (strcmp(header, "subject") == 0) +		{ +			if (subject)	free(subject); + +			subject=strdup(value); +			if (!subject) +				BLOWUP; +		} +		else if (strcmp(header, "reply-to") == 0) +		{ +			if (oldreplyto)	free(oldreplyto); +			oldreplyto=strdup(value); +			if (!oldreplyto) +				BLOWUP; +		} +		else if (strcmp(header, "from") == 0) +		{ +			if (oldfrom)	free(oldfrom); +			oldfrom=strdup(value); +			if (!oldfrom) +				BLOWUP; +		} +		else if (strcmp(header, "message-id") == 0) +		{ +			if (oldmsgid)	free(oldmsgid); +			oldmsgid=strdup(value); +			if (!oldmsgid) +				BLOWUP; +		} +		else if (strcmp(header, "references") == 0) +		{ +			if (oldreferences)	free(oldreferences); +			oldreferences=strdup(value); +			if (!oldreferences) +				BLOWUP; +		} +		else if ((strcmp(header, "return-path") == 0 || +			  strcmp(header, "errors-to") == 0) && +			 ri->replytoenvelope) +		{ +			if (oldenvelope)	free(oldenvelope); +			oldenvelope=strdup(value); +			if (!oldenvelope) +				BLOWUP; +		} +		else if (strcmp(header, "newsgroups") == 0) +		{ +			if (newsgroups)	free(newsgroups); +			newsgroups=strdup(value); +			if (!newsgroups) +				BLOWUP; +		} +		else if (strcmp(header, "x-newsgroup") == 0) +		{ +			if (newsgroup)	free(newsgroup); +			newsgroup=strdup(value); +			if (!newsgroup) +				BLOWUP; +		} +		else if (strcmp(header, "date") == 0) +		{ +			if (date)	free(date); +			date=strdup(value); +			if (!date) +				BLOWUP; +		} +		else if ((strcmp(ri->replymode, "replyall") == 0 +			  || strcmp(ri->replymode, "replylist") == 0) && +			 ( +			  strcmp(header, "to") == 0 || +			  strcmp(header, "cc") == 0 +			  ) +			 ) +		{ +			char	*newh=malloc( (oldtocc ? +					       strlen(oldtocc):0) +					      + strlen(value)+2); +			char	*p; + +				if (!newh) +					BLOWUP; + +				*newh=0; +				if (oldtocc) +					strcat(strcpy(newh, oldtocc), +								","); +				strcat(newh, value); +				if (oldtocc)	free(oldtocc); +				oldtocc=newh; + +				p=mlcheck(ri, value); +				if (!p || (newh=malloc((oldtolist ? +						       strlen(oldtolist):0) +					   + strlen(p)+2)) == NULL) +				{ +					if (p) +						free(p); +					BLOWUP; +				} + +				if (*p) +				{ +					*newh=0; +					if (oldtolist) +						strcat(strcpy(newh, oldtolist), +						       ","); +					strcat(newh, p); +					if (oldtolist) +						free(oldtolist); +					oldtolist=newh; +				} +				free(p); +		} +	} + +	rfc2045header_end(hi); + +	/* Write:  "%s writes:" */ + +	{ +		struct	rfc822t *rfcp=rfc822t_alloc_new(oldfrom ? oldfrom:"", +							NULL, NULL); +		struct	rfc822a *rfcpa; +		char	*sender_name=NULL; +		char	*sender_addr=NULL; +		int	n; + +		if (!rfcp) +			BLOWUP; + +		rfcpa=rfc822a_alloc(rfcp); +		if (!rfcpa) +		{ +			rfc822t_free(rfcp); +			BLOWUP; +		} + +		for (n=0; n<rfcpa->naddrs; ++n) +		{ +			if (rfcpa->addrs[n].tokens == NULL) +				continue; + +			sender_name=rfc822_display_name_tobuf(rfcpa, n, +							      ri->charset); +			sender_addr=rfc822_display_addr_tobuf(rfcpa, n, +							      ri->charset); +			break; +		} + +		rfc822a_free(rfcpa); +		rfc822t_free(rfcp); + +		whowrote=mksalutation(ri->replysalut, +				      newsgroup ? newsgroup:"", +				      oldmsgid ? oldmsgid:"", +				      newsgroups ? newsgroups:"", + +				      sender_addr ? sender_addr:"(no address given)", +				      sender_name ? sender_name:sender_addr, +				      date, +				      subject, +				      ri->charset); + +		if (sender_name) +			free(sender_name); +		if (sender_addr) +			free(sender_addr); + +		if (!whowrote) +		{ +			BLOWUP; +		} +	} + +	if (newsgroups) +		free(newsgroups); +	if (newsgroup) +		free(newsgroup); +	if (date) +		free(date); +	if (oldreplyto) +	{ +		if (oldfrom)	free(oldfrom); +		oldfrom=oldreplyto; +		oldreplyto=0; +	} + +	if (oldenvelope) +	{ +		if (oldfrom)	free(oldfrom); +		oldfrom=oldenvelope; +		oldenvelope=0; +	} + +	/* +	** Replytolist: if we found mailing list addresses, drop +	** oldtocc, we'll use oldtolist. +	** Otherwise, drop oldtolist. +	*/ + +	if (strcmp(ri->replymode, "replylist") == 0) +	{ +		if (oldtolist) +		{ +			if (oldtocc) +			{ +				free(oldtocc); +				oldtocc=0; +			} + +			if (oldfrom) +			{ +				free(oldfrom); +				oldfrom=0; +			} +		} +	} +	else +	{ +		if (oldtolist) +		{ +			free(oldtolist); +			oldtolist=NULL; +		} +	} + +	/* Remove duplicate entries from new Cc header */ + +	if (oldtocc) +	{ +		struct rfc822t	*rfccc, *rfcto; +		struct rfc822a	*arfccc, *arfcto; +		int	i, j; +		char	*new_addresses; + +		rfccc=rfc822t_alloc_new(oldtocc, NULL, NULL); +		rfcto= oldfrom ? rfc822t_alloc_new(oldfrom, NULL, +						   NULL):NULL; +		arfccc=rfccc ? rfc822a_alloc(rfccc):NULL; +		arfcto=rfcto ? rfc822a_alloc(rfcto):NULL; + +		for (i=0; arfccc && i <arfccc->naddrs; i++) +		{ +			char	*addr=rfc822_getaddr(arfccc, i); + +			if (!addr)	continue; + +				/* Remove address from Cc if it is my address */ + +			if ( (ri->myaddr_func)(addr, ri->voidarg)) +			{ +				rfc822_deladdr(arfccc, i); --i; +				free(addr); +				continue; +			} + +				/* Remove address from Cc if it appears in To: */ + +			for (j=0; arfcto && j < arfcto->naddrs; j++) +			{ +				char *addr2=rfc822_getaddr(arfcto, j); + +				if (!addr2)	continue; +				if (strcmp(addr, addr2) == 0) +				{ +					free(addr2); +					break; +				} +				free(addr2); +			} +			if (arfcto && j < arfcto->naddrs) +			{ +				rfc822_deladdr(arfccc, i); --i; +				free(addr); +				continue; +			} + +				/* Remove outright duplicates in Cc */ + +			for (j=i+1; j<arfccc->naddrs; j++) +			{ +				char *addr2=rfc822_getaddr(arfccc, j); + +				if (!addr2)	continue; +				if (strcmp(addr, addr2) == 0) +				{ +					rfc822_deladdr(arfccc, j); +					--j; +				} +				free(addr2); +			} +			free(addr); +		} +		new_addresses=rfc822_getaddrs(arfccc); +		free(oldtocc); +		oldtocc=new_addresses; +		if (arfccc)	rfc822a_free(arfccc); +		if (arfcto)	rfc822a_free(arfcto); +		rfc822t_free(rfccc); +		if (rfcto) rfc822t_free(rfcto); +	} + +	if (strcmp(ri->replymode, "feedback") == 0) +	{ +		if (oldtolist) +		{ +			free(oldtolist); +			oldtolist=NULL; +		} + +		if (oldfrom) +		{ +			free(oldfrom); +			oldfrom=NULL; +		} + +		if (oldtocc) +		{ +			free(oldtocc); +			oldtocc=NULL; +		} +	} + +	if (oldtolist) +	{ +		writes(ri, "To: "); +		writes(ri, oldtolist); +		writes(ri, "\n"); +		free(oldtolist); +	} + +	if (oldfrom) +	{ +		writes(ri, "To: "); +		writes(ri, oldfrom); +		writes(ri, "\n"); +		free(oldfrom); +	} + +	if (oldtocc) +	{ +		writes(ri, "Cc: "); +		writes(ri, oldtocc); +		writes(ri, "\n"); +		free(oldtocc); +	} + +	if (oldmsgid || oldreferences) +	{ +		writes(ri, "References: "); +		if (writereferences(ri, oldreferences, oldmsgid)) +			errflag= -1; +		if (oldreferences)	free(oldreferences); +	} +	if (oldmsgid) +	{ +		writes(ri, "In-Reply-To: "); +		writes(ri, oldmsgid); +		writes(ri, "\n"); +		free(oldmsgid); +	} +	writes(ri,"Subject: "); + +	if (ri->subject) +	{ +		writes(ri, ri->subject); +	} +	else if (subject) +	{ +		if (strcmp(ri->replymode, "feedback") == 0 || +		    strcmp(ri->replymode, "replyfeedback") == 0) +		{ +			writes(ri, subject); +		} +		else +		{ +			char	*s=rfc822_coresubj_keepblobs(subject); + +			writes(ri, "Re: "); +			writes(ri, s ? s:""); +			if (s)	free(s); +		} +		free(subject); +	} + +	writes(ri, "\nMime-Version: 1.0\n"); + +	boundary=NULL; +	dsn_report_type=NULL; + +	if (strcmp(ri->replymode, "replydsn") == 0 && ri->dsnfrom) +	{ +		dsn_report_type="delivery-status"; +		dsn_report_gen=&replydsn; +	} +	else if (strcmp(ri->replymode, "replyfeedback") == 0 || +		 strcmp(ri->replymode, "feedback") == 0) +	{ +		dsn_report_type="feedback-report"; +		dsn_report_gen=&replyfeedback; +	} + +	if (dsn_report_type) +	{ +		boundary=rfc2045_mk_boundary(ri->rfc2045partp, ri->src); +		if (!boundary) +			return (-1); + +		writes(ri, "Content-Type: multipart/report;" +		       " report-type="); + +		writes(ri, dsn_report_type); +		writes(ri, ";\n    boundary=\""); + +		writes(ri, boundary); + +		writes(ri,"\"\n" +			"\n" +			RFC2045MIMEMSG +			"\n" +		       "--"); +		writes(ri, boundary); +		writes(ri, "\n"); +	} + +	if (ri->content_set_charset) +	{ +		(*ri->content_set_charset)(ri->voidarg); +	} +	else +	{ +		writes(ri, "Content-Type: text/plain; format=flowed; delsp=yes; charset=\""); +		writes(ri, ri->charset); +		writes(ri, "\"\n"); +	} +	writes(ri, "Content-Transfer-Encoding: 8bit\n\n"); + +	if (!ri->donotquote) +	{ +		if (whowrote) +		{ +			writes(ri, whowrote); +			free(whowrote); +			writes(ri, "\n\n"); +		} +		if (SRC_SEEK(ri->src, start_body) == (off_t)-1) +			return (-1); + +		replybody(ri, ri->rfc2045partp); +		writes(ri, "\n");	/* First blank line in the reply */ +	} + +	if (ri->content_specify) +		(*ri->content_specify)(ri->voidarg); + +	writes(ri, "\n"); +	if (ri->writesig_func) +		(*ri->writesig_func)(ri->voidarg); +	writes(ri, "\n"); + +	if (boundary) +	{ +		/* replydsn or replyfeedback */ + +		char	*header, *value; +		struct rfc2045headerinfo *hi; + +		writes(ri, "\n--"); +		writes(ri, boundary); +		writes(ri, "\nContent-Type: message/"); + +		writes(ri, dsn_report_type); +		writes(ri, "\n" +		       "Content-Transfer-Encoding: 7bit\n\n"); + +		if (errflag == 0) +			errflag=(*dsn_report_gen)(ri); + +		writes(ri, "\n--"); +		writes(ri, boundary); + +		if (ri->fullmsg) +		{ +			off_t cnt=end_pos - start_pos; +			char buf[BUFSIZ]; + +			writes(ri, "\nContent-Type: message/rfc822\n" +			       "Content-Disposition: attachment\n\n"); + +			if (errflag == 0) +				errflag=SRC_SEEK(ri->src, start_pos); + +			while (errflag == 0 && cnt > 0) +			{ +				int n=cnt > sizeof(BUFSIZ) ? BUFSIZ:(int)cnt; + +				n=SRC_READ(ri->src, buf, n); + +				if (n <= 0) +				{ +					errflag= -1; +					break; +				} +				(*ri->write_func)(buf, n, ri->voidarg); +				cnt -= n; +			} +		} +		else +		{ +			writes(ri, "\nContent-Type: text/rfc822-headers; charset=\"iso-8859-1\"\n" +			       "Content-Disposition: attachment\n" +			       "Content-Transfer-Encoding: 8bit\n\n" +			       ); + +			hi=rfc2045header_start(ri->src, ri->rfc2045partp); + +			while (hi) +			{ +				if (rfc2045header_get(hi, &header, &value, +						      RFC2045H_NOLC) || !header) +				{ +					rfc2045header_end(hi); +					break; +				} + +				writes(ri, header); +				writes(ri, ": "); +				writes(ri, value); +				writes(ri, "\n"); +			} +		} +		writes(ri, "\n--"); +		writes(ri, boundary); +		writes(ri, "--\n"); +		free(boundary); +	} +	return (errflag); +} + +static void dsn_arrival_date(struct rfc2045_mkreplyinfo *ri) +{ +	writes(ri, "Arrival-Date: "); + +	time_t now; + +	time(&now); + +	writes(ri, rfc822_mkdate(now)); +	writes(ri, "\n"); +} + +static int replydsn(struct rfc2045_mkreplyinfo *ri) +{ +	dsn_arrival_date(ri); + +	writes (ri, "\n" +		"Final-Recipient: rfc822; "); + +	writes(ri, ri->dsnfrom); + +	writes(ri, "\n" +	       "Action: delivered\n" +	       "Status: 2.0.0\n"); +	return 0; +} + +static int replyfeedback(struct rfc2045_mkreplyinfo *ri) +{ +	size_t i; + +	dsn_arrival_date(ri); +	writes(ri, "User-Agent: librfc2045 " +	       RFC2045PKG "/" RFC2045VER +	       "\n" +	       "Version: 1\n"); + +	for (i=0; ri->feedbackheaders && +		     ri->feedbackheaders[i] && ri->feedbackheaders[i+1]; +	     i += 2) +	{ +		char *p=strdup(ri->feedbackheaders[i]), *q; +		char lastch; + +		if (!p) +			return -1; + +		lastch='-'; +		for (q=p; *q; q++) +		{ +			if (*q >= 'A' && *q <= 'Z') +				*q += 'a'-'A'; + +			if (lastch == '-' && *q >= 'a' && *q <= 'z') +				*q += 'A' - 'a'; +			lastch=*q; +		} + +		writes(ri, p); +		free(p); +		writes(ri, ": "); +		writes(ri, ri->feedbackheaders[i+1]); +		writes(ri, "\n"); +	} + +	return 0; +} + + +/* +** Accept a list of recipients, and return a list that contains only those +** recipients that are defined as mailing lists. +*/ + +static char *do_checkmailinglists(struct rfc822a *a, +				  struct rfc2045_mkreplyinfo *, +				  char *); + +static char *mlcheck(struct rfc2045_mkreplyinfo *ri, const char *hdr) +{ +	struct rfc822t *t; +	struct rfc822a *a; + +	char *mlcopy; +	char *p; + +	t=rfc822t_alloc_new(hdr, NULL, NULL); + +	if (!t) +		return (0); + +	a=rfc822a_alloc(t); + +	if (!a) +	{ +		rfc822t_free(t); +		return (0); +	} + +	mlcopy=strdup(ri->mailinglists ? ri->mailinglists:""); +	if (!mlcopy) +		p=0; +	else +	{ +		p=do_checkmailinglists(a, ri, mlcopy); +		free(mlcopy); +	} + +	rfc822a_free(a); +	rfc822t_free(t); +	return (p); +} + +static char *do_checkmailinglists(struct rfc822a *a, +				  struct rfc2045_mkreplyinfo *ri, +				  char *mlbuffer) +{ +	int i; + +	for (i=0; i<a->naddrs; i++) +	{ +		char *p=rfc822_getaddr(a, i); +		char *q; + +		if (!p) +			return (NULL); + +		strcpy(mlbuffer, ri->mailinglists ? ri->mailinglists:""); + +		for (q=mlbuffer; (q=strtok(q, "\n \t")) != NULL; q=NULL) +		{ +			if (strcasecmp(p, q) == 0) +				break; +		} + +		free(p); +		if (q == NULL) +		{ +			rfc822_deladdr(a, i); +			--i; +		} +	} + +	if (a->naddrs == 0) +		return (strdup("")); +	return (rfc822_getaddrs(a)); +} | 
