/*++
/* NAME
/*	sent 3
/* SUMMARY
/*	log that a message was or could be sent
/* SYNOPSIS
/*	#include <sent.h>
/*
/*	int	sent(flags, queue_id, stats, recipient, relay, tstats, dsn)
/*	int	flags;
/*	const char *queue_id;
/*	MSG_STATS *stats;
/*	RECIPIENT *recipient;
/*	const char *relay;
/*	const POL_STATS *tstats;
/*	DSN *dsn;
/* DESCRIPTION
/*	sent() logs that a message was successfully delivered,
/*	updates the address verification service, or updates a
/*	sender-requested message delivery record. The
/*	flags argument determines the action.
/*
/*	Arguments:
/* .IP flags
/*	Zero or more of the following:
/* .RS
/* .IP SENT_FLAG_NONE
/*	The message is a normal delivery request.
/* .IP DEL_REQ_FLAG_MTA_VRFY
/*	The message is an MTA-requested address verification probe.
/*	Update the address verification database.
/* .IP DEL_REQ_FLAG_USR_VRFY
/*	The message is a user-requested address expansion probe.
/*	Update the message delivery record.
/* .IP DEL_REQ_FLAG_RECORD
/*	This is a normal message with logged delivery. Update the
/*	the message delivery record.
/* .RE
/* .IP queue_id
/*	The message queue id.
/* .IP stats
/*	Time stamps from different message delivery stages
/*	and session reuse count.
/* .IP recipient
/*	Recipient information. See recipient_list(3).
/* .IP relay
/*	Name of the host we're talking to.
/* .IP tstats
/*	TLS per-feature status.
/* .IP dsn
/*	Delivery status. See dsn(3). The action is ignored in case
/*	of a probe message. Otherwise, "delivered" is assumed when
/*	no action is specified.
/* DIAGNOSTICS
/*	A non-zero result means the operation failed.
/*
/*	Fatal: out of memory.
/* BUGS
/*	Should be replaced by routines with an attribute-value based
/*	interface instead of an interface that uses a rigid argument list.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*
/*	Wietse Venema
/*	porcupine.org
/*--*/

/* System library. */

#include <sys_defs.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <vstring.h>

/* Global library. */

#define DSN_INTERN
#include <mail_params.h>
#include <verify.h>
#include <log_adhoc.h>
#include <trace.h>
#include <defer.h>
#include <sent.h>
#include <dsn_util.h>
#include <dsn_mask.h>

/* Application-specific. */

/* sent - log that a message was or could be sent */

int     sent(int flags, const char *id, MSG_STATS *stats,
	             RECIPIENT *recipient, const char *relay,
	             const POL_STATS *tstats, DSN *dsn)
{
    DSN     my_dsn = *dsn;
    DSN    *dsn_res;
    int     status;

    /*
     * Sanity check.
     */
    if (my_dsn.status[0] != '2' || !dsn_valid(my_dsn.status)) {
	msg_warn("sent: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "2.0.0";
    }

    /*
     * DSN filter (Postfix 3.0).
     */
    if (delivery_status_filter != 0
     && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0)
	my_dsn = *dsn_res;

    /*
     * MTA-requested address verification information is stored in the verify
     * service database.
     */
    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
	my_dsn.action = "deliverable";
	status = verify_append(id, stats, recipient, relay, tstats, &my_dsn,
			       DEL_RCPT_STAT_OK);
	return (status);
    }

    /*
     * User-requested address verification information is logged and mailed
     * to the requesting user.
     */
    if (flags & DEL_REQ_FLAG_USR_VRFY) {
	my_dsn.action = "deliverable";
	status = trace_append(flags, id, stats, recipient, relay, tstats,
			      &my_dsn);
	return (status);
    }

    /*
     * Normal mail delivery. May also send a delivery record to the user.
     */
    else {

	/* Readability macros: record all deliveries, or the delayed ones. */
#define REC_ALL_SENT(flags) (flags & DEL_REQ_FLAG_RECORD)
#define REC_DLY_SENT(flags, rcpt) \
	((flags & DEL_REQ_FLAG_REC_DLY_SENT) \
	&& (rcpt->dsn_notify == 0 || (rcpt->dsn_notify & DSN_NOTIFY_DELAY)))

	if (my_dsn.action == 0 || my_dsn.action[0] == 0)
	    my_dsn.action = "delivered";

	if (((REC_ALL_SENT(flags) == 0 && REC_DLY_SENT(flags, recipient) == 0)
	     || trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0)
	    && ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0
		|| trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0)) {
	    log_adhoc(id, stats, recipient, relay, tstats, &my_dsn, "sent");
	    status = 0;
	} else {
	    VSTRING *junk = vstring_alloc(100);

	    vstring_sprintf(junk, "%s: %s service failed",
			    id, var_trace_service);
	    my_dsn.reason = vstring_str(junk);
	    my_dsn.status = "4.3.0";
	    status = defer_append(flags, id, stats, recipient, relay, tstats,
				  &my_dsn);
	    vstring_free(junk);
	}
	return (status);
    }
}
