/*++
/* NAME
/*	unknown 3
/* SUMMARY
/*	delivery of unknown recipients
/* SYNOPSIS
/*	#include "local.h"
/*
/*	int	deliver_unknown(state, usr_attr)
/*	LOCAL_STATE state;
/*	USER_ATTR usr_attr;
/* DESCRIPTION
/*	deliver_unknown() delivers a message for unknown recipients.
/* .IP \(bu
/*	If an alternative message transport is specified via the
/*	fallback_transport parameter, delivery is delegated to the
/*	named transport.
/* .IP \(bu
/*	If an alternative address is specified via the luser_relay
/*	configuration parameter, mail is forwarded to that address.
/* .IP \(bu
/*	Otherwise the recipient is bounced.
/* .PP
/*	The luser_relay parameter is subjected to $name expansion of
/*	the standard message attributes: $user, $home, $shell, $domain,
/*	$recipient, $mailbox, $extension, $recipient_delimiter, not
/*	all of which actually make sense.
/*
/*	Arguments:
/* .IP state
/*	Message delivery attributes (sender, recipient etc.).
/*	Attributes describing alias, include or forward expansion.
/*	A table with the results from expanding aliases or lists.
/*	A table with delivered-to: addresses taken from the message.
/* .IP usr_attr
/*	Attributes describing user rights and environment.
/* DIAGNOSTICS
/*	The result status is non-zero when delivery should be tried again.
/* 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
/*--*/

/* System library. */

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

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

/* Utility library. */

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

/* Global library. */

#include <been_here.h>
#include <mail_params.h>
#include <mail_proto.h>
#include <bounce.h>
#include <mail_addr.h>
#include <sent.h>
#include <deliver_pass.h>
#include <defer.h>
#include <canon_addr.h>

/* Application-specific. */

#include "local.h"

#define STREQ(x,y) (strcasecmp((x),(y)) == 0)

/* deliver_unknown - delivery for unknown recipients */

int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
{
    const char *myname = "deliver_unknown";
    int     status;
    VSTRING *expand_luser;
    VSTRING *canon_luser;
    static MAPS *transp_maps;
    const char *map_transport;

    /*
     * Make verbose logging easier to understand.
     */
    state.level++;
    if (msg_verbose)
	MSG_LOG_STATE(myname, state);

    /*
     * DUPLICATE/LOOP ELIMINATION
     * 
     * Don't deliver the same user twice.
     */
    if (been_here(state.dup_filter, "%s %s", myname, state.msg_attr.local))
	return (0);

    /*
     * The fall-back transport specifies a delivery mechanism that handles
     * users not found in the aliases or UNIX passwd databases.
     */
    if (*var_fbck_transp_maps && transp_maps == 0)
	transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
				  DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
				  | DICT_FLAG_UTF8_REQUEST);
    /* The -1 is a hint for the down-stream deliver_completed() function. */
    if (transp_maps
	&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
				      DICT_FLAG_NONE)) != 0) {
	state.msg_attr.rcpt.offset = -1L;
	return (deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
			     state.request, &state.msg_attr.rcpt));
    } else if (transp_maps && transp_maps->error != 0) {
	/* Details in the logfile. */
	dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
	return (defer_append(BOUNCE_FLAGS(state.request),
			     BOUNCE_ATTR(state.msg_attr)));
    }
    if (*var_fallback_transport) {
	state.msg_attr.rcpt.offset = -1L;
	return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport,
			     state.request, &state.msg_attr.rcpt));
    }

    /*
     * Subject the luser_relay address to $name expansion, disable
     * propagation of unmatched address extension, and re-inject the address
     * into the delivery machinery. Do not give special treatment to "|stuff"
     * or /stuff.
     */
    if (*var_luser_relay) {
	state.msg_attr.unmatched = 0;
	expand_luser = vstring_alloc(100);
	canon_luser = vstring_alloc(100);
	local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (void *) 0);
	/* In case luser_relay specifies a domain-less address. */
	canon_addr_external(canon_luser, vstring_str(expand_luser));
	/* Assumes that the address resolver won't change the address. */
	if (STREQ(vstring_str(canon_luser), state.msg_attr.rcpt.address)) {
	    dsb_simple(state.msg_attr.why, "5.1.1",
		       "unknown user: \"%s\"", state.msg_attr.user);
	    status = bounce_append(BOUNCE_FLAGS(state.request),
				   BOUNCE_ATTR(state.msg_attr));
	} else {
	    status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
	}
	vstring_free(canon_luser);
	vstring_free(expand_luser);
	return (status);
    }

    /*
     * If no alias was found for a required reserved name, toss the message
     * into the bit bucket, and issue a warning instead.
     */
    if (STREQ(state.msg_attr.user, MAIL_ADDR_MAIL_DAEMON)
	|| STREQ(state.msg_attr.user, MAIL_ADDR_POSTMASTER)) {
	msg_warn("required alias not found: %s", state.msg_attr.user);
	dsb_simple(state.msg_attr.why, "2.0.0", "discarded");
	return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
    }

    /*
     * Bounce the message when no luser relay is specified.
     */
    dsb_simple(state.msg_attr.why, "5.1.1",
	       "unknown user: \"%s\"", state.msg_attr.user);
    return (bounce_append(BOUNCE_FLAGS(state.request),
			  BOUNCE_ATTR(state.msg_attr)));
}
