#!/bin/tcsh -f

# This script is for restricting access to mailing lists.
# Access can be bases on moderation, group membership on which the
# list resides, or membership in the mailing list itself.
#
# Usage:
#
#  Put in aliases:
#
# email-alias: "|/path/this-script-name -l /path/list-filename [-g group] [-m sysprog]  [-M] [-d]"
# where
#	-a /path/list-filename		specifies the filename
#					containing the users allowed
#					to post to the list
#					(assumed 1 name per line)
#	-l /path/list-filename		specifies the filename
#					containing the list membership
#					(assumed 1 name per line)
#	-g group			specifies the local group
#					allowed to use the list
#					(multiple -g options can occur)
#	-m moderator			specifies the local user
#					to which rejected postings are sent
#					(multiple -m options can occur)
#	-M				indicates members of the list
#					can post
#	-R				indicates any Rutgers email address
#					can post
#	-CS				indicates any @cs.rutgers.edu email address
#					can post (or anyone active in the DCS database)
#	-d				debugging log put on /tmp
#	-f				act as filter (output message to stdout)
#	-p				add specified precedence (eg, "-p list") to message
#	-s				add specified Sender (eg, "-s everyone@cs") to message
#	-k				keep (ie, don't delete) tmp file

#    WARNING: Apparently, adding an "owner-<alias>" alias for the list
#    causes this script to believe the sender is the result of that
#    alias, which will break this script.

# "-i" added to invocations of sendmail to prevent message being
# terminated by "." alone on a line (as happened to makmur); daw, 10/28/08

set MODERATORS
set GROUPS
set ARGS = "$*"
set AWK = /usr/local/bin/awk
if (! -e $AWK) then
    set AWK = /bin/awk
endif

