summaryrefslogtreecommitdiffstats
path: root/src/bounce/bounce_append_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bounce/bounce_append_service.c')
-rw-r--r--src/bounce/bounce_append_service.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/bounce/bounce_append_service.c b/src/bounce/bounce_append_service.c
new file mode 100644
index 0000000..c3cea0b
--- /dev/null
+++ b/src/bounce/bounce_append_service.c
@@ -0,0 +1,168 @@
+/*++
+/* NAME
+/* bounce_append_service 3
+/* SUMMARY
+/* append record to bounce log, server side
+/* SYNOPSIS
+/* #include "bounce_service.h"
+/*
+/* int bounce_append_service(flags, service, queue_id, rcpt, dsn),
+/* int flags;
+/* char *service;
+/* char *queue_id;
+/* RECIPIENT *rcpt;
+/* DSN *dsn;
+/* DESCRIPTION
+/* This module implements the server side of the bounce_append()
+/* (append bounce log) request. This routine either succeeds or
+/* it raises a fatal error.
+/* DIAGNOSTICS
+/* Fatal errors: all file access errors; memory allocation errors.
+/* BUGS
+/* SEE ALSO
+/* bounce(3) basic bounce service client interface
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_queue.h>
+#include <quote_822_local.h>
+#include <deliver_flock.h>
+#include <mail_proto.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+/* bounce_append_service - append bounce log */
+
+int bounce_append_service(int unused_flags, char *service, char *queue_id,
+ RECIPIENT *rcpt, DSN *dsn)
+{
+ VSTRING *in_buf = vstring_alloc(100);
+ VSTREAM *log;
+ long orig_length;
+
+ /*
+ * This code is paranoid for a good reason. Once the bounce service takes
+ * responsibility, the mail system will make no further attempts to
+ * deliver this recipient. Whenever file access fails, assume that the
+ * system is under stress or that something has been mis-configured, and
+ * force a backoff by raising a fatal run-time error.
+ */
+ log = mail_queue_open(service, queue_id,
+ O_WRONLY | O_APPEND | O_CREAT, 0600);
+ if (log == 0)
+ msg_fatal("open file %s %s: %m", service, queue_id);
+
+ /*
+ * Lock out other processes to avoid truncating someone else's data in
+ * case of trouble.
+ */
+ if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0)
+ msg_fatal("lock file %s %s: %m", service, queue_id);
+
+ /*
+ * Now, go for it. Append a record. Truncate the log to the original
+ * length when the append operation fails. We use the plain stream-lf
+ * file format because we do not need anything more complicated. As a
+ * benefit, we can still recover some data when the file is a little
+ * garbled.
+ *
+ * XXX addresses in defer logfiles are in printable quoted form, while
+ * addresses in message envelope records are in raw unquoted form. This
+ * may change once we replace the present ad-hoc bounce/defer logfile
+ * format by one that is transparent for control etc. characters. See
+ * also: showq/showq.c.
+ *
+ * While migrating from old format to new format, allow backwards
+ * compatibility by writing an old-style record before the new-style
+ * records.
+ */
+ if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0)
+ msg_fatal("seek file %s %s: %m", service, queue_id);
+
+#define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0)
+#define STR(x) vstring_str(x)
+
+ vstream_fputs("\n", log);
+ if (var_oldlog_compat) {
+ vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" :
+ STR(quote_822_local(in_buf, rcpt->address)),
+ dsn->reason);
+ }
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ?
+ STR(quote_822_local(in_buf, rcpt->address)) : "<>");
+ if (NOT_NULL_EMPTY(rcpt->orig_addr)
+ && strcasecmp_utf8(rcpt->address, rcpt->orig_addr) != 0)
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT,
+ STR(quote_822_local(in_buf, rcpt->orig_addr)));
+ if (rcpt->offset > 0)
+ vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset);
+ if (NOT_NULL_EMPTY(rcpt->dsn_orcpt))
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt);
+ if (rcpt->dsn_notify != 0)
+ vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify);
+
+ if (NOT_NULL_EMPTY(dsn->status))
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status);
+ if (NOT_NULL_EMPTY(dsn->action))
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action);
+ if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) {
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype);
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext);
+ }
+ if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) {
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype);
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname);
+ }
+ if (NOT_NULL_EMPTY(dsn->reason))
+ vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason);
+ vstream_fputs("\n", log);
+
+ if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) {
+#ifndef NO_TRUNCATE
+ if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0)
+ msg_fatal("truncate file %s %s: %m", service, queue_id);
+#endif
+ msg_fatal("append file %s %s: %m", service, queue_id);
+ }
+
+ /*
+ * Darn. If closing the log detects a problem, the only way to undo the
+ * damage is to open the log once more, and to truncate the log to the
+ * original length. But, this could happen only when the log is kept on a
+ * remote file system, and that is not recommended practice anyway.
+ */
+ if (vstream_fclose(log) != 0)
+ msg_warn("append file %s %s: %m", service, queue_id);
+
+ vstring_free(in_buf);
+ return (0);
+}