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