#! /bin/sh
# checkgroups - check active file for missing or extra newsgroups or groups
#	with incorrect moderation status, and update the newsgroups file.
#	stdin must a checkgroups news article
# also subject to $NEWSCTL/controlperm:  four fields per line, first
# a newsgroup pattern, second an author name (or "any"), third a set of
# operations ("n" newgroup, "r" rmgroup, "c" checkgroups), and fourth a set of
# flags ("p" do it iff poster's identity is pgpverified, 
# "y" do it, "n" don't, "q" don't report at all, "v" include
# entire control message in report) (default "yv"); the "p" and "n" flags may
# be followed by the ID of the person permitted to pgpverify;
# the pgpverify program (not supplied) is presumed to be in $NEWSBIN

# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
. ${NEWSCONFIG-/usr/local/libexec/cnews/config}

PATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
desc=/tmp/cg$$d
hiers=/tmp/cg$$h
canact=/tmp/cg$$a
newact=/tmp/cg$$n
diffs=/tmp/cg$$df
actgrps=/tmp/cg$$ag
newgrps=/tmp/cg$$ng
samegrps=/tmp/cg$$sg
addgrps=/tmp/cg$$ad
remgrps=/tmp/cg$$rm
sameact=/tmp/cg$$sa
samenew=/tmp/cg$$sn
modchng=/tmp/cg$$mc
posting=/tmp/cg$$ps
hdr=/tmp/cg$$hd

umask $NEWSUMASK

if [ "$1" = "-f" ]
then force="force"
fi

# get the full article, and its header, into files for inspection
trap 'rm -f /tmp/cg$$* ; trap 0 ; exit' 0 1 2 15
cat > $posting
canonhdr $posting >$hdr

# who sent it?
author="`egrep '^From:' $hdr | sed 's/^[^:]*: *//' `"
authorid="`echo \"$author\" | sed '/.*<\(.*\)>.*/s//\1/
				   /\([^ ][^ ]*\)  *(.*).*/s//\1/'`"

# was it approved?
case "`egrep '^Approved:' $hdr`" in
'')	reject=${reject-'no Approved header'} ;;
esac

# was it pgpsigned?
case "`egrep -i '^X-PGP-Sig: ' $hdr`" in
?*)	if test -x $NEWSBIN/pgpverify
	then	signer=`pgpverify < $posting`
		pgpresult=$?
		case "$pgpresult" in
		0) ;; # Authentication succeeded
		3) reject=${reject-'authentication failed'} ;;
		*) ;;
		esac
	else	pgpresult=1
	fi ;;
*)	pgpresult=1 ;;
esac

# Behead checkgroups article & ignore headers and initial lines that don't
# fit the syntax (see son-of-1036). This is permissive insofar as introductory
# text and signatures should not really occur in a checkgroups message. But
# if they did and were allowed through severe corruption of the newsgroups
# file could ensue. As a byproduct, tale's periodic "Checkgroups message
# (with/without INET groups)" may be piped directly into this script.
# Also, spaces rather than tabs after the newsgroup name are permitted, but
# some white space is required.
# ??? does this raise a problem with Bitnet ???
# "Invalidations" (e.g. !mod) are recognised but not acted upon.
# There is no provision for "encoded words" (see son-of-1036).

ngletgit='a-z_0-9'
ngalpha='-_+a-z0-9'
if test `awk	"
	BEGIN {state = 1}
	/^![$ngletgit][$ngalpha]*(,[$ngletgit][$ngalpha]*)*$/ \
		{if (state == 1) {state = 2; next} else {state = 3; exit} }
	/^[$ngletgit][$ngalpha]*(\.[$ngletgit][$ngalpha]*)+[ 	]+[ -_a-~]*$/ \
		{state = 2; print > \"$desc\"; next}
	/^$/	{next}
	/^-- $/	{exit}
		{if (state == 2) {state = 3; exit} }
	END	{print state}" $posting ` \
	-ne 2
then
	reject=${reject-'ill-formed checkgroups message - edit before resubmitting'}
fi

# generate list of hierarchies affected
sed 's/\..*//' $desc | sort -u >$hiers
hierlist="`cat $hiers`"		# message is assumed authoritative for these
hierpat="` echo $hierlist | tr ' ' , `"	# one more time, with commas

