#! /bin/sh
# inews [-p] [-debug k] [-x site] [-hMD] [-t subj] [-n ng] [-e exp] [-F ref] \
#  [-d dist] [-a mod] [-f from] [-o org] [-C ng] [file...] - inject news:
#	censor locally-posted article and field the "inews -C" kludge;
#	munge the articles, enforce feeble attempts at Usenet security,
#	generate lots of silly headers.
#
# Yes, it's big, slow and awkward.  The alternative is casting a lot of
# local policy in C.

# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
. ${NEWSCONFIG-/usr/lib/news/bin/config}
export NEWSCTL NEWSBIN NEWSARTS NEWSPATH NEWSUMASK NEWSMASTER NEWSCONFIG
PATH=$NEWSCTL/bin:$NEWSBIN/inject:$NEWSBIN/relay:$NEWSBIN:$NEWSPATH; export PATH
PASSEDFROM='';	export PASSEDFROM	# passed to anne.jones in environ.

debug=''			# flags
exclusion=''
hdrspresent=no
autopost=no
waitcmd='status=0'
relayopts=-i			# redirect stdout to log

input=/tmp/in$$in		# uncensored input
inhdrs=/tmp/in$$hdr		# generated by tear: headers
inbody=/tmp/in$$body		# generated by tear: body
censart=/tmp/in$$cens		# censored input
nglist=/tmp/in$$ngs		# newsgroups: list
modroute=/tmp/in$$route		# route to moderator's forwarder
exitflag=/tmp/in$$exit		# exit status, if present
outfile=/tmp/in$$out		# relaynews stdout
rmlist="$inhdrs $inbody $input $censart $nglist $modroute $exitflag $outfile"
grpok=/tmp/in$$grp		# flag file: groups okay if present

umask $NEWSUMASK

# "inews -p": invoke rnews
case "$1" in
-p)
	shift
	cat $* | relaynews -r	# feed directly to relaynews, seat belts off
	exit
	;;
esac

# process arguments: for options, cat headers onto $input; cat files onto $input
>$input
cleanup="test ! -f $HOME/dead.article -o -w $HOME/dead.article &&
  cat $input >>$HOME/dead.article &&
  { echo $0: article in $HOME/dead.article >&2; rm -f $rmlist; }; exit 1"
trap "$cleanup" 0 1 2 3 15
while :
do
	case $# in
	0)	break ;;		# arguments exhausted
	esac

	case "$1" in
	# peculiar to C news
	-debug)	shift; debug="$1" ;;
	-A)	autopost=yes ;;		# wait for free space
	-V)	relayopts= ;;		# verbose: don't redirect stdout (or stderr)
	-W)	waitcmd='wait; status=$?' ;;	# wait for completion
	# useful standard options
	-h)	hdrspresent=yes ;;
	-x)	shift; exclusion="-x $1" ;;	# you're welcome, erik (2.11)
	# silly options supplied by newsreaders
	-a)	shift; echo "Approved: $1" >>$input ;;
	-c)	shift; echo "Control: $1" >>$input ;;
	-d)	shift; echo "Distribution: $1" >>$input ;;
	-e)	shift; echo "Expires: $1" >>$input ;;
	-f)	shift; echo "From: $1" >>$input ;;
	-n)	shift; echo "Newsgroups: $1" >>$input ;;
	-r)	shift; echo "Reply-To: $1" >>$input ;;
	-t)	shift; echo "Subject: $1" >>$input ;;	# aka Title:
	-D)	# obsolete, undocumented: meant "don't check for recordings".
		# last present in B 2.10.1, invoked by readnews for followups.
		;;
	-F)	# undocumented in B 2.10.1, documented in B 2.11.
		shift; echo "References: $1" >>$input ;;
	-M)	# this apparently just sets From: to the author of the article
		# instead of the poster (moderator), by leaving the From: line
		# alone (under -h); easy to implement.
		;;

	# pass next options as environment variables to anne.jones
	-o)	shift; ORGANIZATION="$1"; export ORGANIZATION ;;

	-C)
		cat <<eg >&2
$0: you either want to use addgroup (see newsaux(8)) to make
$0: $2 locally, or this to make it network-wide:
inews -h <<'!'
Control: newgroup $2
Subject: newgroup $2
Newsgroups: $2
Approved: you@hostname

