# Expansion of the local-part of a locally delivered address is done here.
#
# We check (in order):
#
#	$FORCEPUNT -- if defined, route to: user@$FORCEPUNT
#	":include:" or "|" or "/" prefix
#	aliases file entry (mapping to a list of addresses)
#	mboxmap file entry (mapping to a host!homedir!user value)
#	$MAILVAR/lists directory entry (mapping to a list of addresses)
#	-request or -owner suffix (mapping to owner of mailing list file)
#	$PUNTHOST (mapping to user@$PUNTHOST)
#	$forward  file entry (mapping to a list of addresses)
#
# If we get all the way through, we give up.  Note that if PUNTHOST is
# defined, then the mboxmap mechanism should be used to ensure local delivery
# for local users.

# Actually the order is defined by evaluating variable
#	localprotocols
# which has following default value -- unless  router.cf  (pre)defines
# some other value:

[ $localprotocols ] || \
	localprotocols=(include aliases mboxmap lists uid_trap punthost local)

#
# These call functions with names:
#	expand_include, expand_aliases, expand_mboxmap, expand_lists
# and so forth..
#



provide aliases

#
# Define the 'Sendmail-like' aliases database mechanism
# 

if [ -f $MAILVAR/db/aliases.zmsh ]; then

    # If that file exists, it contains scripts defining our databases
    # at load-in time (relation entries), and also runtime callable
    # entrypoint  aliases(uname) { ... }  which "return 1" in case no
    # lookup succeeds.  It yields the first lookup result that succeeded.
    # Also, successfull lookup must yield file privileges of the database
    # file where the lookup was done.  The return is done to a "priv"
    # variable which has local scope in the caller's symbol stack.

    . $MAILVAR/db/aliases.zmsh

else
    if [ -f $MAILVAR/db/aliases ]; then
	# Need to be updated at boot ?
	if [ ! -f $MAILVAR/db/aliases$DBEXTtest -o \
	     $MAILVAR/db/aliases -nt $MAILVAR/db/aliases$DBEXTtest ]; then
		# Yes, so update!
		$MAILBIN/newdb -l -a $MAILVAR/db/aliases
	fi
# ---- raw relation entry
	relation -lmt $DBTYPE -f $MAILVAR/db/aliases$DBEXT aliasesdb
# ---- interface function
	aliases(key) {
		local a
		if a="$(aliasesdb "$key")"; then
		  priv="$(filepriv -M 644 $MAILVAR/db/aliases$DBEXTtest \
					$(db owner aliasesdb))" &&
		  returns "$a"
		fi
		return 1
	}
    else
	aliases () { return 1 }
    fi
fi

# If the 'm' option was NOT specified on the aliases relation,
# presumably whatever creates new aliases will poke us (using SIGURG).

trap "db flush aliases ; log flushed aliases" 16

#
# If FULLNAMEMAP definition can be found..
#
if [ -f $MAILVAR/db/fullnamemap.zmsh ]; then
	. $MAILVAR/db/fullnamemap.zmsh
fi

#
# Safeguard if fullnamemapdb() is not yet created (as part of aliases()?)
#
case "$(type fullnamemapdb)" in
*"not found")

    # optional: Fullname database: <Full_Name>    <login>

    if [ -f $MAILVAR/db/fullnames$DBEXTtest  ]; then
	relation -lmt $DBTYPE -f $MAILVAR/db/fullnames$DBEXT fullnamemapdb
	fullnamemap (key) {
	    local a;
	    if a="$(fullnamemapdb "$key")" ; then
		# "priv" analysis ???
		returns "$a"
	    fi
	    return 1
	}
    else
	fullnamemap () { return 1 }
    fi

    ;;

*)  # Can be found!
    case "$(type fullnamemap)" in
    *"not found")
	# The DB is there, but not this function, probably as a part of
	# aliases() database call entry...
	fullnamemap() { return 1 }
	;;
    esac
    ;;
