summaryrefslogtreecommitdiffstats
path: root/rfc2045/rfc2045reply.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /rfc2045/rfc2045reply.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'rfc2045/rfc2045reply.c')
-rw-r--r--rfc2045/rfc2045reply.c1667
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, &quoteChar, 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));
+}