Home | History | Annotate | Line # | Download | only in bounce
      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