# consult control file, if present
perms=$NEWSCTL/controlperm
action=nv
if test -r $perms
then
	newaction=`gngp -ar $hierpat $perms | awk '$3 ~ /c/' |
		awk '$2 == "any" || "'"$authorid"'" ~ $2 { printf "%s %s\n",$4,$5 }' |
		sed -n 1p`
	case "$newaction" in
	?*)	action=$newaction	;;
	esac
fi
# $action is of the form "y|n|p[q|v] [authorized ID]"
case "$action" in
n*)	reject=${reject-'controlperm file denies permission'}
 	;;

# use $pgpresult computed earlier if controlperm file requires it
p*)	case "$pgpresult" in
 	0) authorized=`echo "$action" | awk '{print $2}'`
	   case "$authorized" in
	   "$signer")	;; # Authentication succeeded
	   '')		;; # Authentication succeeded
	   ?*)		reject=${reject-"unauthorized signature by '$signer' in newgroup message"}
	   		signer='';;
	   esac ;;
 	1) reject=${reject-'newgroup message not signed'} ;;
 	2) reject=${reject-'unrecognized signature in newgroup message'} ;;
 	3) reject=${reject-'authentication failed'} ;;
 	*) reject=${reject-'unknown pgpverify error'} ;;
 	esac
 	;;
esac

# the verdict
case "$force$reject" in
force*)	;; #allow it because it was forced
?*)	case "$action" in
 	*q*)	;;
 	*)	(	echo "checkgroups: \`$author' tried"
			echo "to checkgroups on $NEWSCTL/newsgroups."
			echo "  $reject"
			case "$signer" in
			?*)	echo "But valid signature from '$signer' was given" ;;
			esac
			echo "Use $NEWSBIN/ctl/checkgroups -f"
			echo "to do it by hand, if appropriate."
			case "$action" in
			*v*)	echo '==='
				cat $posting
				echo '==='
				;;
			esac
		) | report 'rejected checkgroups'
		;;
	esac
	exit
	;;
esac

# do the job
# backup newsgroups before updating it
if test -r $NEWSCTL/newsgroups
then
	cp $NEWSCTL/newsgroups $NEWSCTL/newsgroups.bac || exit 1
else
	>$NEWSCTL/newsgroups.bac
fi
# toss out old newsgroups rubbish
(gngp -av "$hierpat" $NEWSCTL/newsgroups.bac
 # add new newsgroups rubbish
 cat $desc) >$NEWSCTL/newsgroups

# canonicalise active file & select interesting hierarchies
awk '{
	modstat = $4
	if (modstat != "y" && modstat != "m")
		modstat = "y"
	print $1, modstat
}' $NEWSCTL/active | gngp -a "$hierpat" | sort >$canact

# canonicalise body into an active-file-like thing
awk '
/Moderated/	{ print $1, "m" } # TODO: " (Moderated)$"? as per B 2.11.19
!/Moderated/	{ print $1, "y" }
' $desc | sort >$newact

# what's different?  first, what groups have vanished or appeared?
sed 's/ .*//' $canact >$actgrps
sed 's/ .*//' $newact >$newgrps
comm -12 $actgrps $newgrps >$samegrps
comm -23 $actgrps $newgrps >$remgrps
comm -13 $actgrps $newgrps >$addgrps
if test -s $remgrps; then
	echo
	echo 'obsolete groups:'
	cat $remgrps
fi >$diffs
if test -s $addgrps; then
	echo
	echo 'new groups:'
	join $addgrps $newact
fi >>$diffs

# next, what surviving groups have changed moderation status?
join $samegrps $canact >$sameact
join $samegrps $newact >$samenew
comm -13 $sameact $samenew >$modchng
if test -s $modchng; then
	echo
	echo 'groups needing moderation status changed to that shown:'
	cat $modchng
fi >>$diffs

if test -s $diffs; then
	(echo "Subject: possible active file problems"; echo;
	 echo "$author issued a checkgroups control message."
	 case "$signer" in
	 ?*)	echo "A valid signature from '$signer' was given." ;;
	 esac
	 echo "If you believe this checkgroups control message for hierarchies"
	 echo "\`$hierpat', the following differences may reflect groups"
	 echo "that should be added, deleted, or have their moderation"
	 echo "status(es) changed:"
	 cat $diffs) | report 'checkgroups output'
fi
exit 0		## end of new one
