#/usr/local/bin/perl

#----------------------------------------------------------------------
# variables you might have to change:

$nntp_server = "news.cis.umn.edu";
$nntp_port   = 119;
$nntp_defart = "article";
$nntp_groups = "/etc/go4gw/ACTIVE"; # could be active file
$nntp_reverse = 0;  # to list articles in reverse order

#----------------------------------------------------------------------

@nntp_acl=(
#     ipaddress  group    access + = allow, - = deny    
     '^134.84\.  .*       +',
     '^128.101\. .*       +',
     '^131.212\. .*       +',
     '^146.57\.  .*       +',
     '.*         ^clari   -',
     '.*         .*       +'
  );
# end of variables

# Commands this server responds to:
# 
# ""                         -> list top level groups
# ls  [$group] [$range]      -> list group's articles and sub-groups
# lsa [$group] [$range]      -> like "", sends "article" & "lsa" commands back
# lsb [$group] [$range]      -> "" sends "body" & "lsb" commands back
# lsh [$group] [$range]      -> "" sends "header" & "lsh" commands back
# article <id>               -> get article by ID
# article $group $number     -> get 1 article, number can be "last" or "first",
#                               or any numeric perl expression.
# body <id>                  -> like article, but just get text
# body $group $number        -> ""
# head <id>                  -> like article, but just get header
# head $group $number        -> ""
# sorry                      -> send sorry message
#
# The optional $range arguement has the following format:
#      n         - return 'n' most recent articles
#     x:y        - return articles x through y (inclusive)
#                  x and y can both be numeric perl expressions. The
#                  string 'last' is replaced with the last article in the
#                  group, and the string 'first' is replaced with the first.
#   first:last   - all articles (same as not specifiying a range
#   last:first   - all articles (in reverse order)
#  last-9:last   - return last 10 articles (same as specifiying 10)
#  last:last-9   - return last 10 articles in reverse order


sub nntp_main {
  local($_) = @_;

  &do_ls("",$nntp_defart,"ls") if /^$/;
  &do_ls("",$nntp_defart,"ls") if /^ls\s*$/;
  &do_ls($1,$nntp_defart,"ls") if /^ls\s+(.*)/i;

  &do_ls("","article","lsa") if /^lsa\s*$/;
  &do_ls($1,"article","lsa") if /^lsa\s+(.*)/i;

  &do_ls("","body","lsb") if /^lsb\s*$/;
  &do_ls($1,"body","lsb") if /^lsb\s+(.*)/i;

  &do_ls("","head","lsh") if /^lsh\s*$/;
  &do_ls($1,"head","lsh") if /^lsh\s+(.*)/i;

  &do_article($1,$2,"ARTICLE") if /^article\s+(\S+)\s+(\S+)/i;
  &do_article_id($1,"ARTICLE") if /^article\s+(<.*>)/i;

  &do_article($1,$2,"HEAD") if /^head\s+(\S+)\s+(\S+)/i;
  &do_article_id($1,"HEAD") if /^head\s+(<.*>)/i;

  &do_article($1,$2,"BODY") if /^body\s+(\S+)\s+(\S+)/i;
  &do_article_id($1,"BODY") if /^body\s+(<.*>)/i;

  &Gsorry if /^sorry$/;
  &Gabort("Unknown command!");
  exit;
}

sub do_article_id {
  local($id,$cmd) = @_;
  &open_nntp;
  &Gsend("$cmd $id");
  $_ = &Grecv;
  &Gabort($_) if !/^2/;
 
  while(<GSERVER>) {
    print;
    last if /^\.\r\n$/;
  }

  &close_nntp;
  exit;

}

sub do_article {
  local($group,$number,$cmd) = @_;

  if (&check_access($group) eq '-')  { &Gsorry; }
  &open_nntp;

  &Gsend("GROUP $group");
  $_ = &Grecv;
  &Gabort($_) if !/^2/;
  ($n,$f,$l) = /\d+\s+(\d+)\s+(\d+)\s+(\d+)/;

  $number =~ s/first/\$f/g;
  $number =~ s/last/\$l/g;
  $number = int(eval($number));

  &Gsend("$cmd $number");
  $_ = &Grecv;
  &Gabort($_) if !/^2/;
 
  while(<GSERVER>) {
    print;
    last if /^\.\r\n$/;
  }

  &close_nntp;
  exit;
}

