#!/bin/perl 
# Gopher-nnrp Gateway
# 08-Jun-1993 version 2.2 Chad Adams (c-adams@bgu.edu)
# remove hardcoded paths and make -G with no param work
#
# 28-May-1993 version 2.1 Chad Adams (c-adams@bgu.edu)
# build in access control for clari groups.  Make errors returned the same
#   format as server errors so our version of gopher will put them in pop
#   up box.
#
# 28-May-1993 version 2.0 Chad Adams (c-adams@bgu.edu)
# major rewrite by: Chad Adams
# add newgroups database.
# add multi level newsgroup menus.  [each .part. of newsgroup automaticly
#   gets it's own menu instead of putting all (like all of comp) in one
#   menu.  {now menus like comp.sys, comp.lang, comp.sources, ect..}]
# convert to use xhdr instead of tin's xindex.  If not used with INN using
#   overview files to speed up xhdr it may be slow.
#
# Gopher-NNTP Gateway version 1.0
# Author: Daniel Schales (dan@engr.latech.edu)
# Major rewrite, socket support: Doug Schales (d1s8027@sc.tamu.edu)
#
# Set the 4 following variables for your setup. the 2 port variables
# are set to the standard, be sure to set gopherhost and nntphost to
# your respective hosts.
$gopherhost="your.host.here";
$gopherport=2008;
$nntphost="your.host.here";
$nntpprt='nntp';

$gonnrp = $0; # path to this script
$newsdbm = '/usr/lib/newsgroups'; # where the newsgroups dbm files are

# localaddr for clari access.  Example:
# @localaddr(143, 43, 139, 67);
# allows access to 143.43.*.* and 139.67.*.*
@localaddr = (143, 43, 139, 67);

@INC=("/usr/local/lib/perl");
require 'sys/socket.ph';
dump QUICKSTART if @ARGV[0] eq '-dump';
QUICKSTART:

$SIG{'ALRM'} = 'stuck';
$option=shift;
$option = '-h' if $option eq '-t';
while ($option eq '-f') {
      $copyright = shift;
      $option = shift;
      open(CR, $copyright);
      $title = <CR>;
      close(CR);
      chop($title);
      print "0$title\t$copyright\t$gopherhost\t$gopherport\r\n";
}
$item=shift;
if ($option eq '-X') {
	@arts = @ARGV;
} else {
	$lookup=shift;
}
if (-S STDIN && ($item =~ m/^clari/)) {
	$sockaddr = 'S n a4 x8';
	($fam, $proto, $addr) = unpack($sockaddr,getpeername(STDIN));
	@inetaddr = unpack('C4',$addr);
	for ($i = 0; $i < $#localaddr; $i += 2) {
		$validaccess = 1 if @localaddr[$i] == @inetaddr[0] &&
			@localaddr[$i+1] == @inetaddr[1];
	}
	$_ = 'Off site access not allowed to clari newsgroups  ';
	&checkcode($validaccess,1);
}

# set an alarm 5 minutes from now, if it goes off we must be stuck
alarm(300);
open(LOG,">>/tmp/nntplog");
$date=`date`;chop($date);
print LOG $date," ",$option," ",$item," ",$lookup,"\n";
close(LOG);
$sockaddr = 'S n a4 x8';
($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $nntpport) = getservbyname($nntpprt, 'tcp');
($name, $aliases, $type, $len, $nntpaddr) = gethostbyname($nntphost);

$rsockaddr = pack($sockaddr, &AF_INET, $nntpport, $nntpaddr);