Non-empty.
!
eg
		exit 1
		;;
	-*)
		echo "$0: bad option $1" >&2
		exit 1
		;;
	*)					# is a filename; append file
		# B 2.11 kludge: assume -h if input starts with headers.
		# apparently the B 2.11 newsreaders assume this.
		tear /tmp/in$$ <$1
		if test -s $inhdrs; then
			hdrspresent=yes
		fi

		case "$hdrspresent" in
		no)	echo "" >>$input; hdrspresent=yes ;;
		esac
		# capture incoming news in case relaynews fails
		if cat $inhdrs $inbody >>$input; then
			: far out
		else
			echo "$0: lost news; cat status $?" >&2
			exit 1
		fi
		fileseen=yes
		;;
	esac
	shift		# pass option or filename (any value was done above)
done

# if no files named, read stdin
case "$fileseen" in
yes)	;;
*)
	# B 2.11 kludge: assume -h if input starts with headers
	# apparently the B 2.11 newsreaders assume this.
	tear /tmp/in$$
	if test -s $inhdrs; then
		hdrspresent=yes
	fi

	case "$hdrspresent" in
	no)	echo "" >>$input; hdrspresent=yes ;;
	esac
	# capture incoming news in case relaynews fails
	if cat $inhdrs $inbody >>$input; then
		: far out
	else
		echo "$0: lost news; cat status $?" >&2
		exit 1
	fi
	;;
esac
trap '' 1 2 15			# ignore signals to avoid losing articles

# run the remainder in the background for the benefit of impatient people
# who lack a window system
(
trap "$cleanup" 0
tear /tmp/in$$ <$input		# output in $inhdrs and $inbody
# canonicalise header keyword capitalisation.
# greps for Control: and Approved: later assume this, as does defhdrs.awk.
canonhdr <$inhdrs >/tmp/in$$realtmp
mv /tmp/in$$realtmp $inhdrs
# pad zero-line articles, since old B [ir]news are confused by them
# and the news readers generate zero-line control messages, alas.
if test ! -s $inbody; then
	(echo '';
	 echo This article was probably generated by a buggy news reader.) \
	 >$inbody
fi

# deduce which tr we have: v6 or v7
case "`echo B | tr A-Z a-z `" in
b)	trversion=v7 ;;
B)	trversion=v6 ;;			# or System V
esac
export trversion

# post with new headers and .signature
(anne.jones <$inhdrs		# bash headers
# lines="`			# sop to msb, just uncomment to use
# (sed 1d $inbody;	# take out the first (blank) line
# if test -r $HOME/.signature; then
# 	echo '-- '
# 	sed 4q $HOME/.signature
# fi) | wc -l `"
# echo Lines: $lines

 # strip invisible chars from body, a la B news.  bells and escapes are right out.
 case "$trversion" in
 v7)	tr -d  '\1-\7\13\15-\37' ;;
 v6)	tr -d '[\1-\7]\13[\15-\37]' ;;
 esac <$inbody

 if test -r $HOME/.signature; then
	echo "-- "; sed 4q $HOME/.signature	# glue on first bit of signature
 fi) >$censart

### the article is fully assembled now in $censart ###

# to post or to mail? that is the question; whether 'tis nobler in the mind
# to suffer the slings and arrows of outrageous mailers - Bill Shakespeare
if grep -s '^Control:' $inhdrs >/dev/null; then
	echo "control"			# a dreadful hack around all.all.ctl
else
	sed -n '
/^Newsgroups:[	 ]/{
s/^Newsgroups:[	 ]*\(.*\)$/\1/p
q
}
' <$inhdrs
fi >$nglist

if test ! -s $nglist; then		# no Newsgroups:
	exit 1				# anne.jones will have already complained
fi

