#!/usr/local/bin/perl
#
# $Header: reader,v 1.4.1.1 93/08/30 03:18:51 wjm Exp $
#
# reader - reads hitfiles from Regex-News
# copyright (c) Bill Middleton (wjm@feenix.metronet.com) 1993
# All rights reserved
# Send bugs, patches, and comments to me.
#
# $Log:	reader,v $
# Revision 1.4.1.1  93/08/30  03:18:51  03:18:51  wjm (Bill Middleton)
# fixed to allow dbz readers to trace references
# startup from any dir will look for the .hits file
# in the correspondent subscriber directory, if not on
# the command line
# 
# Revision 1.4  93/08/26  01:54:58  01:54:58  wjm (Bill Middleton)
# added searching through hits for regex with /[RegEx]
# added simple shelling out capability
# 
# Revision 1.3.1.1  93/08/25  12:56:15  12:56:15  wjm (Bill Middleton)
# fixed - return error from paging article that DNE
# changed - formatting to display more matched Reg Ex
# fixed - couple other little things, cant remember, was late.
# 
# Revision 1.3  93/08/24  13:51:02  13:51:02  wjm (Bill Middleton)
# Couple of help things updated, and changed the display to include
# more of the matched RegEx.
# 
# Revision 1.2  93/08/24  03:11:39  03:11:39  wjm (Bill Middleton)
#  Made able to use either nntp or dbz.  Fixed a bug
# to let selection of lower numbers work correctly.
# 
# Revision 1.1  93/08/24  01:53:09  01:53:09  wjm (Bill Middleton)
# Initial revision
# 
# 
$id=$ENV{USER} ||
    $ENV{LOGNAME} ||
    getpwuid($<) ||
    die "No username.";
$home=$ENV{HOME} ||
    $ENV{LOGDIR} ||
    (getpwuid($<))[7] ||
    die "No Home directory.";
require ("chat2.pl");
$hitfile=shift;
$subscribe_dir='../subscribers';  
($id='demo') unless (-d "$subscribe_dir/$id");
($hitfile="$subscribe_dir/$id/.hits") unless defined($hitfile);
($news_port,$my_nntp_host) = (119,"metronet.com");
$pager='/usr/local/bin/less';
$using_dbz=1;
$using_nntp=0;
$using_INN_server=0;
$lockfile=".lock";
$SIG{'HUP'}=$SIG{'INT'}=$SIG{'TERM'}=$SIG{'QUIT'}=$SIG{'ALRM'} = 'cleanup';
if(defined $ENV{'EDITOR'}){$ed = $ENV{'EDITOR'};}
else{$ed = 'vi'};
$how_we_post="/bin/inews";
$how_we_mail="/usr/lib/sendmail -t ";
$did='wrote';
@hits_start=stat("$hitfile");

# get the nntp server going

if($using_nntp){
  $N = &chat'open_port($my_nntp_host,$news_port);
  select($N);$|=1;select(STDOUT);
  if(!defined $N){ die "open control: $!";}
  $line = &chat'expect($N,5, '^200(.*)\r?\n', '"$1"' );
  if ($using_INN_server){  
    &chat'print($N,"mode reader\r\n");
    $line = &chat'expect($N,5, '^201(.*)\r?\n', '"$1"' );
  }
  $line='';
}elsif($using_dbz){  # edit the following for your news database
  $toplevel_news_dir='/news';     
  $dbztmp="$home/RegEx.dbz.$$";
  $history='/usr/lib/news/history';
  $path_to_dbz='/usr/lib/newsbin/dbz';
  open(DBZ,"|$path_to_dbz -x $history > $dbztmp");
}else{
  die "You gotta define a way to get the articles\n";
}
open(HITS,"<$hitfile");
while (<HITS>){
  chop;
  @ar = split(/\t/,$_);
  next if (defined $arts{$ar[0]});
  next unless (defined $ar[0]);
  $arts{$ar[0]} = "$ar[1]\t$ar[2]\t$ar[3]\t$ar[4]";
  ($using_dbz) && (print DBZ "$ar[0]"."\n");
}
($using_dbz) && (close DBZ) && (&get_path_names());
&read_em;
&cleanup;

   

