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