# set up output to log file (stdout) independent of current output redirection
log () {
	case "$LOGLEVEL" in
#| The LOGLEVEL variable is typically set in the $(ZCONFIG) file, usually
#| /etc/zmailer.conf.  If set, it will restrict log output to messages that
#| begin with tags in the string value of the variable.
	''|*"$1"*)
		echo "$(get envelopeinfo message-id):" "$@" 1>&3 ;;
	*)	# We want to log anything that doesn't have a tag!
		case "$1" in
		*[^:])	echo "$(get envelopeinfo message-id):" "$@" 1>&3 ;;
		esac ;;
	esac
}
exec 3>&1

#| The log function defined here will be used whenever the configuration
#| code wants to put something in the log.


# what to do to roll the log

dribble (file) {
	exec 3>&- 2>&- 1>&- 1>>$file 2>&1 3>&1
}

#| The dribble function ensures that all output filedescriptors append to
#| the (log) file.

# We roll the log by sending a SIGHUP to the router; to avoid problems caused
# by the I/O state while processing a message, the SIGHUP handler is always
# invoked synchronously when the router is NOT processing a message.  HUP is
# the only signal handled in this way.

trap "dribble $LOGDIR/router" 1

case "$-" in
*i*)	case "x$SMTPSERVER" in
	xy*)	# invoked from SMTP server, redirect stderr
		exec 2>&1
		;;
	esac
	exec 3>&1	# always redirect log
	;;
*)	# daemon
	dribble $LOGDIR/router
	;;
esac

#| Logging is set up differently depending on the context.  If running as
#| a daemon, all output should go to the log file.  Otherwise, stdout or
#| stderr.  If invoked from the SMTP server, all output goes to stdout.

# set up the alias expansions table
# NOTE: the key hashing mechanism used presently (with symbol()), won't scale
# well if you alias expand arbitrary off-site addresses; they will all be
# stored in memory for the life of the process.  This will be fixed, later.
#
relation -lt incore expansions

# We do local delivery, more or less, of mail arriving for these hostnames
if [ -f $MAILVAR/db/localnames ]; then
	relation -lt unordered -f $MAILVAR/db/localnames -d pathalias -b thishost
else
	# The default set of hostnames is:
	#	$rawhostname - system hostname, from /bin/hostname (or whatever)
	#	$hostname - the canonicalized version of $rawhostname
	#	$rawhostname.X, where X is a member of the set of organizational
	#	domains in $orgdomains.
	# We use a function and an incore relation, because this
	# file is loaded before the siteinfo stuff.
	relation -lt incore -b thishostdb
	thishost(name) {
		local oldifs
		# If the relation hasn't been initialized, do so.
		if [ $(thishostdb "$hostname") ]; then :; else
			db add thishostdb "$hostname" ""
			if [ "$rawhostname" ]; then
				db add thishostdb "$rawhostname" ""
				oldifs="$IFS"
				IFS="|"
				for org in $orgdomains; do
					db add thishostdb "$rawhostname.$org" ""
				done
				IFS="$oldifs"
			fi
		fi
		[ $(thishostdb "$name") ]
	}
fi

#| If the localnames file does not exist, assume we must do local
#| delivery for our own hostname.  If the localnames file does exist,
#| it must contain a simple sorted list of (domain)names we should
#| deliver locally.  One per line.  Case is ignored.
#| In some situations (a cluster of mail servers) it is prudent to be able
#| to have exclusions in the localnames file.  If we do this through
#| another file containing the names of the other mail servers, we can keep
#| the localnames file the same on all intimate mail servers.  It
#| describes the service domains.

if [ -f $MAILVAR/db/otherservers ]; then
	relation -lt unordered -f $MAILVAR/db/otherservers -b otherservers
else
	otherservers (name) { return 1 }
fi

deliver (name) {
	if [ $(thishost "$name") ]; then
		[ $(otherservers "$name") ] || { echo "$name" ; return 0 }
	fi
	return 1
}


# optional: Fullname database: <Full_Name>    <login>
if [ -f /etc/fullnames.pag ]; then
	relation -lmt ndbm -f /etc/fullnames fullnamemap
else
	fullnamemap () { return 1 }
fi

#| The code to use this is disabled in the rrouter function.  This
#| illustrates a possible mechanism.

# optional: uucp neighbours from L.sys
#relation -t unordered -f /usr/lib/uucp/L.sys -b ldotsys

#| Same comment.  Typically we do not wish to use all our UUCP
#| connections for mail, so this relation is useless to determine how a
#| message should be sent.  UUCP neighbours are specified in the
#| routes database using lines of the form:

#| neighbour    uucp!neighbour

default_attributes=(privilege ${NOBODY:-29999} type recipient)

#| This is the default attributes parameter to the router function
#| below.  It is typically used when doing interactive queries.  The
#| value of the type property indicates whether or not alias expansion
#| should be done.  If it is 'sender', no alias expansion is done.


router (address, attributes) {

#| This is the address routing entry point.  The function name "router"
#| is known by the router program.

	local res
	# set up default attributes for casual interactive use.  Any
	# privilege set here is only important when router is run as root,
	# otherwise it won't matter.
	: ${attributes:=default_attributes}
	defer=""

#| The defer variable is set to a deferral reason by the router internals
#| in case of database lookup timeouts or other problems.  We null it
#| here, and test it later to check for problems.

	log address: "$address"

#| A gratuitous log entry so we can later on tell the exact addresses
#| that were routed.  This could be removed if you like terse logs.

	#grind ${$attributes} 1>&3

	# If you want to do address expansion loop-prevention per-message,
	# instead of per-address, null the expansions list in the process()
	# function instead of here.
	db flush expansions

	res=$(rrouter "$address" $attributes)

#| The rrouter function does the real work of routing.  It should return
#| a list of addresses in the normal AND-XOR tree form.

	if [ "$defer" ]; then
		log deferred: $defer: "$address"
		return (((hold "$defer" "$address" $attributes)))
	fi

#| If an internal problem was detected during the routing, we ignore the
#| results of routing and redirect this address to the hold transport
#| queue for later resubmission.

	return $res
}

header_defer (address) {
	log header_defer: "$address"
	return "\"$address\""@$hostname
}

#| In case of internal problems during header address rewriting, this
#| function will determine the result of rewriting.  It should just quote
#| the original address and make it relative to the current host.  The
#| alternative is to delay the message until rewriting can be done
#| properly, however this is a very unpopular action.  The router program
#| does not give a choice.  The name "header_defer" is known by the
#| program.
