xref: /netbsd-src/external/ibm-public/postfix/dist/src/bounce/bounce_append_service.c (revision b49cc1491953ef2348eff9c84520ffd0678a5c8d)
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