summaryrefslogtreecommitdiffstats
path: root/headers.c
diff options
context:
space:
mode:
Diffstat (limited to 'headers.c')
-rw-r--r--headers.c906
1 files changed, 906 insertions, 0 deletions
diff --git a/headers.c b/headers.c
new file mode 100644
index 0000000..abf3bf5
--- /dev/null
+++ b/headers.c
@@ -0,0 +1,906 @@
+/*
+ * This file is part of libESMTP, a library for submission of RFC 2822
+ * formatted electronic mail messages using the SMTP protocol described
+ * in RFC 2821.
+ *
+ * Copyright (C) 2001,2002 Brian Stafford <brian@stafford.uklinux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include <missing.h>
+
+#include "libesmtp-private.h"
+#include "headers.h"
+#include "htable.h"
+#include "rfc2822date.h"
+#include "api.h"
+
+struct rfc2822_header
+ {
+ struct rfc2822_header *next;
+ struct header_info *info; /* Info for setting and printing */
+ char *header; /* Header name */
+ void *value; /* Header value */
+ };
+
+typedef int (*hdrset_t) (struct rfc2822_header *, va_list);
+typedef void (*hdrprint_t) (smtp_message_t, struct rfc2822_header *);
+typedef void (*hdrdestroy_t) (struct rfc2822_header *);
+
+struct header_actions
+ {
+ const char *name; /* Header for which the action is specified */
+ unsigned int flags; /* Header flags */
+ hdrset_t set; /* Function to set header value from API */
+ hdrprint_t print; /* Function to print the header value */
+ hdrdestroy_t destroy; /* Function to destroy the header value */
+ };
+
+struct header_info
+ {
+ const struct header_actions *action;
+ struct rfc2822_header *hdr; /* Pointer to most recently defined header */
+ unsigned int seen : 1; /* Header has been seen in the message */
+ unsigned int override : 1; /* LibESMTP is overriding the message */
+ unsigned int prohibit : 1; /* Header may not appear in the message */
+ };
+
+#define NELT(x) ((int) (sizeof x / sizeof x[0]))
+
+#define OPTIONAL 0
+#define SHOULD 1
+#define REQUIRE 2
+#define PROHIBIT 4
+#define PRESERVE 8
+#define LISTVALUE 16
+#define MULTIPLE 32
+
+static struct rfc2822_header *create_header (smtp_message_t message,
+ const char *header,
+ struct header_info *info);
+void destroy_string (struct rfc2822_header *header);
+void destroy_mbox_list (struct rfc2822_header *header);
+struct header_info *find_header (smtp_message_t message,
+ const char *name, int len);
+struct header_info *insert_header (smtp_message_t message, const char *name);
+
+/* RFC 2822 headers processing */
+
+/****************************************************************************
+ * Functions for setting and printing header values
+ ****************************************************************************/
+
+static int
+set_string (struct rfc2822_header *header, va_list alist)
+{
+ const char *value;
+
+ assert (header != NULL);
+
+ if (header->value != NULL) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const char *);
+ if (value == NULL)
+ return 0;
+ header->value = strdup (value);
+ return header->value != NULL;
+}
+
+static int
+set_string_null (struct rfc2822_header *header, va_list alist)
+{
+ const char *value;
+
+ assert (header != NULL);
+
+ if (header->value != NULL) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const char *);
+ if (value == NULL)
+ return 1;
+ header->value = strdup (value);
+ return header->value != NULL;
+}
+
+/* Print header-name ": " header-value "\r\n" */
+static void
+print_string (smtp_message_t message, struct rfc2822_header *header)
+{
+ assert (message != NULL && header != NULL);
+
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer, header->header, ": ",
+ (header->value != NULL) ? header->value : "", "\r\n", NULL);
+}
+
+void
+destroy_string (struct rfc2822_header *header)
+{
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ free (header->value);
+}
+
+/* Print header-name ": <" message-id ">\r\n" */
+static void
+print_message_id (smtp_message_t message, struct rfc2822_header *header)
+{
+ const char *message_id;
+ char buf[64];
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+#endif
+
+ assert (message != NULL && header != NULL);
+
+ message_id = header->value;
+ if (message_id == NULL)
+ {
+#ifdef HAVE_GETTIMEOFDAY
+ if (gettimeofday (&tv, NULL) == -1) /* This shouldn't fail ... */
+ snprintf (buf, sizeof buf, "%ld.%ld.%d@%s", tv.tv_sec, tv.tv_usec,
+ getpid (), message->session->localhost);
+ else /* ... but if it does fall back to using time() */
+#endif
+ snprintf (buf, sizeof buf, "%ld.%d@%s", time (NULL),
+ getpid (), message->session->localhost);
+ message_id = buf;
+ }
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer,
+ header->header, ": <", message_id, ">\r\n",
+ NULL);
+}
+
+/****/
+
+static int
+set_date (struct rfc2822_header *header, va_list alist)
+{
+ const time_t *value;
+
+ assert (header != NULL);
+
+ if ((time_t) header->value != (time_t) 0) /* Already set */
+ return 0;
+
+ value = va_arg (alist, const time_t *);
+ header->value = (void *) *value;
+ return 1;
+}
+
+/* Print header-name ": " formatted-date "\r\n" */
+static void
+print_date (smtp_message_t message, struct rfc2822_header *header)
+{
+ char buf[64];
+ time_t when;
+
+ assert (message != NULL && header != NULL);
+
+ when = (time_t) header->value;
+ if (when == (time_t) 0)
+ time (&when);
+ vconcatenate (&message->hdr_buffer, header->header, ": ",
+ rfc2822date (buf, sizeof buf, &when), "\r\n", NULL);
+}
+
+/****/
+
+struct mbox
+ {
+ struct mbox *next;
+ char *mailbox;
+ char *phrase;
+ };
+
+void
+destroy_mbox_list (struct rfc2822_header *header)
+{
+ struct mbox *mbox, *next;
+
+ assert (header != NULL);
+
+ mbox = header->value;
+ while (mbox != NULL)
+ {
+ next = mbox->next;
+ if (mbox->phrase != NULL)
+ free ((void *) mbox->phrase);
+ if (mbox->mailbox != NULL)
+ free ((void *) mbox->mailbox);
+ free (mbox);
+ mbox = next;
+ }
+}
+
+static int
+set_from (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+
+ /* Allow this to succeed as a special case. Effectively requesting
+ default action in print_from(). Fails if explicit values have
+ already been set. */
+ if (phrase == NULL && mailbox == NULL)
+ return header->value == NULL;
+
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ header->value = mbox;
+ return 1;
+}
+
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n" */
+static void
+print_from (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ /* TODO: implement line folding at white spaces */
+ if (header->value == NULL)
+ {
+ mailbox = message->reverse_path_mailbox;
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ "\r\n", NULL);
+ }
+ else
+ for (mbox = header->value; mbox != NULL; mbox = mbox->next)
+ {
+ mailbox = mbox->mailbox;
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ NULL);
+ else
+ vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
+ " <", (mailbox != NULL) ? mailbox : "", ">", NULL);
+ vconcatenate (&message->hdr_buffer,
+ (mbox->next != NULL) ? ",\r\n " : "\r\n", NULL);
+ }
+}
+
+/* Same arguments and syntax as from: except that only one value is
+ allowed. */
+static int
+set_sender (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ return 0;
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (phrase == NULL && mailbox == NULL)
+ return 0;
+
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+ mbox->next = NULL;
+
+ mbox->next = header->value;
+ return 1;
+}
+
+/* TODO: do nothing if the mailbox is NULL. Check this doesn't fool
+ the protocol engine into thinking it has seen end of file. */
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n"
+ */
+static void
+print_sender (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ mbox = header->value;
+ mailbox = mbox->mailbox;
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer,
+ (mailbox != NULL && *mailbox != '\0') ? mailbox : "<>",
+ "\r\n", NULL);
+ else
+ vconcatenate (&message->hdr_buffer, "\"", mbox->phrase, "\""
+ " <", (mailbox != NULL) ? mailbox : "", ">\r\n", NULL);
+}
+
+static int
+set_to (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (phrase == NULL && mailbox == NULL)
+ mbox = NULL;
+ else
+ {
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ }
+ header->value = mbox;
+ return 1;
+}
+
+static int
+set_cc (struct rfc2822_header *header, va_list alist)
+{
+ struct mbox *mbox;
+ const char *mailbox;
+ const char *phrase;
+
+ assert (header != NULL);
+
+ phrase = va_arg (alist, const char *);
+ mailbox = va_arg (alist, const char *);
+ if (mailbox == NULL)
+ return 0;
+ mbox = malloc (sizeof (struct mbox));
+ if (mbox == NULL)
+ return 0;
+ mbox->phrase = (phrase != NULL) ? strdup (phrase) : NULL;
+ mbox->mailbox = strdup (mailbox);
+
+ mbox->next = header->value;
+ header->value = mbox;
+ return 1;
+}
+
+/* Print header-name ": " mailbox "\r\n"
+ or header-name ": \"" phrase "\" <" mailbox ">\r\n"
+ ad nauseum. */
+static void
+print_cc (smtp_message_t message, struct rfc2822_header *header)
+{
+ struct mbox *mbox;
+
+ assert (message != NULL && header != NULL);
+
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ for (mbox = header->value; mbox != NULL; mbox = mbox->next)
+ {
+ if (mbox->phrase == NULL)
+ vconcatenate (&message->hdr_buffer, mbox->mailbox, NULL);
+ else
+ vconcatenate (&message->hdr_buffer,
+ "\"", mbox->phrase, "\" <", mbox->mailbox, ">",
+ NULL);
+ vconcatenate (&message->hdr_buffer,
+ (mbox->next != NULL) ? ",\r\n " : "\r\n", NULL);
+ }
+}
+
+/* As above but generate a default value from the recipient list.
+ */
+static void
+print_to (smtp_message_t message, struct rfc2822_header *header)
+{
+ smtp_recipient_t recipient;
+
+ assert (header != NULL);
+
+ if (header->value != NULL)
+ {
+ print_cc (message, header);
+ return;
+ }
+
+ /* TODO: implement line folding at white spaces */
+ vconcatenate (&message->hdr_buffer, header->header, ": ", NULL);
+ for (recipient = message->recipients;
+ recipient != NULL;
+ recipient = recipient->next)
+ vconcatenate (&message->hdr_buffer, recipient->mailbox,
+ (recipient->next != NULL) ? ",\r\n " : "\r\n",
+ NULL);
+}
+
+
+/* Header actions placed here to avoid the need for many akward forward
+ declarations for set_xxx/print_xxx. */
+
+static const struct header_actions header_actions[] =
+ {
+ /* This is the default header info for a simple string value.
+ */
+ { NULL, OPTIONAL,
+ set_string, print_string, destroy_string},
+ /* A number of headers should be present in every message
+ */
+ { "Date", REQUIRE,
+ set_date, print_date, NULL, },
+ { "From", REQUIRE,
+ set_from, print_from, destroy_mbox_list, },
+ /* Certain headers are added when a message is delivered and
+ should not be present in a message being posted or which
+ is in transit. If present in the message they will be stripped
+ and if specified by the API, the relevant APIs will fail. */
+ { "Return-Path", PROHIBIT, NULL, NULL, NULL, },
+ /* RFC 2298 - Delivering MTA may add the Original-Recipient: header
+ using DSN ORCPT parameter and may discard
+ Original-Recipient: headers present in the message.
+ No point in sending it then. */
+ { "Original-Recipient", PROHIBIT, NULL, NULL, NULL, },
+ /* MIME-*: and Content-*: are MIME headers and must not be generated
+ or processed by libESMTP. Similarly, Resent-*: and Received: must
+ be retained unaltered. */
+ { "Content-", PRESERVE, NULL, NULL, NULL, },
+ { "MIME-", PRESERVE, NULL, NULL, NULL, },
+ { "Resent-", PRESERVE, NULL, NULL, NULL, },
+ { "Received", PRESERVE, NULL, NULL, NULL, },
+ /* Headers which are optional but which are recommended to be
+ present. Default action is to provide a default unless the
+ application explicitly requests not to. */
+ { "Message-Id", SHOULD,
+ set_string_null,print_message_id, destroy_string, },
+ /* Remaining headers are known to libESMTP to simplify handling them
+ for the application. All other headers are reaated as simple
+ string values. */
+ { "Sender", OPTIONAL,
+ set_sender, print_sender, destroy_mbox_list, },
+ { "To", OPTIONAL,
+ set_to, print_to, destroy_mbox_list, },
+ { "Cc", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ { "Bcc", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ { "Reply-To", OPTIONAL,
+ set_cc, print_cc, destroy_mbox_list, },
+ /* RFC 2298 - MDN request. Syntax is the same as the From: header and
+ default when set to NULL is the same as From: */
+ { "Disposition-Notification-To", OPTIONAL,
+ set_from, print_from, destroy_mbox_list, },
+ /* TODO:
+ In-Reply-To: *(phrase / msgid)
+ References: *(phrase / msgid)
+ Keywords: #phrase
+
+ Handle Resent- versions of
+ To Cc Bcc Message-ID Date Reply-To From Sender
+ */
+ };
+
+static int
+init_header_table (smtp_message_t message)
+{
+ int i;
+ struct header_info *hi;
+
+ assert (message != NULL);
+
+ if (message->hdr_action != NULL)
+ return -1;
+
+ message->hdr_action = h_create ();
+ if (message->hdr_action == NULL)
+ return 0;
+ for (i = 0; i < NELT (header_actions); i++)
+ if (header_actions[i].name != NULL)
+ {
+ hi = h_insert (message->hdr_action, header_actions[i].name, -1,
+ sizeof (struct header_info));
+ if (hi == NULL)
+ return 0;
+ hi->action = &header_actions[i];
+
+ /* REQUIREd headers must be present in the message. SHOULD
+ means the header is optional but its presence is recommended.
+ Create a NULL valued header. This will either be set later
+ with the API, or the print_xxx function will handle the NULL
+ value as a special case, e.g, the To: header is generated
+ from the recipient_t list. */
+ if (hi->action->flags & (REQUIRE | SHOULD))
+ {
+ if (create_header (message, header_actions[i].name, hi) == NULL)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+destroy_header_table (smtp_message_t message)
+{
+ struct rfc2822_header *header, *next;
+
+ assert (message != NULL);
+
+ /* Take out the linked list */
+ for (header = message->headers; header!= NULL; header = next)
+ {
+ next = header->next;
+ if (header->info->action->destroy != NULL)
+ (*header->info->action->destroy) (header);
+ free (header->header);
+ free (header);
+ }
+
+ /* Take out the hash table */
+ if (message->hdr_action != NULL)
+ {
+ h_destroy (message->hdr_action, NULL, NULL);
+ message->hdr_action = NULL;
+ }
+
+ message->headers = message->end_headers = NULL;
+}
+
+struct header_info *
+find_header (smtp_message_t message, const char *name, int len)
+{
+ struct header_info *info;
+ const char *p;
+
+ assert (message != NULL && name != NULL);
+
+ if (len < 0)
+ len = strlen (name);
+ if (len == 0)
+ return NULL;
+ info = h_search (message->hdr_action, name, len);
+ if (info == NULL && (p = memchr (name, '-', len)) != NULL)
+ info = h_search (message->hdr_action, name, p - name + 1);
+ return info;
+}
+
+struct header_info *
+insert_header (smtp_message_t message, const char *name)
+{
+ struct header_info *info;
+
+ assert (message != NULL && name != NULL);
+
+ info = h_insert (message->hdr_action, name, -1, sizeof (struct header_info));
+ if (info == NULL)
+ return NULL;
+ info->action = &header_actions[0];
+ return info;
+}
+
+static struct rfc2822_header *
+create_header (smtp_message_t message, const char *header,
+ struct header_info *info)
+{
+ struct rfc2822_header *hdr;
+
+ assert (message != NULL && header != NULL && info != NULL);
+
+ if ((hdr = malloc (sizeof (struct rfc2822_header))) == NULL)
+ return NULL;
+
+ memset (hdr, 0, sizeof (struct rfc2822_header));
+ hdr->header = strdup (header);
+ hdr->info = info;
+ info->hdr = hdr;
+ APPEND_LIST (message->headers, message->end_headers, hdr);
+ return hdr;
+}
+
+/****************************************************************************
+ * Header processing
+ ****************************************************************************/
+
+/* Called just before copying the messge from the application.
+ Resets the seen flag for headers libESMTP is interested in */
+
+static void
+reset_headercb (const char *name __attribute__ ((unused)),
+ void *data, void *arg __attribute__ ((unused)))
+{
+ struct header_info *info = data;
+
+ assert (info != NULL);
+
+ info->seen = 0;
+}
+
+int
+reset_header_table (smtp_message_t message)
+{
+ int status;
+
+ assert (message != NULL);
+
+ if ((status = init_header_table (message)) < 0)
+ h_enumerate (message->hdr_action, reset_headercb, NULL);
+ return status;
+}
+
+/* This is called to process headers present in the application supplied
+ message. */
+const char *
+process_header (smtp_message_t message, const char *header, int *len)
+{
+ const char *p;
+ struct header_info *info;
+ const struct header_actions *action;
+ hdrprint_t print;
+
+ assert (message != NULL && header != NULL && len != NULL);
+
+ if (*len > 0
+ && (p = memchr (header, ':', *len)) != NULL
+ && (info = find_header (message, header, p - header)) != NULL)
+ {
+ if ((action = info->action) != NULL)
+ {
+ /* RFC 2822 states that headers may only appear once in a
+ message with the exception of a few special headers.
+ This restriction is enforced here. */
+ if (info->seen && !(action->flags & (MULTIPLE | PRESERVE)))
+ header = NULL;
+ if (info->prohibit || (action->flags & PROHIBIT))
+ header = NULL;
+
+ /* When libESMTP is overriding headers in the message with
+ ones supplied in the API, the substitution is done here
+ to preserve the original ordering of the headers. */
+ if (header != NULL && info->override)
+ {
+ if ((print = action->print) == NULL)
+ print = print_string;
+ cat_reset (&message->hdr_buffer, 0);
+ (*print) (message, info->hdr);
+ header = cat_buffer (&message->hdr_buffer, len);
+ }
+ }
+ else if (info->seen)
+ header = NULL;
+ info->seen = 1;
+ }
+ return header;
+}
+
+/* This is called to supply headers not present in the application supplied
+ message. */
+const char *
+missing_header (smtp_message_t message, int *len)
+{
+ struct header_info *info;
+ hdrprint_t print;
+
+ assert (message != NULL && len != NULL);
+
+ /* Move on to the next header */
+ if (message->current_header == NULL)
+ message->current_header = message->headers;
+ else
+ message->current_header = message->current_header->next;
+
+ /* Look for the next header that is actually required */
+ print = NULL;
+ while (message->current_header != NULL)
+ {
+ info = message->current_header->info;
+ if (info == NULL) /* shouldn't happen */
+ break;
+ if (!info->seen)
+ {
+ if (info->action != NULL)
+ print = info->action->print;
+ break;
+ }
+ message->current_header = message->current_header->next;
+ }
+ if (message->current_header == NULL)
+ {
+ /* Free the buffer created by concatenate() and return NULL to
+ mark the end of the headers */
+ cat_free (&message->hdr_buffer);
+ return NULL;
+ }
+
+ if (print == NULL)
+ print = print_string;
+
+ cat_reset (&message->hdr_buffer, 0);
+ (*print) (message, message->current_header);
+ return cat_buffer (&message->hdr_buffer, len);
+}
+
+/****************************************************************************
+ * Header API
+ ****************************************************************************/
+
+int
+smtp_set_header (smtp_message_t message, const char *header, ...)
+{
+ va_list alist;
+ struct rfc2822_header *hdr;
+ struct header_info *info;
+ hdrset_t set;
+
+ SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
+
+ if (!init_header_table (message))
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ info = find_header (message, header, -1);
+ if (info == NULL && (info = insert_header (message, header)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ /* Cannot specify a value for headers that must pass unchanged (MIME)
+ or which may not appear in a posted message (Return-Path:). */
+ if (info->prohibit || (info->action->flags & (PROHIBIT | PRESERVE)))
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ set = info->action->set;
+ if (set == NULL)
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ if (info->hdr == NULL)
+ hdr = create_header (message, header, info);
+ else if (info->hdr->value == NULL)
+ hdr = info->hdr;
+ else
+ {
+ /* Header has a previous value. If multiple headers are permitted,
+ create a new value. If the header has a list value, the value
+ is appended to the iost. If neither condition applies, this
+ is an error. */
+ if (info->action->flags & MULTIPLE)
+ hdr = create_header (message, header, info);
+ else if (info->action->flags & LISTVALUE)
+ hdr = info->hdr;
+ else
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+ }
+
+ /* Set its value */
+ va_start (alist, header);
+ (*set) (hdr, alist);
+ va_end (alist);
+
+ return 1;
+}
+
+int
+smtp_set_header_option (smtp_message_t message, const char *header,
+ enum header_option option, ...)
+{
+ va_list alist;
+ struct header_info *info;
+
+ SMTPAPI_CHECK_ARGS (message != NULL && header != NULL, 0);
+
+ if (!init_header_table (message))
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ info = find_header (message, header, -1);
+ if (info == NULL && (info = insert_header (message, header)) == NULL)
+ {
+ set_errno (ENOMEM);
+ return 0;
+ }
+
+ /* Don't permit options to be set on headers that must pass intact or
+ which are prohibited. */
+ if (info->action->flags & (PROHIBIT | PRESERVE))
+ {
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+ }
+
+ /* There is an odd quirk when setting options. Setting an option for
+ the OPTIONAL headers known to libESMTP causes default values to be
+ generated automatically when not found in the message, so long as
+ there is no other reason to prevent them appearing in the message! */
+
+ /* Don't allow the user to set override on prohibited headers. */
+ if (option == Hdr_OVERRIDE && !info->prohibit)
+ {
+ va_start (alist, option);
+ info->override = !!va_arg (alist, int);
+ va_end (alist);
+ return 1;
+ }
+ /* Don't allow the user to prohibit required headers. */
+ if (option == Hdr_PROHIBIT && !(info->action->flags & REQUIRE))
+ {
+ va_start (alist, option);
+ info->prohibit = !!va_arg (alist, int);
+ va_end (alist);
+ return 1;
+ }
+
+ set_error (SMTP_ERR_INVAL);
+ return 0;
+}
+
+int
+smtp_set_resent_headers (smtp_message_t message, int onoff)
+{
+ SMTPAPI_CHECK_ARGS (message != NULL, 0);
+
+ /* TODO: place holder, implement real functionality here.
+ For now, succeed if the onoff argument is zero. */
+ SMTPAPI_CHECK_ARGS (onoff == 0, 0);
+
+ return 1;
+}