esac

#
# Hooks to define USERDB mapping
#

if [ -f $MAILVAR/db/userdb.zmsh ]; then
   . $MAILVAR/db/userdb.zmsh
else
    if [ -f $MAILVAR/db/userdb$DBEXTtest ]; then
	relation -lmt $DBTYPE -f $MAILVAR/db/userdb$DBEXT userdb
    else
	userdb () { return 1 }
    fi
fi

#
# Safeguard if userdb() is not yet created (as part of aliases()?)
#
case "$(type userdb)" in
*"not found")
	userdb () { return 1 }
    ;;
esac


# If an mboxmap file exists, only users specified in the mboxmap are
# recognized, and we want to deliver to them on the host, home directory
# filesystem, and filename, specified as the value in the mboxmap DB.
# The key->value mapping looks like this:
#	<name> <$hostname:$MAILBOX:$name>
# The mboxmap file is in MAILSHARE because it is identical for all machines
# in the cluster.

if [ -f $MAILSHARE/db/mboxmap ]; then
	relation -lmt ordered -f $MAILSHARE/db/mboxmap mboxmap
	POBOX=PObox	# becomes $HOME/../PObox/$USER
else
	mboxmap () { return 1 }
	POBOX=''
fi

#
# Map containing accounts that have expired, we return
# a special error report on them.
#
if [ -f $MAILVAR/db/expiredaccts$DBEXTtest ]; then
       relation -lmt ndbm -f $MAILVAR/db/expiredaccts$DBEXT expired
else
       expired () { return 1 }
fi

#
# ################################################################
#
# This is the main engine for expansion.  This will call
# $(expand_$method $user $attr) for each method it's given, with a
# guard against recursion, until it gets an answer.
#
# Note the difference between
#	$(localexpand 'method1' "$user" "$host" "$attr" "$key")
#	$(localexpand 'method2' "$user" "$host" "$attr" "$key")
# and
#	$(localexpand 'method1 method2' "$user" "$host" "$attr" "$key")
#
# In the first case, $(method2 foo) can get called recursively while
# expanding $(method1 foo).  In the second case, $(method2 foo) will
# not get called recursively while expanding $(method1 foo).
# XXX Perhaps this distinction is a little too confusing; maybe
# XXX there's a better way of doing it.

localexpand (method, user, host, attr, key) {
	local expansion success result

	# This is the key used as a guard.
	expansion="$@ * $user"

	# A second instance of the same name disappears.
	# XXX This is not quite the right place to do this.
	# XXX And expansions should be split into two tables.
	expansions "finished $expansion" && return ()

	# A recursive instance of the same name gets rejected.
	expansions "pending $expansion" && return 99

	db add expansions "pending $expansion" 1
	success=''
	for method in "$@"; do
		if result=$(expand_$method "$user" $attr "$origaddr"); then
			success=yes
			break
		fi
	done
	db remove expansions "pending $expansion"

	[ $success ] || return 99

	db add expansions "finished $expansion" 1
	returns $result
}


#
# ################################################################
#
#   Usage: routeuser (channel host user attributes)
#
# This is where local alias expansion is controlled.

