#! /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/local/lib/cnews/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

	# FLAME ON!
	# "wait $!" in the -N and -W cases should *NOT* need the "$!".
	# V7 sh and thus the 4BSD sh got this right; presumably some
	# System V bozo broke it and then changed the manual page to
	# match the breakage, probably while breaking recursive here
	# here documents.  System V: none genuine without the USL
	# sledgehammer of approval.  Any allegedly-sh-compatible shell
	# that doesn't print "12" as the response to
	# "(exit 12) & wait; echo $?" is just plain broken and I don't
	# care what nonsense is in its manual page.  "0" is right out.
	# And if it can't parse "fd=2; true <&$fd", it's also broken.
	# Writing a shell isn't a trivial hack, kids; don't try it at
	# home.

	-N)	justfilter=yes		# don't post, just massage
		waitcmd='wait $!; status=$?' ;;
	-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
		;;
	*)	break ;;			# is a filename
	esac
	shift					# pass option
done

# 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

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 >/tmp/in$$realtmp	# bash headers
 status=$?
 mv /tmp/in$$realtmp $inhdrs
 cat $inhdrs		# | grep -v "^Lines:"	# uncomment if generating Lines:
 case $status in
 0)	;;
 *)
	trap 0			# no cleanup yet
	exit $status		# anne.jones was unhappy - bail out
	;;
 esac
 lines="`			# sop to msb, just uncomment to use

# uncommented to let cnews add Lines header (vds)
 (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
 trap 0				# no cleanup yet
 exit 0) >$censart

case $? in
0)	;;
*)	exit $? ;;		# clean up; anne.jones will have complained
esac

# 64512 = 63*1024 to allow for Path: growth
max=64512
if test "`wc -c <$censart`" -gt $max; then
	echo "$0: your article is over $max bytes, which makes it likely to be" >&2
	echo "$0: dropped by older sites (and it's impolite to post such big ">&2
	echo "$0: articles).  Try splitting it up, if you must." >&2
	exit 1			# clean up
fi

### 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
				# unApproved article: mail it to the moderator(s).
				# look for a route for this group ($ng).
				# a dreadful B 2.11 hack: backbone == all
				nl=$NEWSCTL	# short form
				(sed 's/^backbone[	 ]/all /' $nl/mailpaths |
				    gngp -a -r "$ng";
				 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

case "$justfilter" in
yes)
	cat $censart
	rm -f $rmlist			# far out, it worked: clean up
	trap 0				# normal exit: cleanup done
	exit 0
	;;
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