socket(NNTPSOCK, &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
connect(NNTPSOCK, $rsockaddr) || die "connect: $!";

select(NNTPSOCK); $|= 1; select(stdout);

$_ = <NNTPSOCK>;

if ($option eq '-g') {
    dbmopen(newsgroups, $newsdbm, 0444);
    print NNTPSOCK "LIST\n";
    $_ = <NNTPSOCK>;
    chop; chop;
    while($_ ne "."){
	if($_ =~ "^$item"){
	    ($group) = split;
	    push(@out,"1$group - $newsgroups{$group}\texec:-h $group:".
			"$gonnrp\t$gopherhost\t$gopherport\r\n");
	}
	$_ = <NNTPSOCK>;
	chop; chop;
    }
    print sort(@out);
    print ".\r\n";
} elsif ($option eq '-G') {
    dbmopen(newsgroups, $newsdbm, 0444);
    print NNTPSOCK "LIST\n";
    $_ = <NNTPSOCK>;
    chop; chop;
    if ($item ne '') {
	$itemlen = length($item) + 1;
	$dot = '.';
    } else {
	$itemlen = 0;
	$dot = '';
    }
    @grouplist = ();
    while($_ ne "."){
	if($_ =~ "^$item"){
	    	($group) = split;
		push(@grouplist, $group);
	}
    	$_ = <NNTPSOCK>;
    	chop; chop;
    }
    @grouplist = sort(@grouplist);
    for ($i = 0; $i <= $#grouplist; $i++) {
	    $group = @grouplist[$i];
	    if ($group eq $item) {
		$grp = $group;
	        print "1$newsgroups{$group}\texec:-T $group:".
			"$gonnrp\t$gopherhost\t$gopherport\r\n";
	    } else {
		$grp = substr($group,$itemlen,40);
		if (index($grp,'.') != -1) {
		    @grppart = split(/\./,$grp);
		    if (@grppart[0] eq $oldgrp) {
			next;
		    }
		    $oldgrp = @grppart[0];
		    $grp = @grppart[0];
	    	    print "1$grp - ".$newsgroups{"$item$dot$grp.all"}.
			"\texec:-G $item$dot$grp".
			":$gonnrp\t$gopherhost\t$gopherport\r\n";
		} else {
		    if ($group eq substr(@grouplist[$i+1],0,length($group))) {
	    	    	print "1$grp - ".$newsgroups{"$item$dot$grp.all"}.
			    "\texec:-G $group:".
			    "$gonnrp\t$gopherhost\t$gopherport\r\n";
			$oldgrp = $grp;
		    } else {
	    	    	print "1$grp - $newsgroups{$group}\texec:-T $group:".
			    "$gonnrp\t$gopherhost\t$gopherport\r\n";
		    }
		}
	    }
    }
    print ".\r\n";
} elsif($option eq '-X') {
#	$item = newsgroup
#	@arts = articles in this thread
#	  or
#	@arts = 0 low high  if list would be too long
	($code) = &group($item);
	# build arts array if we were passed range
	@arts = split(' ', &buildidx(@arts[1], @arts[2])) if @arts[0] == 0;
	foreach $art (@arts) { $goodart{$art} = 1; }
	&xhdr('from', @arts[0], @arts[$#arts]);
	while (<NNTPSOCK>) {
		last if substr($_,0,1) eq '.';
		chop; chop;
		($art, $from) = split(/ /,$_,2);
		print "0$from\texec:-a ${item} $art:$gonnrp\t".
			"$gopherhost\t$gopherport\r\n" if $goodart{$art};
	}
	print ".\r\n";
} elsif($option eq '-T') {
	($code, $cnt, $low, $high) = &group($item);
	&buildidx($low, $high);
	@keys = sort(keys %idx);
	foreach $key (@keys) {
		@arts = split(' ',$idx{$key});
		if ($#arts == 0) { # single article
			print "0$key\texec:-a ${item} @arts[0]:".
			  "$gonnrp\t$gopherhost\t$gopherport\r\n";
		} else { # thread
			if (length($idx{$key}) < 80) { # send article list
				print "1$key\texec:-X $item$idx{$key}:".
				  "$gonnrp\t$gopherhost\t$gopherport\r\n";
			} else { # give range
				print "1$key\texec:".
				  "-X $item 0 @arts[0] @arts[$#arts]:".
				  "$gonnrp\t$gopherhost\t$gopherport\r\n";
			}
		}
	}
	print ".\r\n";
} elsif($option eq '-l'){
	($code, $count, $start, $end) = &group($item);
	if($count ne "0"){
		print NNTPSOCK "ARTICLE $end\n";
		$body=0;
		$_ = <NNTPSOCK>;
		chop; chop;
		while($_ ne "."){
			if ($body) {
				print "$_\r\n";
			} elsif ($_ =~ "^220 " || $_ =~ "^222 ") {
				$body = 1;
			}
		}
 	        $_ = <NNTPSOCK>;
 	        chop; chop;
 	}
}
# rwp 20Aug92 Add ability to fetch last article.

elsif($option eq '-h' || $option eq '-b' || $option eq '-s'){
	($code, $count, $start, $end) = &group($item);
	if($count ne "0"){
		&xhdr('subject', $start, $end);
		$_ = <NNTPSOCK>;
		chop; chop;
		while($_ ne '.'){
			($num,$desc) = split (/ /,$_,2);
			if ($option eq '-h' ) {
				print "0$desc\texec:-a ${item} ${num}:".
				  "$gonnrp\t$gopherhost\t$gopherport\r\n";
			} elsif ($option eq '-b') {
				print "0$desc\texec:-a ${item} ${num} body".
				  ":$gonnrp\t$gopherhost\t$gopherport\r\n";
			} elsif ($option eq '-s') {
				$desc1="\L$desc\E";
				$lookup1 ="\L$lookup\E";
				if ($desc1 =~ $lookup1 ) {
				 print "0$desc\texec:-a ${item} ${num}:".
				  "$gonnrp\t$gopherhost\t$gopherport\t\r\n";
				}
			}
			$_ = <NNTPSOCK>;
			chop; chop;
		}
	}
	print ".\r\n";
} elsif($option eq '-a'){
	$num = $lookup;
	$part = shift;
	($code) = &group($item);
	if($part eq "body") {
		print NNTPSOCK "BODY $num\n";
		($code) = split(/ /,($_ = <NNTPSOCK>));
		&checkcode($code,222);
	} else {
		print NNTPSOCK "ARTICLE $num\n";
		($code) = split(/ /,($_ = <NNTPSOCK>));
		&checkcode($code,220);
	}
	$_ = <NNTPSOCK>;
	chop; chop;
	while($_ ne "."){
		print "$_\r\n";
		$_ = <NNTPSOCK>;
		chop; chop;
	}
}

print NNTPSOCK "QUIT\n";
shutdown(NNTPSOCK, 2);
exit(0);

sub stuck {
open(LOG,">>/tmp/nntplog");
$date=`date`;chop($date);
print LOG $date," hung on ",$option," ",$item," ",$lookup,"\n";
close(LOG);

exit;
}

# Chad Adams  28-May-1993  tin's xindex to xhdr conversion
sub checkcode { # return error when nntp command failes
	local($code, $goodcode) = @_;
	if ($code != $goodcode) {
		chop; chop;
		print "0nnrp error: $_\t\terror.host\t1\r\n";
		print ".\r\n";
		exit;
	}
}
sub buildidx {	# build subject threads
	local ($low, $high) = @_;
	local ($first, $fsubj, $re, $subj);
	$first = 1;
	&xhdr('subject', $low, $high);
	$cnt = 0;
	while (<NNTPSOCK>) {
		last if substr($_,0,1) eq '.';
		chop; chop;
		($art, $subj) = split(/ /,$_,2);
		while (1) { # remove Re:
			$re = substr($subj,0,2);
			$re =~ tr/A-Z/a-z/;
			if ($re eq 're') {
				$subj = substr($subj,2);
				next;
			} elsif (substr($subj,0,1) eq ':') {
				$subj = substr($subj,1);
				next;
			} elsif (substr($subj,0,1) eq ' ') {
				$subj = substr($subj,1);
				next;
			}
			last;
		}
		if ($first) {
			$fsubj = $subj;
			$first = 0;
		}
		$idx{$subj} .= " $art";
		$cnt++;
	}
	return $idx{$fsubj};
}
sub group { # (code, count, low, high) = &group(newsgroup)
	local(@rtn);
	print NNTPSOCK "group @_[0]\n";
	@rtn = split(/ /,($_ = <NNTPSOCK>), 5);
	&checkcode(@rtn[0],211);
	return @rtn;
}
sub xhdr { # &xhdr(header,low,high)
	local($code);
	print NNTPSOCK "xhdr @_[0] ".@_[1].'-'.@_[2]."\n";
	($code) = split(/ /,($_ = <NNTPSOCK>));
	&checkcode($code,221);
}