routeuser (quad, plustail, domain) {
	local chan user host lcuser mxh al a l
	local key attr pobox didexpand nattr homedir
	local plustail plustail2 forward


	plustail=""
	plustail2=""
	attr="$(attributes $quad)"
	chan="$(channel $quad)"
	
	case "$(get $attr type)" in
	sender)	returns (($quad)) ;;
	esac

	# get rid of the attribute tag for alias loop prevention
	a=$quad
	##mxh=$(setf $(last $a) ())

	# For the expansions control tag we can use only
	# the 'channel',  and the 'username' strings, we CAN'T
	# use 'attributes' element, because .forward recursion
	# will have subsequently DIFFERENT values for it.
	# Use of 'host' field means we can't use 

	user="$(user $quad)"
	key="$chan$(host $quad)$user"
	didexpand=$(expansions "$key")

	[ -n "$didexpand" ] &&
	case "$didexpand" in
	user)	# Looping thru  .forward  will cause us to call back
		# into here :-(
		;;
	*)	
		# Sigh, the easiest way to handle duplicate invokations
		# in here is to yield "bitbucket".  In theory we could
		# yield (), but the router dislikes it.  (Must teach
		# it for good manners..)
		returns (((bitbucket "$(host $quad)1" "$(user $quad)" $(attributes $quad))))
		;;
	esac

	al=()

	case "$(channel $quad)" in
	#smtp)	mxh=$(mxhosts $(host $quad).)
	#	user="$(user $quad)"
	#	attr="$(attributes $quad)"
	#	case $#mxh in
	#	[1-9]*)	# make the XOR list
	#		for host in $(elements $mxh)
	#		do
	#			case $#al in
	#			0)	al=$(rrouter "$address" "$address" $attr "" "") ;;
	#			*)	set $(cdr $(last $al)) \
	#				    (((smtp "$host" "$user" $attr))) ;;
	#			esac
	#		done
	#		returns $al
	#		#a=((x) (smtp '$x' "$(user $quad)" $attr))
	#		#mapcar $a $mxhosts
	#		;;
	#	esac
	#	;;
	local)	;;	# this is the normal case, below
	*)	returns (($quad))
		;;
	esac

	user="$(condquote -- "$(user $quad)")"
	host="$(condquote -- "$(host $quad)")"

# echo "routeuser: host=$host, user=$user" >> /dev/tty

	[ -n "$FORCEPUNT" ] &&
		returns $(rrouter "$user@$FORCEPUNT" "$host" $attr "" "")

	ssift "$user" in
	\\(.*)
#!!!		user="\1" # Back-quoted username -- most likely
		didexpand=local
		;;

	:include:(.+)
		a=$(expand_include "\1" "$host" "$attr" "$origaddr" "$key") &&
			returns $a
		;;

#	# This following requires ZM 2.99.46+ shell language!
#	# The single/double quotes are paired, and ignored while
#	# scanning the selector pattern.  If a quote is needed,
#	# try following mapping:
#	# left is input, right is result:  '"pat'"'" -> "pat'
#	# That is, to produce a double quote, code: '"',
#	# and for single quote, code: "'"
#	# Here we have SPACE and TAB in this pattern!
#	'[^"]*[ 	][^"]*'
#		# If the $user had no quotes on it, we add them here!
#		if [ "$(dequote -- "$user")" = "$user" ]; then
#			user="\"$user\""
#		fi
#		;;
	[|/].*	returns (($quad))
		;;
	tfiss

	for func in ($elements $localprotocols); do
	    if [ -z "$didexpand" ]; then
		a=$(localexpand "$func" "$user" "$host" "$attr" "$key") &&
			returns $a
	    fi
	done


# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#
#           Below here are many partially unproceduralized tests
#	    (work continues!), or just plain obsolete open-coded
#           version of the loop above..
#

	# ``aliases''

	if [ $do_aliases ]; then
		a=$(expand_aliases "$user" "$host" "$attr" "$key") &&
			returns $a
	fi

	# ``expired''

	# Listed in 'expired accounts' database ?
	[ -z "$PUNTHOST" ] &&
		a=$(expand_expired "$user" "$host" "$attr" "$key") &&
			returns $a

	# ``pobox_host''

# POBOX INTEGRATION MOVE UNFINISHED
#	[ -z "$didexpand" ] &&
#	a=$(expand_pobox_host "$user" "$host" "$attr" "$key") && returns $a


	# ``lists''

	[ -z "$didexpand" ] &&
	a=$(expand_lists "$user" "$host" "$attr" "$key") && returns $a