# look up groups in active, to determine disposition of this message.
# n, x and (unapproved) m flags are dealt with on the spot; if none are
# seen, the article is posted normally.
# escape egrep metacharacters.  In theory one could add " ' ` \ to the list.
egreppat="^(` sed -e 's/[.+*()|[]/\\\\&/g' -e 's/,/|/g' <$nglist `) "
egrep "$egreppat" $NEWSCTL/active >/dev/null || {
	echo "$0: `cat $nglist` matches no groups in $NEWSCTL/active" >&2
	exit 1
}
rm -f $grpok
egrep "$egreppat" $NEWSCTL/active |
	(while read ng high low flag junk	# look at next group's active entry
	do
		>>$grpok
		case "$flag" in
		[nx])
			echo "$0: sorry, $ng may not be posted to locally." >&2
			trap 0		# this is a child process - no cleanup here
			echo 1 >$exitflag
			exit 1		# dregs in /tmp/in$$*
			;;
		m)
			if grep -s '^Approved:[	 ]' $inhdrs >/dev/null; then
				:		# just post normally
			else
				# un-Approved article: mail it to the moderator(s).
				# look for a route for this group.
				# a dreadful B 2.11 hack: backbone == all
				(sed 's/^backbone[	 ]/all /' \
							$NEWSCTL/mailpaths |
						gngp -a -r "`cat $nglist`";
						echo 'default	%s') |
					sed -n "1{s/^[^	 ]*[	 ][	 ]*//
					  s/%s/` echo $ng | tr . - `/;p;q;}" \
						>$modroute
				moderator="`cat $modroute `"
				echo "mailing your article to $moderator"
				mail "$moderator" <$censart
				trap 0	# this is a child process - no cleanup here
				echo 0 >$exitflag
				exit 0
			fi
			;;

			# "" matches short active entries,
			#	to be backward compatible.
			# * matches garbage flags, to be cautious.
		y|""|*)
			# okay so far, but wait until we see all Newsgroups:.
			;;
		esac
	done
	trap 0					# paranoia - no clean up
	)
if test ! -r $grpok; then
	echo "$0: no active groups in `cat $nglist`" >&2
	exit 1			# abnormal exit - cleans up, makes dead.article
fi
rm -f $grpok
if test -f $exitflag; then
	exitstatus="`cat $exitflag`"
	case "$exitstatus" in
	0)	rm -f $rmlist; trap 0 ;; # normal exit - cleanup, no dead.article
	esac
	exit $exitstatus	# trap 0 may cleanup, make dead.article
fi

### if the article was mailed, that happened above; posting will happen below ###

# deal with inadequate free space
case "$autopost" in
no)
	if test "`spacefor 1 articles`" -le 0; then
		echo "$0: too little space free on $NEWSARTS" >&2
		exit 1			# dregs in /tmp/in$$* for trap 0
	fi
	;;
*)
	iter=0
	while test "`spacefor 1 articles`" -le 0 -o "`spacefor 1 control`" -le 0
	do
		sleep 30
		iter=`expr $iter + 1`
		case "$iter" in
		3)
			mail "$NEWSMASTER" <<!
Subject: free space too low on $NEWSARTS

There is too little free space on $NEWSARTS for inews to run comfortably.
!
			;;
		esac
	done
	;;
esac

# to get here, we must have seen no n, x, nor (unapproved) m flags.
# <$censart is used rather than a pipe to work around a bug in the 4.2 sh
# which made it sometimes return the wrong exit status (that of anne.jones).
# execute relaynews commands on the server, for the sake of locking.
# may not use "exec" or sh will leave /tmp/sh* files from here docs in /tmp.
me="`hostname`"
server=`cat $NEWSCTL/server 2>/dev/null`
case "$server" in
"")	server="$me" ;;			# if no server file, assume this is it
esac
case "$me" in
$server)
	relaynews $relayopts -s $exclusion -d "$debug" <$censart
	status=$?
#	echo "status $? from relaynews" >>/tmp/inewsdebug # DEBUG
	;;
*)
	# send article+commands to remote shell, including clean up
	(echo "sed 's/^-//' >/tmp/irsh\$\$ <<'!'"	# remove guard
	 sed 's/^[^A-EG-Za-z0-9]/-&/' $censart	# prepend guard
	 echo !
	 cat <<!
PATH=$PATH relaynews $relayopts -s $exclusion -d "$debug" </tmp/irsh\$\$
!
	 echo 'echo status $?'
	 echo 'rm -f /tmp/irsh$$') |
		rsh $server /bin/sh >$outfile

	status=`sed -n '/^status /s///p' $outfile `
	sed '/^status /d' $outfile	# print relaynews's stdout
	;;
esac
case "$status" in
0)
	rm -f $rmlist			# far out, it worked: clean up
	if test ! -f $NEWSCTL/sys; then
		echo "$0: $NEWSCTL/sys missing; your news can't leave this machine" >&2
	fi
	trap 0				# normal exit: cleanup done
	;;
esac
exit $status				# trap 0 may cleanup, make dead.article
) &
eval $waitcmd				# wait & get status if -W given
trap 0					# let the background run on unmolested
exit $status