sub read_em{
  @artids=sort bygroup keys(%arts);
  local($plen) = 19;
  system "clear";
  for(;;){
    if($i>=$plen){
      ($j>=$#artids)? 
      (print "\n$#artids of $#artids: "):(print "\n".($j-1)." of $#artids: "); 
      print "select article number, or h for help > ";
      chop($num=<STDIN>);
      if($num =~ /^[Ff]/){
       $j += ($plen) unless ($j>=$#artids); 
      }elsif($num =~ /^[Bb]/){
        $j -= ($plen) unless ($j <= ($plen+1)); 
        ($j < 0) && ($j=0);
      }elsif($num =~ /^[Hh]/){
        &give_a_clue1;
      }elsif($num =~ /^[Qq]/){
        last;
      }elsif($num =~ /^\!(.*)/){
        ($com = $1);
        ($com = "sh") unless (length($com)>1);
        system("$com");
        print " Carriage Return to continue > ";
        chop($num=<STDIN>); 
      }elsif(($num =~ /^\/(.*)\n?$/) && ($key = $1)){
        for($k=$j;$k <= $#artids;$k++){
	  if(($artids[$k] =~ /$key/)||($arts{$artids[$k]}=~/$key/)){
            $j=$k+$plen;last;
          }
        }
        if($j!=($k+$plen)){ 
          print "Not found from $j to $k, <CR> to continue\n";
          chop($num=<STDIN>);
        }
      }elsif($num =~ /^[Dd]/){
        print "\nEnter hits to mark for deletion >";
        chop($ans=<STDIN>);
        @specs=split(',',$ans);
        for $spec (@specs){
         if($spec=~/^\d*$/){
           defined($artids[$spec]) ?
             (delete($arts{$artids[$spec]})):(print "$spec: no such number\n"); 
         }elsif($spec=~/^(\d+)\s*[-]\s*(\d+)/){
           ($first,$last)=($1,$2);
           (($first>$#artids)||($last>$#artids))&&(print "Bad range\n")&&(next);
           (($first<0)||($last<0))&&(print "Bad range\n")&&(next);
           ($first>$last)&&(print "Bad range\n")&&(next);
           for ($first..$last){ delete($arts{$artids[$_]})||last;}
         }
       }
      }elsif(($num =~ /^\d+/)&&($num >= 0) && ($num <= $#artids)){
        $ret = &page_art($num,@artids);
        $ret =~ /resort/ && (@artids=sort bygroup keys(%arts)) && next; 
        ($ret > $j) && ($j = $ret+$plen);
        ($ret < ($j-$plen)) && ($j = $ret+$plen);
      }
      $j-=($plen); 
      system "clear";
      $- = 0;
      $i=0; next;
    }elsif($j<0){
     $j=$i;next;
    }else{
      $t="$j".">";
      ($j==0)&&($t='0>');
      if($j > $#artids){$t=undef;($subj,$froup,$hit,$from) = ();
      }else{
       ($subj,$froup,$hit,$from) = split(/\t/,$arts{$artids[$j]});
       ($froup eq "Referenced Art") && ($from = "> Reference");
       $date=~s/^\S+\s+(\S+\s+\d+)/$1/;
      }
      write;
      $i++;$j++;
    }
  }
}    
      

format STDOUT_TOP =
Num     From          Newsgroup                Subject                RegEx
--------------------------------------------------------------------------------
.
 
format STDOUT =
@<< @<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<<<<<<<<<  @|||||||||||||
$t,$from,$froup,$subj,$hit
.
 

sub save_art{
local ($path)=$_[0];
local($name);
print "Save to what filename?  Enter name > ";
chop($name=<STDIN>);
$name=~s/^[~]/$home/;
$ok = system "cp $home/RegEx.tmpfile.$$ $name";
if($ok != 0){
warn "That didn't work at all..\n";
}
}


sub reply_art{
local ($path)=@_;
open(REPLYART,"<$home/RegEx.tmpfile.$$");
$/ = ""; $headers=<REPLYART>; $/ = "\n";
local($to,$subject,$msgid,$refs,$rto,$from,$ngps)=&parse_headers($headers);
if(!(length($to))||!(length($subject))||!(length($msgid))){
  print "a little confusion..."; return;
}
print "Are you sure you wanna do this?";  
chop($ans=<STDIN>);
$ans=~/^[Nn]/ && return;
open(TMPREPLY,">$home/RegEx.reply.$$");
chmod(0700,"$home/RegEx.reply.$$");
defined($rto)&&($to=$rto);
print TMPREPLY "To: $to\n";
print TMPREPLY "Newsgroups: $ngps\n"; 
print TMPREPLY "X-Mailer: RegEx News (version 0.1)\n";
print TMPREPLY "In-Reply-To: $msgid\n"; 
print TMPREPLY "Subject: $subject\n\n";

print "Use a copy of article in reply? y/n >";
chop($ans=<STDIN>);
if($ans=~/[Yy]/){
  print TMPREPLY "In article $msgid you $did:\n";
  while(<REPLYART>){
    print TMPREPLY ">"."$_";
  }
}
close TMPREPLY;
close REPLYART;
print "edit your message now\n";
system "$ed $home/RegEx.reply.$$";
for(;;){
  print "send, edit, abort, quit > ";
  chop($ans=<STDIN>);
  if($ans=~/[Ss]/){
   &mail_it;
   unlink "$home/RegEx.reply.$$";
   close SENDMAIL;
   return;
  }elsif($ans=~/[Ee]/){
   system "$ed $home/RegEx.reply.$$";
  }elsif($ans=~/[Aa]||[Qq]/){
   print "aborting...\n";
   unlink "$home/RegEx.reply.$$";
   return;
  }
  
}
}
sub parse_headers{
local($headers)=@_;
local($to,$subject,$msgid,$refs,$rto,$from,$ngps,$dist,$kwds);
 ($headers=~/From:\s(.*)[\n]{1,1}/) && ($to = $1);
 ($headers=~/Reply-To:\s(.*)[\n]{1,1}/) && ($rto = $1);
 ($headers=~/Subject:\s(.*)[\n]{1,1}/) && ($subject = $1);
 ($headers=~/Message-ID:\s(.*)[\n]{1,1}/) && ($msgid = $refs = $1);
 ($headers=~/Newsgroups:\s(.*)[\n]{1,1}/) && ($ngps = $1);
 ($headers=~/Followup-To:\s(.*)[\n]{1,1}/) && ($ngps = $1);
 ($headers=~/References:\s(.*)[\n]{1,1}/) && ($refs .= $1);
 ($headers=~/Distribution:\s(.*)[\n]{1,1}/) && ($dist .= $1);
 ($headers=~/Keywords:\s(.*)[\n]{1,1}/) && ($kwds .= $1);
 ($subject=~/^Re:/)||($subject="Re: ".$subject);
($to,$subject,$msgid,$refs,$rto,$from,$ngps,$dist,$kwds);
}

sub post_art{
local ($path)=@_;
open(REPLYART,"<$home/RegEx.tmpfile.$$");
$/ = "";
$headers=<REPLYART>;
$/ = "\n";
local($to,$subject,$msgid,$refs,$rto,$from,$ngps,$dist,$kwds)
  =&parse_headers($headers);
if(!(length($to))||!(length($subject))||!(length($msgid))){
  print "a little confusion while parsing headers..."; return;
}
print "\nYour post may cost the net BUZZILLIONS of dollars, do you REALLY\n";
print "feel like you have something worthwhile to say here? (y|n) > ";
chop($ans=<STDIN>);
$ans=~/[Nn]/ && return; 
open(TMPREPLY,">$home/RegEx.followup.$$");
print TMPREPLY "Newsgroups: $ngps\nSubject: $subject\nSummary:\n";
print TMPREPLY "Distribution: $dist\nReferences: $refs\n";
print TMPREPLY "X-Newsreader: RegEx News (version 0.1)\n";
print TMPREPLY "Followup-To:\n";
print TMPREPLY "Keywords:\n\n";

print "Use a copy of article in followup? y/n >";
chop($ans=<STDIN>);
if($ans=~/[Yy]/){
  print TMPREPLY "In article $msgid $to $did:\n";
  while(<REPLYART>){
    print TMPREPLY ">"."$_";
  }
}
close TMPREPLY; close REPLYART;
print "edit your message now\n";
system "$ed $home/RegEx.followup.$$";
for(;;){
  print "send, edit, abort, quit > ";
  chop($ans=<STDIN>);
  if($ans=~/[Ss]/){
   open(TMPREPLY,"<$home/RegEx.followup.$$");
   $dunright = &post_it;
   unlink "$home/RegEx.followup.$$";
   return;
  }elsif($ans=~/[Ee]/){
   system "$ed $home/RegEx.followup.$$";
  }elsif($ans=~/[Aa]||[Qq]/){
   print "aborting...\n";
   unlink "$home/RegEx.followup.$$";
   return;
  }
  
}
}

sub post_it{
 system("$how_we_post < $home/RegEx.followup.$$");
1;
}
sub mail_it{
 system("$how_we_mail < $home/RegEx.reply.$$");
1;
}

sub give_a_clue1{
local ($ans);
system ("clear");
print <<"HelpEnd";
                  RegEx News Help Section - Main Menu Help

All commands must be followed by a carriage return
At the main menu of hits (articles) you can enter the following commands:

[number]   read that article (skips to article off menu)
h          This help section
f          forward page
b          back a page
d          delete articles: m, m-n
q          quit
/[RegEx]   Search hits for keyword
![command] start a command (defaults to sh)


 Note that all hits marked for deletion will be deleted from 
the hits file when exiting, unless you specify not to.  
HelpEnd
print "\n<CR> to continue >";
$ans=<STDIN>;
}

sub give_a_clue2{
local ($ans);
system ("clear");
print <<"HelpEnd";
                  RegEx News Help Section - Article Paging Help

After paging an article  you can enter the following commands:

n   next article in order
r   reply to an article
f   followup to an article
s   save the article in your specified path (Dont use ~)
t   trace references to this article

HelpEnd
print "\n<CR> to continue >";
$ans=<STDIN>;
}

sub page_art{
 local($count) = shift @_;
 local(@id) = @_;
  for(;;){
    $ok = &send_request($id[$count]);
    if($ok==1){
      system ("$pager $home/RegEx.tmpfile.$$");
    }else{
      print "Problem with that one..\n$ok"; 
      sleep 3;
      return $count;
    }
    print "\nh for help, or <CR> for main menu > ";
    chop($ans=<STDIN>);
    if($ans =~ /^[Ss]/){
      &save_art($artpath);
      last;
    }elsif($ans =~ /^[Rr]/){
      &reply_art($artpath);
      last;
    }elsif($ans =~ /^[Ff]/){
      &post_art($artpath);
      last;
    }elsif($ans =~ /^[Nn]/){
      $count++;
      next;
    }elsif($ans =~ /^[Hh]/){
      &give_a_clue2;
    }elsif($ans =~ /^[Tt]/){
      open(HEADER,"<$home/RegEx.tmpfile.$$");
      $/ = ""; $headers=<HEADER>; $/ = "\n";
      ($headers=~/References:\s(.*)\r?[\n]{1,1}/) && ($newrefs = $1);
      (print "No References\n" && sleep 1) unless defined($newrefs);
      $newrefs=~s/></> </g;
      @ar=split('[ ,]',$newrefs);
      print "@ar"."\n";
      sleep 1;
      $r=0;
      ($using_dbz) && (open(DBZ,"|$path_to_dbz -x $history > $dbztmp"));
      for(@ar){
        next if defined($arts{$_});
        $r++;
        ($subj,$froup,$hit,$from) = split(/\t/,$arts{$id[$count]});
        $arts{$_} = "$subj\tReferenced Art\t$hit\tzzzzzzzz";
        ($using_dbz) && (print DBZ "$_\n");
        
      }
      ($using_dbz) && (close(DBZ)) && (&get_path_names);
      $count="resort";
      print "Included $r reference(s)\n";sleep 1;last;
    }else{
     last;
    }
  }
$count;
}


sub send_request{
  local($id)=@_;
  local($line,$i);
  local(@page);
  $timeout = 2;
  open(TMP,">$home/RegEx.tmpfile.$$");
  if($using_nntp){
    &chat'print($N,"article $id\r\n");
    while(1){                 #While receiving data
      $line = &chat'expect($N, $timeout, '(.*)\r?\n', '"$1\n"' );
      if($i++==0){
        ($line=~/^2/) && next;
        return $line;
      }
      last if ($line =~ /^\.\s*$/);
      ($line=~/^\./)&&($line=~s/^.//);
      ($line =~ /^430.*sorry/)&&(return $line);
      $line=~s/\r//g;
      print TMP $line;
    }
  }elsif($using_dbz){
    @tmp=split('\t',$arts{$id});
    open(INPUT,"<$tmp[$#tmp]")||(return "Cant open $tmp[$#tmp]\n");
    while(<INPUT>){
      print TMP $_;
    }
  }
    
close(TMP);
1;
}

   
sub bygroup{
local($Asubj,$Afroup,$Ahit,$Afrom) = split(/\t/,$arts{$a});
local($Bsubj,$Bfroup,$Bhit,$Bfrom) = split(/\t/,$arts{$b});
if($Ahit lt $Bhit){
  return -1;
}elsif($Ahit gt $Bhit){
  return 1;
}else{
  $Asubj=~s/^Re: //;
  $Bsubj=~s/^Re: //;
  if($Asubj lt $Bsubj){
    return -1;
  }elsif($Asubj gt $Bsubj){
    return 1;
  }else{
    if($Afrom lt $Bfrom){
      return  -1;
    }elsif($Afrom gt $Bfrom){
      return 1;
    }else{
      return 0;
    }
  }
}
}
sub save_em{
 @hits_done=stat("$hitfile");
 if(($hits_done[10] != $hits_start[10])||(-f "$lockfile")){
  warn "changes not saved, hitfile being updated";return;}
 print "Delete all hits marked for deletion? (y|n) > ";
 $ans=<STDIN>;
 if($ans =~ /^[Yy]/){
 open(LOCK,">$lockfile")||(warn "Cant open lockfile" && return);
 open(HITS,">$hitfile")||(warn "Cant open hitfile" && return);
   foreach $key (keys(%arts)){
     ($j1,$j2,$j3,$j4) = split(/\t/,$arts{$key});
     next if ($j2 eq "Referenced Art");
     print HITS "$key\t$j1\t$j2\t$j3\t$j4\n";
   }
   close LOCK;
   close HITS;
 }
}

sub cleanup {   # broadcast the kill signal and close up shop
&save_em;
&chat'print($N,"quit\r\n");
&chat'close($N) ;
unlink ("$home/RegEx.tmpfile.$$");
unlink ("$home/RegEx.dbz.$$");
unlink ("$lockfile");
exit 0;
}
sub get_path_names{      # this stuff is pretty specific to dbz
 open(TMP,"<$dbztmp")||die "couldnt open $dbztmp\n";
 while(<TMP>){
  chop;
  @ar=split;
  $ar[$#ar]=~s/^/$toplevel_news_dir\//;
  $ar[$#ar]=~s/\./\//g;
  $arts{$ar[0]}.="\t$ar[$#ar]";
}
close TMP;
unlink "$dbztmp";
}