#	only allow .forward file reading if privs allow it

	homedir="$(homedirectory "$user")" ||
	ssift "$user" in # No 'homedir' for this user ? Has a '+' in it ?
	(.+)\+.+
		# Try expanding 'user+', and then plain 'user'
		didexpand=$(expansions "$(channel $quad)\1+")
		if [ -n "$didexpand" ] ; then
			returns (((bitbucket "$(host $quad)2" "$(user $quad)" $(attributes $quad))))
			# Return empty -- plusfull was expanded!
		fi
		a="$(aliases "\1+")" &&
		  db add expansions "$key" alias &&
		  priv=$(filepriv -M 644 $MAILVAR/db/aliases \
					  $(db owner aliases)) &&
		  nattr=$(newattribute $attr privilege $priv) &&
		  returns $(echo -- "$a" | listexpand -e root \
						  -c 'alias expansion' \
						  -N -   -- \
						  "$nattr" "$a" "$host")
		# For a "user+" there is no homedirectory, try plusless below
		;;
	(.+)\+(.*)
		# It was 'user+', try now plain 'user' for
		# aliases, and .forward
		didexpand=$(expansions "$(channel $quad)\1") &&
		if [ -n "$didexpand" ] ; then
			returns (((bitbucket "$(host $quad)3" "$(user $quad)" $(attributes $quad))))
			# Return empty -- plusfull was expanded!
		fi
		a="$(aliases "\1")" &&
		  db add expansions "$key" alias &&
		  priv=$(filepriv -M 644 $MAILVAR/db/aliases \
					  $(db owner aliases)) &&
		  nattr=$(newattribute $attr privilege $priv) &&
		  returns $(echo -- "$a" | listexpand -e root \
						  -c 'alias expansion' \
						  -N -   --  \
						  "$nattr" "$a" "$host")
		plustail2="+\2"
		pobox="$(mboxmap "\1")" &&
		  tsift "$pobox" in
		  ([^:]+):([^:]+):(.+)
			if [ "\1" != $hostname ]; then
				db add expansions "$key" mboxmap
				returns (((smtp "\1" "$user@\1" $attr)))
			fi
			plustail="$plustail2"
			user="\3"	# this is also good for .forward stuff
			pobox="\2/$POBOX/\3"
			break
			;;
		  .+	returns (((error database "$user" $attr)))
			;;
		  tfist

		homedir="$(homedirectory "\1")"
		;;
	tfiss

	forward="$homedir/.forward"

	case "$(get $attr type)" in
	expandsender)	[ -f "$forward" ] && returns (($quad))
			;;
	esac

	priv=$(get $attr privilege)

	[ -z "$PUNTHOST" ] && [ -n "$homedir" ] && [ -f "$forward" ] &&
	[ -z "$didexpand" ] &&
	  db add expansions "$key" user &&
	    priv=$(getpriv "644" $priv "$forward" .forward) &&
	      returns $(runas $priv cat "$forward" | \
			listaddresses -e '"\\'"$user"'"' -c "$forward .forward expansion" | \
			 maprrouter $(newattribute $attr privilege $priv) \
					"$forward" "$host")

	# local user with no alias and no .forward file

