1/*++ 2/* NAME 3/* bounce_append_service 3 4/* SUMMARY 5/* append record to bounce log, server side 6/* SYNOPSIS 7/* #include "bounce_service.h" 8/* 9/* int bounce_append_service(flags, service, queue_id, rcpt, dsn), 10/* int flags; 11/* char *service; 12/* char *queue_id; 13/* RECIPIENT *rcpt; 14/* DSN *dsn; 15/* DESCRIPTION 16/* This module implements the server side of the bounce_append() 17/* (append bounce log) request. This routine either succeeds or 18/* it raises a fatal error. 19/* DIAGNOSTICS 20/* Fatal errors: all file access errors; memory allocation errors. 21/* BUGS 22/* SEE ALSO 23/* bounce(3) basic bounce service client interface 24/* LICENSE 25/* .ad 26/* .fi 27/* The Secure Mailer license must be distributed with this software. 28/* AUTHOR(S) 29/* Wietse Venema 30/* IBM T.J. Watson Research 31/* P.O. Box 704 32/* Yorktown Heights, NY 10598, USA 33/*--*/ 34 35/* System library. */ 36 37#include <sys_defs.h> 38#include <unistd.h> 39#include <fcntl.h> 40#include <errno.h> 41#include <ctype.h> 42#include <string.h> 43 44#ifdef STRCASECMP_IN_STRINGS_H 45#include <strings.h> 46#endif 47 48/* Utility library. */ 49 50#include <msg.h> 51#include <vstring.h> 52#include <vstream.h> 53#include <stringops.h> 54 55/* Global library. */ 56 57#include <mail_params.h> 58#include <mail_queue.h> 59#include <quote_822_local.h> 60#include <deliver_flock.h> 61#include <mail_proto.h> 62 63/* Application-specific. */ 64 65#include "bounce_service.h" 66 67/* bounce_append_service - append bounce log */ 68 69int bounce_append_service(int unused_flags, char *service, char *queue_id, 70 RECIPIENT *rcpt, DSN *dsn) 71{ 72 VSTRING *in_buf = vstring_alloc(100); 73 VSTREAM *log; 74 long orig_length; 75 76 /* 77 * This code is paranoid for a good reason. Once the bounce service takes 78 * responsibility, the mail system will make no further attempts to 79 * deliver this recipient. Whenever file access fails, assume that the 80 * system is under stress or that something has been mis-configured, and 81 * force a backoff by raising a fatal run-time error. 82 */ 83 log = mail_queue_open(service, queue_id, 84 O_WRONLY | O_APPEND | O_CREAT, 0600); 85 if (log == 0) 86 msg_fatal("open file %s %s: %m", service, queue_id); 87 88 /* 89 * Lock out other processes to avoid truncating someone else's data in 90 * case of trouble. 91 */ 92 if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0) 93 msg_fatal("lock file %s %s: %m", service, queue_id); 94 95 /* 96 * Now, go for it. Append a record. Truncate the log to the original 97 * length when the append operation fails. We use the plain stream-lf 98 * file format because we do not need anything more complicated. As a 99 * benefit, we can still recover some data when the file is a little 100 * garbled. 101 * 102 * XXX addresses in defer logfiles are in printable quoted form, while 103 * addresses in message envelope records are in raw unquoted form. This 104 * may change once we replace the present ad-hoc bounce/defer logfile 105 * format by one that is transparent for control etc. characters. See 106 * also: showq/showq.c. 107 * 108 * While migrating from old format to new format, allow backwards 109 * compatibility by writing an old-style record before the new-style 110 * records. 111 */ 112 if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0) 113 msg_fatal("seek file %s %s: %m", service, queue_id); 114 115#define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0) 116#define STR(x) vstring_str(x) 117 118 vstream_fputs("\n", log); 119 if (var_oldlog_compat) { 120 vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" : 121 STR(quote_822_local(in_buf, rcpt->address)), 122 dsn->reason); 123 } 124 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ? 125 STR(quote_822_local(in_buf, rcpt->address)) : "<>"); 126 if (NOT_NULL_EMPTY(rcpt->orig_addr) 127 && strcasecmp(rcpt->address, rcpt->orig_addr) != 0) 128 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT, 129 STR(quote_822_local(in_buf, rcpt->orig_addr))); 130 if (rcpt->offset > 0) 131 vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset); 132 if (NOT_NULL_EMPTY(rcpt->dsn_orcpt)) 133 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt); 134 if (rcpt->dsn_notify != 0) 135 vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify); 136 137 if (NOT_NULL_EMPTY(dsn->status)) 138 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status); 139 if (NOT_NULL_EMPTY(dsn->action)) 140 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action); 141 if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) { 142 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype); 143 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext); 144 } 145 if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) { 146 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype); 147 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname); 148 } 149 if (NOT_NULL_EMPTY(dsn->reason)) 150 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason); 151 vstream_fputs("\n", log); 152 153 if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) { 154#ifndef NO_TRUNCATE 155 if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0) 156 msg_fatal("truncate file %s %s: %m", service, queue_id); 157#endif 158 msg_fatal("append file %s %s: %m", service, queue_id); 159 } 160 161 /* 162 * Darn. If closing the log detects a problem, the only way to undo the 163 * damage is to open the log once more, and to truncate the log to the 164 * original length. But, this could happen only when the log is kept on a 165 * remote file system, and that is not recommended practice anyway. 166 */ 167 if (vstream_fclose(log) != 0) 168 msg_warn("append file %s %s: %m", service, queue_id); 169 170 vstring_free(in_buf); 171 return (0); 172} 173