while($#argv)
	switch ("$1")
	case -D:
		set VDEBUG
#		shift
#		breaksw
# fall through to debug
	case -d:
		set DEBUG
#		shift
#		breaksw
# fall through to verbose
	case -v:
		set VERBOSE
		set SENDMAILVsw = "-v"
		shift
		breaksw
	case -l:
		set LIST = $2
		shift; shift
		breaksw
	case -a:
#		set ALIST = $2
# allow more than one ALIST; 3/26/14
		if (! $?ALISTS) set ALISTS
		set ALISTS = ( $ALISTS $2 )
		shift; shift
		breaksw
	case -g:
		set GROUPS = ( $GROUPS $2 )
		shift; shift
		breaksw
	case -G:
		set GFILE = $2
		shift; shift
		breaksw
	case -m:
		set MODERATORS = ( $MODERATORS $2 )
		shift; shift
		breaksw
	case -M:
		set MEMBERS
		shift
		breaksw
	case -R:
		set RUTGERS
		shift
		breaksw
	case -CS:
		set CS
		shift
		breaksw
	case -f:
		set FILTER
		shift
		breaksw
	case -p:
		set PSCRIPT = `/bin/dirname $0`
		set PSCRIPT = "$AWK -f $PSCRIPT/add-precedence.awk PRECEDENCE=$2"
		shift; shift
		breaksw
	case -s:
		set SSCRIPT = `/bin/dirname $0`
		set SSCRIPT = "$AWK -f $SSCRIPT/add-sender.awk SENDER=$2"
		shift; shift
		breaksw
	case -ns:		# no send -- don't really send message
		set NOSEND
		shift
		breaksw
	case -k:		# keep tmp file
		set KEEP
		shift
		breaksw
	default:
		set DQ = '"'
		echo ${0}: "What do I do with $DQ$1$DQ?"
		exit 1
	endsw
end

if (! $?LIST) exit 1

if (! $?PSCRIPT) set PSCRIPT = "/bin/cat"
if (! $?SSCRIPT) set SSCRIPT = "/bin/cat"
if (! $?SENDMAILVsw) set SENDMAILVsw

set TMP = /tmp/moderated-mailing.$$
set BOUNCE = /tmp/moderated-mailing.bounce.$$
if ($?DEBUG) set DFILE = /tmp/`/bin/basename $LIST`.debug.$$

umask 77			# for privacy

cat > $TMP

#set WHO = `head -1 $TMP | awk '{print $2}' | sed 's;@.*\.[Rr][Uu][Tt][Gg][Ee][Rr][Ss]\.[Ee][Dd][Uu];;'`

# WHO is the address the email is from in the "From " line at the beginning of the message,
# with "@<host>.rutgers.edu" stripped off

#set WHO = `head -1 $TMP | awk '{print $2}' | tr "[A-Z]" "[a-z]" | sed 's;@.*\.rutgers.edu;;'`
# someone could send from <foo>@rutgers.edu too; 1/8/06
#set WHO = `head -1 $TMP | awk '{print $2}' | tr "[A-Z]" "[a-z]" | sed 's;@.*rutgers.edu;;'`
# preserve envelope from; 4/24/08
set EWHO = `head -1 $TMP | awk '{print $2}'`
set WHO = `echo $EWHO | tr "[A-Z]" "[a-z]" | sed 's;@.*rutgers.edu;;'`

# If Rutgers address, make sure it's only 8 characters.
# (We sometimes add forwarding for "longlastname" to "longlast"
#  and some people send mail as longlastname@...).
echo $WHO | grep "@" > /dev/null
if ($status) then
    set WHO = `echo $WHO | cut -c1-8`
endif

# RWHO is "from" address iff it contains "rutgers.edu"

set RWHO = `head -1 $TMP | awk '{print $2}' | tr "[A-Z]" "[a-z]" | grep "rutgers\.edu"`

# CSWHO is "from" address iff it contains "cs.rutgers.edu"

set CSWHO = `head -1 $TMP | awk '{print $2}' | tr "[A-Z]" "[a-z]" | grep "cs\.rutgers\.edu"`

# FWHO is full "from" address

set FWHO = `head -1 $TMP | awk '{print $2}' | tr "[A-Z]" "[a-z]"`

# LWHO is that person if they exist in aramis's /etc/passwd

set LWHO = `ypmatch $WHO passwd |& grep ${WHO}: | sed 's;:.*;;'`

if ($?VDEBUG) echo LWHO set

#if ("$LWHO" == "daemon") then
# daemon is not in yp some places! daw; 6/6/06
#if ("$LWHO" == "daemon" || "$WHO" == "daemon") then
# Doug Motto is sending email from nobody@www3.srv.lcsr! 7/26/06
if ("$LWHO" == "daemon" || "$WHO" == "daemon" || \
    "$LWHO" == "nobody" || "$WHO" == "nobody") then

# Special case for daemon (lists within lists):
# DWHO is the address the email is from in the "From: " line in the headers.

#    set DWHO = `/bin/awk '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' -e 's;@.*\.[Rr][Uu][Tt][Gg][Ee][Rr][Ss]\.[Ee][Dd][Uu];;'`
# someone could send from <foo>@rutgers.edu too; 1/8/06
    set DWHO = `$AWK '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' -e 's;@.*[Rr][Uu][Tt][Gg][Ee][Rr][Ss]\.[Ee][Dd][Uu];;'`

# If Rutgers address, make sure it's only 8 characters.
    echo $DWHO | grep "@" > /dev/null
    if ($status) then
	set DWHO = `echo $DWHO | cut -c1-8`
    endif


# update RWHO in case of daemon relay

#    set RWHO = `/bin/awk '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' | tr "[A-Z]" "[a-z]" | grep "rutgers\.edu"`
# avoid RWHO looking like "mlittman@cs.rutgers.edu (michael l. littman)"
    set RWHO = `$AWK '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' -e 's;(.*);;' | tr "[A-Z]" "[a-z]" | grep "rutgers\.edu"`

# update CSWHO as well

    set CSWHO = `$AWK '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' -e 's;(.*);;' | tr "[A-Z]" "[a-z]" | grep "cs\.rutgers\.edu"`

# update FWHO as well

#    set FWHO = `/bin/awk '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' | tr "[A-Z]" "[a-z]"`
# avoid FWHO looking like "mlittman@cs.rutgers.edu (michael l. littman)"
    set FWHO = `$AWK '{if(NF==0)exit;print}' $TMP | grep "^From: " | sed -e 's;^From: ;;' -e 's;.*<;;' -e 's;>.*;;' -e 's;(.*);;' | tr "[A-Z]" "[a-z]"`

# envelope should have legitimate return address; 3/8/09

    set EWHO = $FWHO

# LDWHO is that person if they exist in aramis's /etc/passwd
# ... or in ALIST if it is specified
# if LDWHO is defined, we'll use that instead of WHO to check access.

    set LDWHO = `ypmatch $DWHO passwd |& grep ${DWHO}: | sed 's;:.*;;'`
#    if ("$LDWHO" == "" && $?ALIST) then
    if ("$LDWHO" == "" && $?ALISTS) then
#	egrep "^${WHO}"'$' $ALIST > /dev/null
# should be looking for DWHO here; daw, 2/17/05
#	egrep "^${DWHO}"'$' $ALIST > /dev/null
#	egrep "^${DWHO}"'$' $ALISTS > /dev/null
	egrep "^${DWHO}"'$'"|<$DWHO>" $ALISTS > /dev/null
	if (! $status) then
	    set LDWHO = $DWHO
	endif
    endif
    if ($?VDEBUG) echo LWHO updated
endif


if ($?DFILE) then
	date > $DFILE
	echo $0 $ARGS >> $DFILE
	if ($AWK == "/bin/awk") then
	    echo "" >> $DFILE
	    echo "   " /usr/local/bin/awk does not exist -- using $AWK >> $DFILE
	    echo "" >> $DFILE
	endif
	echo LIST = $LIST >> $DFILE
#	echo WHO = $WHO"($LWHO)" \; GROUPS = $GROUPS \; MODERATORS = $MODERATORS >> $DFILE
#	echo WHO = $WHO"($LWHO)" \; RWHO = $RWHO \; GROUPS = $GROUPS \; MODERATORS = $MODERATORS >> $DFILE
#	echo WHO = $WHO"($LWHO)" \; RWHO = $RWHO \; FWHO = $FWHO >> $DFILE
	echo WHO = $WHO"($LWHO)" \; RWHO = $RWHO \; CSWHO = $CSWHO \; FWHO = $FWHO >> $DFILE
	echo GROUPS = $GROUPS \; MODERATORS = $MODERATORS >> $DFILE
	if ($?DWHO) then
	    echo DWHO = $DWHO"($LDWHO)" >> $DFILE
#	    if ("$LDWHO" != "") then
# Don't recall why we used LDWHO here -- doesn't work on farside; daw, 6/6/06
	    if ("$DWHO" != "") then
#		echo Substituting $LDWHO for $WHO in determining posting privs >> $DFILE
		echo Substituting $DWHO for $WHO in determining posting privs >> $DFILE
#		set LWHO = $LDWHO
# Move above outside conditional, so it will work even if DFILE is not specified
	    endif
	endif
	echo "" >> $DFILE
#	head  $TMP >> $DFILE
	$AWK '{if(NF==0)exit;print}' $TMP >> $DFILE
	echo "" >> $DFILE
	if ($?VDEBUG) echo $DFILE started
endif

# If mail through daemon from other, authorized user, use that name to check access now.

#if ($?LDWHO) then
#    if ("$LDWHO" != "") then
#	set LWHO = $LDWHO
#	set WHO = $LDWHO
#    endif
#endif
if ($?DWHO) then
    if ("$DWHO" != "") then
	set LWHO = $DWHO
	set WHO = $DWHO
    endif
endif

if ($?VDEBUG) echo Checking on $LIST

if (! -r $LIST) then
	if ("$MODERATORS" != "" ) /bin/echo `/bin/whoami` "cannot read $LIST\nmessage left $TMP" | /usr/ucb/mail -s "$0 error" $MODERATORS
	if ($?DFILE) then
		/bin/echo `/usr/ucb/whoami` "cannot read $LIST\nmessage left in $TMP" >> $DFILE
	endif
	exit 1
endif

# Don't allow postmaster to send to restricted lists; 1/18/07

if ($?VDEBUG) echo Checking postmaster

if ("$WHO" == "postmast" ) then
	if ($?DFILE) then
		echo "Posting from Postmaster to restricted lists forbidden" >> $DFILE
	endif
	goto reject
endif

if ($?VDEBUG) echo Checking moderators "($MODERATORS)"

foreach MODERATOR ( $MODERATORS )
	if ("$LWHO" == "$MODERATOR" ) then
		if ($?DFILE) then
			echo "Posting from $LWHO allowed as moderator ($MODERATOR)" >> $DFILE
		endif
		goto caccept
	endif
	if ($?DFILE) then
		/bin/echo "$LWHO is not moderator ($MODERATOR)" >> $DFILE
	endif
end

if ($?VDEBUG) echo Checking groups "($GROUPS)"

foreach GROUP ( $GROUPS )
	if ("$LWHO" != "" ) then
		foreach UGROUP (`/bin/groups $LWHO |& grep -v "No such user"`)
			if ($GROUP == $UGROUP) then
				if ($?DFILE) then
					echo Posting from $LWHO allowed via group $GROUP >> $DFILE
				endif
				goto caccept
			endif
		end
		if ($?DFILE) then
			/bin/echo "$LWHO is not in group ($GROUP)" >> $DFILE
		endif
	endif
# if Rutgers user, check if they're in group file regardless if local account
	if ("$RWHO" != "") then
# "-G" no longer used -- ignoring this wrt CSWHO
		if ($?GFILE) then
#		    set XXX = `grep "^${GROUP}:" $GFILE | grep -w $WHO`
		    grep "^${GROUP}:" $GFILE | grep -w $WHO > /dev/null
		    if (! $status) then
			if ($?DFILE) then
				echo Posting from $WHO allowed via group $GROUP in $GFILE >> $DFILE
			endif
			goto caccept
		    endif
		    if ($?DFILE) then
			/bin/echo "$WHO is not in group ($GROUP) in $GFILE" >> $DFILE
		    endif
		endif
	endif
end

if ($?MEMBERS) then
	if ($?VDEBUG) echo Checking list membership
#	grep "^${WHO}@" $LIST > /dev/null
	egrep "^${WHO}@|<${WHO}@.*>" $LIST > /dev/null
	if (! $status) then
		if ($?DFILE) then
			echo Posting from $WHO allowed via list membership >> $DFILE
		endif
		goto caccept
	else if ($?DFILE) then
		/bin/echo "$WHO is not in list ($LIST)" >> $DFILE
	endif
endif

if ($?ALISTS) then
    if ($?VDEBUG) echo Checking allowed posting list
    foreach ALIST ( $ALISTS )
      if (-r $ALIST) then
#	egrep "^${WHO}"'$' $ALIST > /dev/null
	egrep "^${WHO}"'$'"|<${WHO}>" $ALIST > /dev/null
	if (! $status) then
		if ($?DFILE) then
			echo Posting from $WHO allowed via allowed posting membership "($ALIST)" >> $DFILE
		endif
		goto caccept
	else
#		egrep "^${FWHO}"'$' $ALIST > /dev/null
		egrep "^${FWHO}"'$'"|<${FWHO}>"  $ALIST > /dev/null
		if (! $status) then
		    if ($?DFILE) then
			echo Posting from $FWHO allowed via allowed posting membership "($ALIST)" >> $DFILE
		    endif
		    goto caccept
		else if ($?DFILE) then
		    /bin/echo "$WHO($FWHO) is not in allowed posting ($ALIST)" >> $DFILE
		endif
	endif
      else
	/bin/echo `/usr/ucb/whoami` "cannot read $ALIST" >> $DFILE
      endif
    end
endif

if ($?CS) then
	if ($?VDEBUG) echo Checking for CS address
# If it's a "cs-related" alias, let the post through; daw, 11/20/19
	egrep "^${WHO}"'$' /var/lib/mlists/cs-related-email-addresses > /dev/null
	if (! $status) set CSWHO = "$WHO"
# RWHO might be cs-related too; 1/14/20
	if ("$RWHO" != "") then
	    egrep "^${RWHO}"'$' /var/lib/mlists/cs-related-email-addresses > /dev/null
	    if (! $status) set CSWHO = "$RWHO"
	endif
	if ("$CSWHO" != "") then
		if ($?DFILE) then
			echo Posting from $WHO"($CSWHO)" allowed due to CS-related address >> $DFILE
		endif
		goto caccept
	else if ($?DFILE) then
		/bin/echo "$WHO($RWHO) is not a CS address" >> $DFILE
	endif
endif

if ($?RUTGERS) then
	if ($?VDEBUG) echo Checking for Rutgers address
	if ("$RWHO" != "") then
		if ($?DFILE) then
			echo Posting from $WHO"($RWHO)" allowed due to Rutgers address >> $DFILE
		endif
		goto caccept
	else if ($?DFILE) then
		/bin/echo "$WHO($RWHO) is not a Rutgers address" >> $DFILE
	endif
endif

# reached this point -- message being rejected
goto reject

# message would be accepted.  make sure it's not spam

caccept:
if ($?VDEBUG) echo "Message conditionally accepted -- make sure it's not spam"
#grep -i "X-Spam-Flag: YES" $TMP > /dev/null
# picked up spam flag from forwarded message; 6/12/08
awk '{if(NF==0)exit;print}' $TMP | grep -i "X-Spam-Flag: YES" > /dev/null
if ($status) then
	if ($?VDEBUG) echo "    (it's not)"
	goto accept
else
	if ($?VDEBUG) echo "    (it is)"
	if ($?DFILE) then
	    echo "Posting denied due to X-Spam-Flag" >> $DFILE
	endif
endif


reject:

if ($?VDEBUG) echo Rejecting message
if ("$MODERATORS" != "" ) then
	if ($?VDEBUG) echo "    (to moderators)"

	set VWHO = `head -1 $TMP | awk '{print $2}'`
	echo From nobody@`hostname` `date` > $BOUNCE
	echo Date: `date` >> $BOUNCE
	echo From: nobody@`hostname` >> $BOUNCE
	grep "^Subject: " $TMP | head -1 | sed 's;: ;: Re: ;' >> $BOUNCE
	echo To: $VWHO >> $BOUNCE
	echo "" >> $BOUNCE
	set LISTNAME = `basename $LIST | sed 's;\.dist;;'`
	echo "You are not allowed to post directly to $LISTNAME." >> $BOUNCE
	echo "" >> $BOUNCE
	echo "Your message is being forwarded to the list moderator(s)." >> $BOUNCE
	if ($?DFILE) then
	   echo Sending moderated warning to $VWHO >> $DFILE
	   echo	/bin/cat $BOUNCE \| /usr/lib/sendmail -i $VWHO >> $DFILE
	endif
	if (! $?NOSEND) then
	    /bin/cat $BOUNCE | /usr/lib/sendmail -i $VWHO
	else
	    echo would really send message to $VWHO
	endif

   if ($?DFILE) then
	echo "Message being sent to moderator(s)" >> $DFILE
   echo	/bin/cat $TMP \| /usr/lib/sendmail -i $MODERATOR>> $DFILE
   endif
	if (! $?NOSEND) then
	    /bin/cat $TMP | /usr/lib/sendmail -i $MODERATOR
	else
	    echo would really send message to $MODERATOR
	endif
else
	if ($?VDEBUG) echo "    (no moderators)"
	grep -i "X-Spam-Flag: YES" $TMP > /dev/null
	set NOTSPAM = $status
	set VWHO = `head -1 $TMP | awk '{print $2}'`
	echo From nobody@`hostname` `date` > $BOUNCE
	echo Date: `date` >> $BOUNCE
	echo From: nobody@`hostname` >> $BOUNCE
	grep "^Subject: " $TMP | head -1 | sed 's;: ;: Re: ;' >> $BOUNCE
	echo To: $VWHO >> $BOUNCE
	echo "" >> $BOUNCE
	set LISTNAME = `basename $LIST | sed 's;\.dist;;'`
	echo "You are not allowed to post to $LISTNAME." >> $BOUNCE
	echo "" >> $BOUNCE
	echo "Your message is being returned:" >> $BOUNCE
	echo "" >> $BOUNCE
	if ($?DFILE) then
	    if ($NOTSPAM) then
		echo Bouncing message back to $VWHO >> $DFILE
		echo	/bin/cat $BOUNCE $TMP \| /usr/lib/sendmail -i $VWHO >> $DFILE
	    else
		echo Not bouncing message back to $VWHO >> $DFILE
	    endif
	endif
	if ($NOTSPAM) then
	    if (! $?NOSEND) then
		/bin/cat $BOUNCE $TMP | /usr/lib/sendmail -i $VWHO
	    else
		echo would really send bounce message to $VWHO
	    endif
	endif
	if ( $?DFILE ) then
		echo Rejected message left in $TMP >> $DFILE
	endif
	exit 1
endif

set RC = 1

if ($?VDEBUG) echo Off to done "(RC = 1)"
goto done

accept:
if ($?VDEBUG) echo Message accepted

if ($?DFILE) then
#   echo	/bin/cat $TMP \| /usr/lib/sendmail `cat $LIST`>> $DFILE
# allow comments in mailing lists; 1/10/06
#    echo	/bin/cat $TMP \| /usr/lib/sendmail `sed 's;#.*;;' $LIST`>> $DFILE
# add envelope from; 4/24/08
#    echo	/bin/cat $TMP \| /usr/lib/sendmail -f $EWHO `sed 's;#.*;;' $LIST`>> $DFILE
# ok, show filtering going on too; 5/5/08
#    echo	/bin/cat $TMP \| $PSCRIPT \| $SSCRIPT \| /usr/lib/sendmail -i -f $EWHO `sed 's;#.*;;' $LIST`>> $DFILE
# handle names in list file too; 5/14/14
    echo	/bin/cat $TMP \| $PSCRIPT \| $SSCRIPT \| /usr/lib/sendmail $SENDMAILVsw -i -f $EWHO `sed -e 's;#.*;;' -e 's;"[^"]*";;' $LIST`>> $DFILE
endif
#	/bin/cat $TMP | /usr/lib/sendmail `cat $LIST`
	if (! $?NOSEND) then
	    /bin/cat $TMP | \
		$PSCRIPT | \
		$SSCRIPT | \
		/usr/lib/sendmail $SENDMAILVsw -i -f $EWHO `sed -e 's;#.*;;' -e 's;"[^"]*";;' $LIST` >& $TMP.sendmail-output
	    set STATUS = $status
	    if ($?DFILE) then
		/bin/cat $TMP.sendmail-output >> $DFILE
	    endif
	    /bin/rm -f $TMP.sendmail-output
	    if ($STATUS) then
		set DQ = '"'
		echo $DQ/bin/cat $TMP \| \
		    $PSCRIPT \| \
		    $SSCRIPT \| \
		    /usr/lib/sendmail $SENDMAILVsw -i -f $EWHO \`sed -e \'s\;\#.\*\;\;\' -e \'s\;\"\[\^\"\]\*\"\;\;\' $LIST\`$DQ \
		  exited with status $STATUS
		set KEEP	# keep tmp file around for post mortem
	    endif
	    if ($?VDEBUG) then
		/bin/cat $TMP | $PSCRIPT | $SSCRIPT
	    endif
	else
#	    echo would really pipe message through $PSCRIPT to $LIST
	    set DQ = '"'
	    echo would really pipe message through $DQ$PSCRIPT$DQ \
		    and $DQ$SSCRIPT$DQ to $LIST
	endif

set RC = 0

if ($?VDEBUG) echo Falling through to done "(RC = 0)"
done:
if ($?VDEBUG) echo Done "(RC = $RC)"
if ($?FILTER) cat $TMP

if (! $?KEEP) /bin/rm -f $TMP

exit $RC