# --- POBOX PROCESSING MOVE UNFINISHED
#	[ -z "$didexpand" ] &&
#	if [ -n "$pobox" ]; then
#		db add expansions "$key" pobox
#		priv=$(login2uid "$user")
#		returns (((local "pob:$user" "$pobox$plustail$domain" $(newattribute $attr privilege $priv))))
#	fi


	# oh well... give up, and sent to the PUNTHOST
	if [ -n "$PUNTHOST" ]; then
		returns $(rrouter "$user$plustail"@$PUNTHOST "$host" $attr "" "$domain")
	elif [ -n "$didexpand" ]; then
		case "$didexpand" in
		user|alias)
			db add expansions "$key" other
			;;
		local)
			# This is nasty special case -- a failing .forward
			# will generate "\user" which in turn will generate
			# didexpand="local" and $user without the leading \
			# Treat it as local delivery.
			returns (((local "$(host $quad)" "$user" $(attributes $quad))))
			;;
		*)	# the address just disappears... we've got it already
			#log expansion "$(expansions "$key"):"
			#returns ()
			returns (((bitbucket "$(host $quad)5" "$(user $quad)" $(attributes $quad))))
			;;
		esac
	fi

	case "$user" in
	uid#*)	returns $(rrouter postmaster "$host" $attr "" "$domain")
		;;
	esac


	case x$POBOX in
	x)	db add expansions "$key" local
		if [ -z "$localdoesdomain" ]; then
			domain=""
		fi
		quad=($chan $host "$user$plustail$domain" $attr)
		returns (($quad))
		;;
	esac

	returns (((error err.nosuchuser "$user$plustail$domain" $attr)))
}


#
# ################################################################
#


expand_pobox_host(user,host,attr,key) {
# XXX: UNFINISHED!
	pobox="$(mboxmap "$user")" || return 99
	tsift "$pobox" in
	([^:]+):([^:]+):(.+)
			if [ "\1" != $hostname ]; then
				db add expansions "$key" mboxmap
				returns (((smtp "\1" "$user@\1" $attr)))
			fi
			user="\3"	# this is also good for .forward stuff
			pobox="\2/$POBOX/\3"
			break
			;;
	.+		returns (((error database "$user" $attr)))
			;;
	tfist
	return 99
}

expand_pobox_local(user,host,attr,key) {
# XXX: UNFINISHED!
	local pobox
	pobox="$(mboxmap "$user")" || return 99
	tsift "$pobox" in
	([^:]+):([^:]+):(.+)
			if [ "\1" != $hostname ]; then
				db add expansions "$key" mboxmap
				returns (((smtp "\1" "$user@\1" $attr)))
			fi
			user="\3"	# this is also good for .forward stuff
			pobox="\2/$POBOX/\3"
			break
			;;
	.+		returns (((error database "$user" $attr)))
			;;
	tfist
	return 99
}

expand_lists(user,host,attr,key) {
	local a aa priv nattr lcuser l

	priv=$(get $attr privilege)

	# check in the mailing list directory
	lcuser="$(recase -l -- "$user")"

	l="$preowner$lcuser$postowner$domain"
#	a="$MAILVAR/listrun/$lcuser"
#	[ -f "$a" ] &&
#	    db add expansions "$key" list &&
#	      priv=$(getpriv "644" $priv "$a" maillist) &&
#	        returns $(runas $priv cat "$a" | \
#			 listaddresses	-E "$l" -e "$l"	\
#					-c "$a file expansion" |
#		         maprrouter $(newattribute $attr privilege $priv \
#					sender "$l") "$a" "$host")

	a="$MAILVAR/lists/$lcuser"
	nattr=()
	#l="$preowner$lcuser$postowner$domain"
	[ -f "$a" ] &&
	  db add expansions "$key" list &&
	    priv=$(getpriv "664" $priv "$a" maillist) &&
	      nattr=$(newattribute $attr privilege $priv sender "$l") &&
		returns $(runas $priv cat "$a" | \
			 listexpand -E "$l" -e "$l" -p $priv		\
				    -c "$a file expansion"		\
				    "$nattr"  "$a" "$host" )

	a="$MAILVAR/modlists/$lcuser"
# /bin/ls -lL "$a"
	# WARNING: THIS SHOULD USE  $(listexpand ...) FACILITY !
	if [ -f "$a" ] &&
	   db add expansions "$key" list ; then
		priv=$(getpriv "664" $priv "$a" maillist)
		case "$lcuser" in
		*-mod)
			l="$preowner$(basename "$lcuser" -mod)$postowner"
			# echo -- "$l exists!" >> /dev/tty
			returns $(runas $priv cat "$a" | \
				 listaddresses	-E "$l$domain"		\
						-e "$l"			\
						-c "$a file expansion" |
				maprrouter $(newattribute $attr		\
					      privilege $priv		\
					      sender "$l") "$a" "$host")
			;;
		esac
	fi

	# turn *-owner and *-request into the owner of a mailing list file
	# turn *-group into members listed in /etc/groups

	ssift "$lcuser" in
	(.+)-owner
		a="$MAILVAR/lists/\1"
		if [ -f "$a" ] ; then
		  aa=$(rrouter "$(uid2login $(filepriv -M 664 "$a"))" \
					 "$host" $attr "" "") &&
			returns $aa
		  return $aa # error code return
		fi
		break
		;;
	owner-(.+)
		a="$MAILVAR/lists/\1"
		if [ -f "$a" ] ; then
		  aa=$(rrouter "$(uid2login $(filepriv -M 664 "$a"))" \
					 "$host" $attr "" "") &&
			returns $aa
		  return $aa # error code return
		fi
		break
		;;
	(.+)-request
		a="$MAILVAR/lists/\1"
		if [ -f "$a" ] ; then
		  aa=$(rrouter "$(uid2login $(filepriv -M 664 "$a"))" \
					 "$host" $attr "" "") &&
			returns $aa
		  return $aa # error code return
		fi
		break
		;;
	(.*)-group
		if a="$(groupmembers "\1")" &&
		   db add expansions "$key" list ; then
		  aa=$(echo $a |
			   listaddresses -e postmaster -c "$lcuser expansion" |
			   maprrouter $(newattribute $attr sender postmaster) \
					"$lcuser" "$host") &&
			returns $aa
		  return $aa # error code return
		fi
		;;
	tfiss
}


