xref: /netbsd-src/external/ibm-public/postfix/dist/src/bounce/bounce_append_service.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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 
bounce_append_service(int unused_flags,char * service,char * queue_id,RECIPIENT * rcpt,DSN * dsn)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