aboutsummaryrefslogtreecommitdiffstats
path: root/bounce.c
diff options
context:
space:
mode:
Diffstat (limited to 'bounce.c')
-rw-r--r--bounce.c818
1 files changed, 0 insertions, 818 deletions
diff --git a/bounce.c b/bounce.c
deleted file mode 100644
index e6fc5578..00000000
--- a/bounce.c
+++ /dev/null
@@ -1,818 +0,0 @@
-/* $OpenBSD: bounce.c,v 1.82 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define BOUNCE_MAXRUN 2
-#define BOUNCE_HIWAT 65535
-
-enum {
- BOUNCE_EHLO,
- BOUNCE_MAIL,
- BOUNCE_RCPT,
- BOUNCE_DATA,
- BOUNCE_DATA_NOTICE,
- BOUNCE_DATA_MESSAGE,
- BOUNCE_DATA_END,
- BOUNCE_QUIT,
- BOUNCE_CLOSE,
-};
-
-struct bounce_envelope {
- TAILQ_ENTRY(bounce_envelope) entry;
- uint64_t id;
- struct mailaddr dest;
- char *report;
- uint8_t esc_class;
- uint8_t esc_code;
-};
-
-struct bounce_message {
- SPLAY_ENTRY(bounce_message) sp_entry;
- TAILQ_ENTRY(bounce_message) entry;
- uint32_t msgid;
- struct delivery_bounce bounce;
- char *smtpname;
- char *to;
- time_t timeout;
- TAILQ_HEAD(, bounce_envelope) envelopes;
-};
-
-struct bounce_session {
- char *smtpname;
- struct bounce_message *msg;
- FILE *msgfp;
- int state;
- struct io *io;
- uint64_t boundary;
-};
-
-SPLAY_HEAD(bounce_message_tree, bounce_message);
-static int bounce_message_cmp(const struct bounce_message *,
- const struct bounce_message *);
-SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,
- bounce_message_cmp);
-
-static void bounce_drain(void);
-static void bounce_send(struct bounce_session *, const char *, ...);
-static int bounce_next_message(struct bounce_session *);
-static int bounce_next(struct bounce_session *);
-static void bounce_delivery(struct bounce_message *, int, const char *);
-static void bounce_status(struct bounce_session *, const char *, ...);
-static void bounce_io(struct io *, int, void *);
-static void bounce_timeout(int, short, void *);
-static void bounce_free(struct bounce_session *);
-static const char *action_str(const struct delivery_bounce *);
-
-static struct tree wait_fd;
-static struct bounce_message_tree messages;
-static TAILQ_HEAD(, bounce_message) pending;
-
-static int nmessage = 0;
-static int running = 0;
-static struct event ev_timer;
-
-static void
-bounce_init(void)
-{
- static int init = 0;
-
- if (init == 0) {
- TAILQ_INIT(&pending);
- SPLAY_INIT(&messages);
- tree_init(&wait_fd);
- evtimer_set(&ev_timer, bounce_timeout, NULL);
- init = 1;
- }
-}
-
-void
-bounce_add(uint64_t evpid)
-{
- char buf[LINE_MAX], *line;
- struct envelope evp;
- struct bounce_message key, *msg;
- struct bounce_envelope *be;
-
- bounce_init();
-
- if (queue_envelope_load(evpid, &evp) == 0) {
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
- return;
- }
-
- if (evp.type != D_BOUNCE)
- errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
- evp.id);
-
- key.msgid = evpid_to_msgid(evpid);
- key.bounce = evp.agent.bounce;
- key.smtpname = evp.smtpname;
-
- switch (evp.esc_class) {
- case ESC_STATUS_OK:
- key.bounce.type = B_DELIVERED;
- break;
- case ESC_STATUS_TEMPFAIL:
- key.bounce.type = B_DELAYED;
- break;
- default:
- key.bounce.type = B_FAILED;
- }
-
- key.bounce.dsn_ret = evp.dsn_ret;
- key.bounce.ttl = evp.ttl;
- msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
- if (msg == NULL) {
- msg = xcalloc(1, sizeof(*msg));
- msg->msgid = key.msgid;
- msg->bounce = key.bounce;
-
- TAILQ_INIT(&msg->envelopes);
-
- msg->smtpname = xstrdup(evp.smtpname);
- (void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
- evp.sender.domain);
- msg->to = xstrdup(buf);
- nmessage += 1;
- SPLAY_INSERT(bounce_message_tree, &messages, msg);
- log_debug("debug: bounce: new message %08" PRIx32,
- msg->msgid);
- stat_increment("bounce.message", 1);
- } else
- TAILQ_REMOVE(&pending, msg, entry);
-
- line = evp.errorline;
- if (strlen(line) > 4 && (*line == '1' || *line == '6'))
- line += 4;
- (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user,
- evp.dest.domain, line);
-
- be = xmalloc(sizeof *be);
- be->id = evpid;
- be->report = xstrdup(buf);
- (void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user));
- (void)strlcpy(be->dest.domain, evp.dest.domain,
- sizeof(be->dest.domain));
- be->esc_class = evp.esc_class;
- be->esc_code = evp.esc_code;
- TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
- log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report);
-
- msg->timeout = time(NULL) + 1;
- TAILQ_INSERT_TAIL(&pending, msg, entry);
-
- stat_increment("bounce.envelope", 1);
- bounce_drain();
-}
-
-void
-bounce_fd(int fd)
-{
- struct bounce_session *s;
- struct bounce_message *msg;
-
- log_debug("debug: bounce: got enqueue socket %d", fd);
-
- if (fd == -1 || TAILQ_EMPTY(&pending)) {
- log_debug("debug: bounce: cancelling");
- if (fd != -1)
- close(fd);
- running -= 1;
- bounce_drain();
- return;
- }
-
- msg = TAILQ_FIRST(&pending);
-
- s = xcalloc(1, sizeof(*s));
- s->smtpname = xstrdup(msg->smtpname);
- s->state = BOUNCE_EHLO;
- s->io = io_new();
- io_set_callback(s->io, bounce_io, s);
- io_set_fd(s->io, fd);
- io_set_timeout(s->io, 30000);
- io_set_read(s->io);
- s->boundary = generate_uid();
-
- log_debug("debug: bounce: new session %p", s);
- stat_increment("bounce.session", 1);
-}
-
-static void
-bounce_timeout(int fd, short ev, void *arg)
-{
- log_debug("debug: bounce: timeout");
-
- bounce_drain();
-}
-
-static void
-bounce_drain()
-{
- struct bounce_message *msg;
- struct timeval tv;
- time_t t;
-
- log_debug("debug: bounce: drain: nmessage=%d running=%d",
- nmessage, running);
-
- while (1) {
- if (running >= BOUNCE_MAXRUN) {
- log_debug("debug: bounce: max session reached");
- return;
- }
-
- if (nmessage == 0) {
- log_debug("debug: bounce: no more messages");
- return;
- }
-
- if (running >= nmessage) {
- log_debug("debug: bounce: enough sessions running");
- return;
- }
-
- if ((msg = TAILQ_FIRST(&pending)) == NULL) {
- log_debug("debug: bounce: no more pending messages");
- return;
- }
-
- t = time(NULL);
- if (msg->timeout > t) {
- log_debug("debug: bounce: next message not ready yet");
- if (!evtimer_pending(&ev_timer, NULL)) {
- log_debug("debug: bounce: setting timer");
- tv.tv_sec = msg->timeout - t;
- tv.tv_usec = 0;
- evtimer_add(&ev_timer, &tv);
- }
- return;
- }
-
- log_debug("debug: bounce: requesting new enqueue socket...");
- m_compose(p_pony, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0);
-
- running += 1;
- }
-}
-
-static void
-bounce_send(struct bounce_session *s, const char *fmt, ...)
-{
- va_list ap;
- char *p;
- int len;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&p, fmt, ap)) == -1)
- fatal("bounce: vasprintf");
- va_end(ap);
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
-
- io_xprintf(s->io, "%s\r\n", p);
-
- free(p);
-}
-
-static const char *
-bounce_duration(long long int d)
-{
- static char buf[32];
-
- if (d < 60) {
- (void)snprintf(buf, sizeof buf, "%lld second%s", d,
- (d == 1) ? "" : "s");
- } else if (d < 3600) {
- d = d / 60;
- (void)snprintf(buf, sizeof buf, "%lld minute%s", d,
- (d == 1) ? "" : "s");
- }
- else if (d < 3600 * 24) {
- d = d / 3600;
- (void)snprintf(buf, sizeof buf, "%lld hour%s", d,
- (d == 1) ? "" : "s");
- }
- else {
- d = d / (3600 * 24);
- (void)snprintf(buf, sizeof buf, "%lld day%s", d,
- (d == 1) ? "" : "s");
- }
- return (buf);
-}
-
-#define NOTICE_INTRO \
- " Hi!\r\n\r\n" \
- " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n"
-
-const char *notice_error =
- " An error has occurred while attempting to deliver a message for\r\n"
- " the following list of recipients:\r\n\r\n";
-
-const char *notice_warning =
- " A message is delayed for more than %s for the following\r\n"
- " list of recipients:\r\n\r\n";
-
-const char *notice_warning2 =
- " Please note that this is only a temporary failure report.\r\n"
- " The message is kept in the queue for up to %s.\r\n"
- " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n";
-
-const char *notice_success =
- " Your message was successfully delivered to these recipients.\r\n\r\n";
-
-const char *notice_relay =
- " Your message was relayed to these recipients.\r\n\r\n";
-
-static int
-bounce_next_message(struct bounce_session *s)
-{
- struct bounce_message *msg;
- char buf[LINE_MAX];
- int fd;
- time_t now;
-
- again:
-
- now = time(NULL);
-
- TAILQ_FOREACH(msg, &pending, entry) {
- if (msg->timeout > now)
- continue;
- if (strcmp(msg->smtpname, s->smtpname))
- continue;
- break;
- }
- if (msg == NULL)
- return (0);
-
- TAILQ_REMOVE(&pending, msg, entry);
- SPLAY_REMOVE(bounce_message_tree, &messages, msg);
-
- if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
- bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
- "Could not open message fd");
- goto again;
- }
-
- if ((s->msgfp = fdopen(fd, "r")) == NULL) {
- (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
- log_warn("warn: bounce: fdopen");
- close(fd);
- bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf);
- goto again;
- }
-
- s->msg = msg;
- return (1);
-}
-
-static int
-bounce_next(struct bounce_session *s)
-{
- struct bounce_envelope *evp;
- char *line = NULL;
- size_t n, sz = 0;
- ssize_t len;
-
- switch (s->state) {
- case BOUNCE_EHLO:
- bounce_send(s, "EHLO %s", s->smtpname);
- s->state = BOUNCE_MAIL;
- break;
-
- case BOUNCE_MAIL:
- case BOUNCE_DATA_END:
- log_debug("debug: bounce: %p: getting next message...", s);
- if (bounce_next_message(s) == 0) {
- log_debug("debug: bounce: %p: no more messages", s);
- bounce_send(s, "QUIT");
- s->state = BOUNCE_CLOSE;
- break;
- }
- log_debug("debug: bounce: %p: found message %08"PRIx32,
- s, s->msg->msgid);
- bounce_send(s, "MAIL FROM: <>");
- s->state = BOUNCE_RCPT;
- break;
-
- case BOUNCE_RCPT:
- bounce_send(s, "RCPT TO: <%s>", s->msg->to);
- s->state = BOUNCE_DATA;
- break;
-
- case BOUNCE_DATA:
- bounce_send(s, "DATA");
- s->state = BOUNCE_DATA_NOTICE;
- break;
-
- case BOUNCE_DATA_NOTICE:
- /* Construct an appropriate notice. */
-
- io_xprintf(s->io,
- "Subject: Delivery status notification: %s\r\n"
- "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n"
- "To: %s\r\n"
- "Date: %s\r\n"
- "MIME-Version: 1.0\r\n"
- "Content-Type: multipart/mixed;"
- "boundary=\"%16" PRIu64 "/%s\"\r\n"
- "\r\n"
- "This is a MIME-encapsulated message.\r\n"
- "\r\n",
- action_str(&s->msg->bounce),
- s->smtpname,
- s->msg->to,
- time_to_text(time(NULL)),
- s->boundary,
- s->smtpname);
-
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Notification\r\n"
- "Content-Type: text/plain; charset=us-ascii\r\n"
- "\r\n"
- NOTICE_INTRO
- "\r\n",
- s->boundary, s->smtpname);
-
- switch (s->msg->bounce.type) {
- case B_FAILED:
- io_xprint(s->io, notice_error);
- break;
- case B_DELAYED:
- io_xprintf(s->io, notice_warning,
- bounce_duration(s->msg->bounce.delay));
- break;
- case B_DELIVERED:
- io_xprint(s->io, s->msg->bounce.mta_without_dsn ?
- notice_relay : notice_success);
- break;
- default:
- log_warn("warn: bounce: unknown bounce_type");
- }
-
- TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
- io_xprint(s->io, evp->report);
- io_xprint(s->io, "\r\n");
- }
- io_xprint(s->io, "\r\n");
-
- if (s->msg->bounce.type == B_DELAYED)
- io_xprintf(s->io, notice_warning2,
- bounce_duration(s->msg->bounce.ttl));
-
- io_xprintf(s->io,
- " Below is a copy of the original message:\r\n"
- "\r\n");
-
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Delivery Report\r\n"
- "Content-Type: message/delivery-status\r\n"
- "\r\n",
- s->boundary, s->smtpname);
-
- io_xprintf(s->io,
- "Reporting-MTA: dns; %s\r\n"
- "\r\n",
- s->smtpname);
-
- TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
- io_xprintf(s->io,
- "Final-Recipient: rfc822; %s@%s\r\n"
- "Action: %s\r\n"
- "Status: %s\r\n"
- "\r\n",
- evp->dest.user,
- evp->dest.domain,
- action_str(&s->msg->bounce),
- esc_code(evp->esc_class, evp->esc_code));
- }
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
- s, io_queued(s->io));
-
- s->state = BOUNCE_DATA_MESSAGE;
- break;
-
- case BOUNCE_DATA_MESSAGE:
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Message headers\r\n"
- "Content-Type: text/rfc822-headers\r\n"
- "\r\n",
- s->boundary, s->smtpname);
-
- n = io_queued(s->io);
- while (io_queued(s->io) < BOUNCE_HIWAT) {
- if ((len = getline(&line, &sz, s->msgfp)) == -1)
- break;
- if (len == 1 && line[0] == '\n' && /* end of headers */
- s->msg->bounce.type == B_DELIVERED &&
- s->msg->bounce.dsn_ret == DSN_RETHDRS) {
- free(line);
- fclose(s->msgfp);
- s->msgfp = NULL;
- io_xprintf(s->io,
- "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary,
- s->smtpname);
- bounce_send(s, ".");
- s->state = BOUNCE_DATA_END;
- return (0);
- }
- line[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\r\n",
- (len == 2 && line[0] == '.') ? "." : "", line);
- }
- free(line);
-
- if (ferror(s->msgfp)) {
- fclose(s->msgfp);
- s->msgfp = NULL;
- bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
- "Error reading message");
- s->msg = NULL;
- return (-1);
- }
-
- io_xprintf(s->io,
- "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname);
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
- s, io_queued(s->io) - n);
-
- if (feof(s->msgfp)) {
- fclose(s->msgfp);
- s->msgfp = NULL;
- bounce_send(s, ".");
- s->state = BOUNCE_DATA_END;
- }
- break;
-
- case BOUNCE_QUIT:
- bounce_send(s, "QUIT");
- s->state = BOUNCE_CLOSE;
- break;
-
- default:
- fatalx("bounce: bad state");
- }
-
- return (0);
-}
-
-
-static void
-bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
-{
- struct bounce_envelope *be;
- struct envelope evp;
- size_t n;
- const char *f;
-
- n = 0;
- while ((be = TAILQ_FIRST(&msg->envelopes))) {
- if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
- if (queue_envelope_load(be->id, &evp) == 0) {
- fatalx("could not reload envelope!");
- }
- evp.retry++;
- evp.lasttry = msg->timeout;
- envelope_set_errormsg(&evp, "%s", status);
- queue_envelope_update(&evp);
- m_create(p_scheduler, delivery, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- } else {
- m_create(p_scheduler, delivery, 0, 0, -1);
- m_add_evpid(p_scheduler, be->id);
- m_close(p_scheduler);
- queue_envelope_delete(be->id);
- }
- TAILQ_REMOVE(&msg->envelopes, be, entry);
- free(be->report);
- free(be);
- n += 1;
- }
-
-
- if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL)
- f = "TempFail";
- else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL)
- f = "PermFail";
- else
- f = NULL;
-
- if (f)
- log_warnx("warn: %s injecting failure report on message %08"
- PRIx32 " to <%s> for %zu envelope%s: %s",
- f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
-
- nmessage -= 1;
- stat_decrement("bounce.message", 1);
- stat_decrement("bounce.envelope", n);
- free(msg->smtpname);
- free(msg->to);
- free(msg);
-}
-
-static void
-bounce_status(struct bounce_session *s, const char *fmt, ...)
-{
- va_list ap;
- char *status;
- int len, delivery;
-
- /* Ignore if there is no message */
- if (s->msg == NULL)
- return;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&status, fmt, ap)) == -1)
- fatal("bounce: vasprintf");
- va_end(ap);
-
- if (*status == '2')
- delivery = IMSG_QUEUE_DELIVERY_OK;
- else if (*status == '5' || *status == '6')
- delivery = IMSG_QUEUE_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL;
-
- bounce_delivery(s->msg, delivery, status);
- s->msg = NULL;
- if (s->msgfp)
- fclose(s->msgfp);
-
- free(status);
-}
-
-static void
-bounce_free(struct bounce_session *s)
-{
- log_debug("debug: bounce: %p: deleting session", s);
-
- io_free(s->io);
-
- free(s->smtpname);
- free(s);
-
- running -= 1;
- stat_decrement("bounce.session", 1);
- bounce_drain();
-}
-
-static void
-bounce_io(struct io *io, int evt, void *arg)
-{
- struct bounce_session *s = arg;
- const char *error;
- char *line, *msg;
- int cont;
- size_t len;
-
- log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_DATAIN:
- nextline:
- line = io_getline(s->io, &len);
- if (line == NULL && io_datalen(s->io) >= LINE_MAX) {
- bounce_status(s, "Input too long");
- bounce_free(s);
- return;
- }
-
- if (line == NULL)
- break;
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
-
- if ((error = parse_smtp_response(line, len, &msg, &cont))) {
- bounce_status(s, "Bad response: %s", error);
- bounce_free(s);
- return;
- }
- if (cont)
- goto nextline;
-
- if (s->state == BOUNCE_CLOSE) {
- bounce_free(s);
- return;
- }
-
- if (line[0] != '2' && line[0] != '3') { /* fail */
- bounce_status(s, "%s", line);
- s->state = BOUNCE_QUIT;
- } else if (s->state == BOUNCE_DATA_END) { /* accepted */
- bounce_status(s, "%s", line);
- }
-
- if (bounce_next(s) == -1) {
- bounce_free(s);
- return;
- }
-
- io_set_write(io);
- break;
-
- case IO_LOWAT:
- if (s->state == BOUNCE_DATA_MESSAGE)
- if (bounce_next(s) == -1) {
- bounce_free(s);
- return;
- }
- if (io_queued(s->io) == 0)
- io_set_read(io);
- break;
-
- default:
- bounce_status(s, "442 i/o error %d", evt);
- bounce_free(s);
- break;
- }
-}
-
-static int
-bounce_message_cmp(const struct bounce_message *a,
- const struct bounce_message *b)
-{
- int r;
-
- if (a->msgid < b->msgid)
- return (-1);
- if (a->msgid > b->msgid)
- return (1);
- if ((r = strcmp(a->smtpname, b->smtpname)))
- return (r);
-
- return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
-}
-
-static const char *
-action_str(const struct delivery_bounce *b)
-{
- switch (b->type) {
- case B_FAILED:
- return ("failed");
- case B_DELAYED:
- return ("delayed");
- case B_DELIVERED:
- if (b->mta_without_dsn)
- return ("relayed");
-
- return ("delivered");
- default:
- log_warn("warn: bounce: unknown bounce_type");
- return ("");
- }
-}
-
-SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
- bounce_message_cmp);