#
# Listed in 'expired accounts' database ?
#
expand_expired(user,host,attr,key) {
	local a
	a=$(expired "$user") || return 99
	db add expansions "$key" expired || return 99
	returns (((error acctexpired "$user" $attr)))
}

#
# expand_include:
#
expand_include (file,host,attr,key) {
	local priv nattr aa

	db add expansions "$key" file
	priv=$(get $attr privilege)

	[ -f "$file" ] &&
	 priv=$(getpriv "644" $priv "$file" include) &&
	  nattr=$(newattribute $attr privilege $priv) &&
	   defer=''

	aa=$(runas $priv cat "$file" | \
	     listexpand -E "postmaster" -e root -p $priv	\
			-c ":include:$file file expansion"	\
			"$nattr" "$file" "$host" ) &&
		returns $aa
	return $aa # error code return
}

#
#	alias expansion
#
expand_aliases(user,host,attr,key) {
	local a priv nattr aa
	# aliases() updates 'priv' too..
	a="$(aliases "$user")"		|| return 99
	db add expansions "$key" alias	|| return 99
	priv=$(filepriv -M 644 $MAILVAR/db/aliases \
				$(db owner aliases)) || return 99
	nattr=$(newattribute $attr privilege $priv)  || return 99
	aa=$(echo "$a" | listexpand -e root \
				    -c 'alias expansion' \
				    -N - \
				    "$nattr" "$a" "$host") &&
		returns $aa
	return $aa # error code return
}

#
# LISTSERV expansion
#
expand_listserv(user,host,attr,key) {

# listserv automatic list alias mapping support, the listserv home
# probably should go to zmailer.conf or elsewhere at some time
# but let's keep it simple during testing <hks@nic.funet.fi> 1998
# We check first zmailer lists that can override listserv in an
# emergency, for example.
#
	local LISTSERV a listbase lcuser

	# check in the mailing list directory
	lcuser="$(recase -l -- "$user")"

	LISTSERV="/v/net/listserv.funet.fi"
	a="$LISTSERV/home/$lcuser.list"
  
	if [ -f "$a" ] ; then
		return (((local pipe.$lcuser "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool $lcuser" $(newattribute $attr privilege "0" ))))
	fi

	ssift "$lcuser" in

	owner-(.+)
		listbase="\1"
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ]; then
		   return (((local pipe.owner-$listbase "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool owner-$listbase" $(newattribute $attr privilege "0" ))))
		fi
		;;

	(.+)-owner
		listbase="\1"
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ]; then
		   return (((local pipe.owner-$listbase "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool owner-$listbase" $(newattribute $attr privilege "0" ))))
		fi
		;;

	(.+)-search-request
		listbase="\1"
