1 /* $NetBSD: bounce_append_service.c,v 1.1.1.1 2009/06/23 10:08:42 tron 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 #ifdef STRCASECMP_IN_STRINGS_H 47 #include <strings.h> 48 #endif 49 50 /* Utility library. */ 51 52 #include <msg.h> 53 #include <vstring.h> 54 #include <vstream.h> 55 #include <stringops.h> 56 57 /* Global library. */ 58 59 #include <mail_params.h> 60 #include <mail_queue.h> 61 #include <quote_822_local.h> 62 #include <deliver_flock.h> 63 #include <mail_proto.h> 64 65 /* Application-specific. */ 66 67 #include "bounce_service.h" 68 69 /* bounce_append_service - append bounce log */ 70 71 int bounce_append_service(int unused_flags, char *service, char *queue_id, 72 RECIPIENT *rcpt, DSN *dsn) 73 { 74 VSTRING *in_buf = vstring_alloc(100); 75 VSTREAM *log; 76 long orig_length; 77 78 /* 79 * This code is paranoid for a good reason. Once the bounce service takes 80 * responsibility, the mail system will make no further attempts to 81 * deliver this recipient. Whenever file access fails, assume that the 82 * system is under stress or that something has been mis-configured, and 83 * force a backoff by raising a fatal run-time error. 84 */ 85 log = mail_queue_open(service, queue_id, 86 O_WRONLY | O_APPEND | O_CREAT, 0600); 87 if (log == 0) 88 msg_fatal("open file %s %s: %m", service, queue_id); 89 90 /* 91 * Lock out other processes to avoid truncating someone else's data in 92 * case of trouble. 93 */ 94 if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0) 95 msg_fatal("lock file %s %s: %m", service, queue_id); 96 97 /* 98 * Now, go for it. Append a record. Truncate the log to the original 99 * length when the append operation fails. We use the plain stream-lf 100 * file format because we do not need anything more complicated. As a 101 * benefit, we can still recover some data when the file is a little 102 * garbled. 103 * 104 * XXX addresses in defer logfiles are in printable quoted form, while 105 * addresses in message envelope records are in raw unquoted form. This 106 * may change once we replace the present ad-hoc bounce/defer logfile 107 * format by one that is transparent for control etc. characters. See 108 * also: showq/showq.c. 109 * 110 * While migrating from old format to new format, allow backwards 111 * compatibility by writing an old-style record before the new-style 112 * records. 113 */ 114 if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0) 115 msg_fatal("seek file %s %s: %m", service, queue_id); 116 117 #define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0) 118 #define STR(x) vstring_str(x) 119 120 vstream_fputs("\n", log); 121 if (var_oldlog_compat) { 122 vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" : 123 STR(quote_822_local(in_buf, rcpt->address)), 124 dsn->reason); 125 } 126 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ? 127 STR(quote_822_local(in_buf, rcpt->address)) : "<>"); 128 if (NOT_NULL_EMPTY(rcpt->orig_addr) 129 && strcasecmp(rcpt->address, rcpt->orig_addr) != 0) 130 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT, 131 STR(quote_822_local(in_buf, rcpt->orig_addr))); 132 if (rcpt->offset > 0) 133 vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset); 134 if (NOT_NULL_EMPTY(rcpt->dsn_orcpt)) 135 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt); 136 if (rcpt->dsn_notify != 0) 137 vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify); 138 139 if (NOT_NULL_EMPTY(dsn->status)) 140 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status); 141 if (NOT_NULL_EMPTY(dsn->action)) 142 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action); 143 if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) { 144 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype); 145 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext); 146 } 147 if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) { 148 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype); 149 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname); 150 } 151 if (NOT_NULL_EMPTY(dsn->reason)) 152 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason); 153 vstream_fputs("\n", log); 154 155 if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) { 156 #ifndef NO_TRUNCATE 157 if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0) 158 msg_fatal("truncate file %s %s: %m", service, queue_id); 159 #endif 160 msg_fatal("append file %s %s: %m", service, queue_id); 161 } 162 163 /* 164 * Darn. If closing the log detects a problem, the only way to undo the 165 * damage is to open the log once more, and to truncate the log to the 166 * original length. But, this could happen only when the log is kept on a 167 * remote file system, and that is not recommended practice anyway. 168 */ 169 if (vstream_fclose(log) != 0) 170 msg_warn("append file %s %s: %m", service, queue_id); 171 172 vstring_free(in_buf); 173 return (0); 174 } 175