sub list_group {
  local($group,$type,$range) = @_;

  &Gsend("GROUP $group");
  $_ = &Grecv;
  &Gabort($_) if !/^211/;

  ($n,$f,$l) = /211\s+(\d+)\s+(\d+)\s+(\d+)/;

  if ($range =~ /^(\S+):(\S+)$/) {
      $low = $1; $high = $2;
      $low =~ s/first/\$f/g;
      $low =~ s/last/\$l/g;
      $high =~ s/first/\$f/g;
      $high =~ s/last/\$l/g;
      $low  = int(eval($low));
      $high = int(eval($high));
      if ($low > $l) { $low = $l; }
      elsif ($low < $f) {$low = $f; }
      if ($high > $l) { $high = $l; }
      elsif ($high < $f) {$high = $f; }

      if ($high < $low)  {  $f = $high; $l = $low;  $nntp_reverse=1; }
      if ($low <= $high) {  $f = $low; $l = $high;  $nntp_reverse=0; }
        
  }
  elsif ($range ne '') {
       $range =~ s/first/\$f/g;
       $range =~ s/last/\$l/g;
       $range = int(eval($range));
       if ($range >0 && $range < $n) {  $f = $l - $range + 1; }
  }

  &Gsend("XHDR Subject $f-$l");
  $_ = &Grecv;
  &Gabort($_) if !/^221/;
 
  while(<GSERVER>) {
    chop; chop;
    last if /^\.$/;
    ($article,$subject) = /^(\d+)\s+(.*)/;
    $subject =~ s/\t/ /g; # just in case!
    if ($nntp_reverse) {
       push(@reply,"0$subject\t$Ggw $atype $group $article\t$Ghost\t$Gport");
    } else {
       &Greply("0$subject\t$Ggw $atype $group $article\t$Ghost\t$Gport");
    }
  }

  if ($nntp_reverse) {
    for ($i=$#reply; $i!= -1; $i--) { &Greply($reply[$i]); } 
  }

  &Greply(".");
  &close_nntp;
  exit;
}

sub do_ls {
  local($prefix,$atype,$lscmd) = @_;
  local($range);

  $prefix =~ s/\s+$//;

  if ($prefix =~ /(\S+)\s+(\S+)/) {
      $prefix = $1;
      $range  = $2;
  }
  elsif (($prefix =~ /^\d+$/) || ($prefix =~ /:/)) {
      $range = $prefix;
      $prefix = "";
  }

  if (&check_access($prefix) eq '-') {
      &Greply("0Sorry! No access off of campus!\t$Ggw sorry\t$Ghost\t$Gport");
      &Greply("."); 
      exit; 
  }

  &open_nntp;
  &get_groups;

  foreach ( sort @groups) {
    if ($_ eq $prefix) { $do_list_group = $_; }
    elsif (/^$prefix\.([^.]*)\.?/) {
      $leaf=$1;
      $save{"$prefix.$leaf"} = "1$leaf\t$Ggw $lscmd $prefix.$leaf $range\t$Ghost\t$Gport";
    }
    elsif ($prefix eq '' && /([^.]*)/) {
         $save{"$1"} = "1$1\t$Ggw $lscmd $1 $range\t$Ghost\t$Gport";
    }
  }

  foreach ( sort keys %save) { &Greply($save{$_}); }
  &list_group($do_list_group,$atype,$range) if ($do_list_group);

  &Greply(".");
  &close_nntp;
  exit;
}

sub open_nntp {
  local($_);
  &GopenServer($nntp_server,$nntp_port);
  $_ = &Grecv;
  &Gabort($_) if !/^2/;
}

sub close_nntp {
  &Gsend("QUIT");
  close(GSERVER);
}

sub get_groups {
 if (open(GROUPS,$nntp_groups)) {
      while(<GROUPS>) {
          chop;
          ($grp) = /^(\S+)/;
          push(@groups,$grp);
      }
      close(GROUPS);
 } else {                  # can't open file, get list from server!
  &load_groups;
 }
}

sub load_groups {

  &open_nntp;
  &Gsend("LIST");
  $_ = &Grecv;
  &Gabort($_) if !/^215/;

  while(<GSERVER>) {
    chop; chop;
    last if /^\.$/;
    s/^(\S+).*/$1/;
    push(@groups,$_);
  }

}

sub create_groups { 
  &load_groups;
  open(GROUPS,">$nntp_groups") || die "$nntp_groups: $!";
  foreach (@groups) { print GROUPS "$_\n"; }
  close GROUPS;
  &close_nntp;
  exit;
}

sub check_access {
   local($group)=@_;

   return 1 if (-t STDIN);
   $sockaddr = 'S n a4 x8';
   $mysockaddr = getpeername(STDIN);
   ($ramily,$rport,$raddr) = unpack($sockaddr,$mysockaddr);
   ($a,$b,$c,$d) = unpack('C4',$raddr);
   $ipaddress = "$a.$b.$c.$d";

   foreach (@nntp_acl) {
      ($ipacl,$groupacl,$access)=split;
      return $access if  ($ipaddress =~ /$ipacl/) && ($group =~ /$groupacl/);
   }
   return '-'; #default is to restrict access
}

1; # for require