#listsserv search facility support <hks>
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ]; then
		   return (((local pipe.$listbase-search-request "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool $listbase-search-request" $(newattribute $attr privilege "0" ))))
		fi
		;;

	(.+)-request
		listbase="\1"
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ] ; then
		   return (((local pipe.$listbase-request "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool $listbase-request" $(newattribute $attr privilege "0" ))))
		fi
		;;

	(.+)-server
		listbase="\1"
#listsserv search facility support <hks>
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ]; then
		   return (((local pipe.$listbase-server "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool $listbase-server" $(newattribute $attr privilege "0" ))))
		fi
		;;
	(.+)
		listbase="\1"
		a="$LISTSERV/home/$listbase.list"
		if [ -f "$a" ] ; then
		   return (((local pipe.owner-$listbase "|$LISTSERV/bin/lsv_amin $LISTSERV/lsvspool owner-$listbase" $(newattribute $attr privilege "0" ))))
		fi
		break
		;;
	tfiss
}


# Usage: getpriv <maxperm> <present privilege> <filename> <filename type>
#
# Determine the privileges associated with addresses coming from the filename.
# The type value is one of .forward, maillist, or include.  Setting
# private='' ensures that noone can access (modulo a small window) information
# through the mailer (e.g., by sending mail to addresses taken from a
# protected file and waiting excitedly for the informative bounce messages)
# that they couldn't access otherwise.  If private='.forward maillist' then
# people stop complainig about the former behaviour...

getpriv (maxperm, priv, file, type) {
#echo "getpriv($maxperm, $priv, $file, $type)" > /dev/tty
	for ptype in $(elements $private)
	do
		if [ $type = $ptype ]; then
			filepriv -M $maxperm "$file"
			return $?
		fi
	done
	runas $priv filepriv -M $maxperm "$file"
}

# Usage: newattribute <oldattribute> <key1> <value1> [ <key2> <value2> ] ...
#
# Returns a new attribute list symbol with the <keyN> <valueN>
# attributes added to the contents of the <oldattribute> list.

newattribute (oldattribute) {
	local a null value
#echo "newattribute(old=$oldattribute,args=$*)" > /dev/tty
	a=$(gensym)
	eval $a=\$$oldattribute
	while [ "$#" != 0 ];
	do
		lreplace $a "$1" "$2"
		# value=$(get $a "$1")
		# if [ x"$value" != x"$2" ]; then
		# 	null=$(setf $(get $a "$1") "$2")
		# fi
		shift ; shift
	done
	echo $a
}

# Usage: maprrouter <newattributes> <what-is-being-expanded> <what-it-was>
#
# This function applies the rrouter function to each address read from
# stdin, passing the <newattributes> parameter.  In case of error, the
# localpart parameter is used as appropriate descriptive text.  The
# return value is the concatenation of the return values of the rrouter
# invocations.

maprrouter (attribute, localpart, origaddr) {
	local shh al

	al=()
	while read address
	do
		case "$address" in
		'')	shh=(((error expansion "$localpart")))
			lappend al $shh
			continue
			;;
		esac

		defer=''

		shh=$(rrouter "$address" "$origaddr" $attribute "" "")
		[ -n "$defer" ] &&
		    shh=(((hold "$defer" "$address" $attribute)))
		defer=''
		lappend al $shh
	done
	returns $al
}
