#!/usr/local/bin/perl # # readnews-m (configured to read from U. Mannheim news server) # # readnews # Usenet newsgroup article header retriever. # Reads a Usenet newsgroup's article headers, via nntp, and displays # a Web page with the results. # Works with the accompanying readmsg script, which reads an # individual article given the newsgroup name and article number. # Usage: # group.name # # or #
# # This CGI script retrieves article headers in a Usenet newsgroup # from the news server via NNTP, and displays them on the Web. It # prints links to read the individual articles with the separate # readmsg-s CGI script. # # If the incoming string exactly matches a newsgroup name, readnews # retrieves the most recent article headers for the group, and prints # links to display older batches of article headers. # # If no newsgroup name exactly matches, readnews searches the list # of newsgroups and returns any group names containing the entered # string(s), up to some maximum number of groups. It prints links to # retrieve the most recent article headers from each matching group. # It lists the matching groups in descending order by the number of # strings matched. # # Extensively revised from Bob K.'s scnnews script for Seattle Community # Network. Some of G. Heil's scnnews revisions may still be here. # ($these $AND_THESE are unchanged, $TheseVariables are my changes) # # Rod Clark\n";
print "$TooManyMatchesPrompt\n";
}
&PrintPageFooter;
}
sub PrintMatchingGroupsExact
{
foreach $FoundLine (@FoundLinesExact)
{
($ThisGroup, $ThisNumberOfMessages) = split (/ /, $FoundLine);
print "$ThisGroup $ThisNumberOfMessages
\n";
}
}
sub PrintMatchingGroupsAndOr
{
if ($NumberOfFoundLinesAnd)
{
for ($Index = 0; (($Index < $NumberOfFoundLinesAnd) && ($Index < $MaxMatchingGroups)); $Index++)
{
$FoundLine = $FoundLinesAnd[$Index];
($ThisGroup, $ThisNumberOfMessages) = split (/ /, $FoundLine);
print "$ThisGroup $ThisNumberOfMessages
\n";
$MatchesPrinted++;
}
if ($NumberOfFoundLinesOr)
{
print "
\n";
}
}
for ($Index = 0; (($Index < $NumberOfFoundLinesOr) && ($MatchesPrinted < $MaxMatchingGroups)); $Index++)
{
$FoundLine = $FoundLinesOr[$Index];
($ThisGroup, $ThisNumberOfMessages) = split (/ /, $FoundLine);
print "$ThisGroup $ThisNumberOfMessages
\n";
$MatchesPrinted++;
}
}
sub GetArticleHeaders
{
$GroupStatus = &CheckGroupStatus;
if ($GroupStatus == 411)
{
$ErrorMessage = "$newsgroup does not exist on the news server";
&PrintErrorFooter;
exit;
}
elsif ($GroupStatus == 211)
{
# if group is empty
if ($gmany == 0)
{
$ErrorMessage = "$newsgroup: no articles found";
&PrintErrorFooter;
exit;
}
if (!$CurrentBatch)
{
$CurrentBatch = 1;
}
&ComputeNumberOfBatches;
$CurrentArticleNum = &ComputeStartAndEndArticles;
$ArticleStatus = &CheckArticleStatus;
if ($ArticleStatus != 223)
{
&FindNextValidArticle;
}
&ReadArticleHeaders;
&PrintPageFooter;
}
}
sub FindNextValidArticle
{
while (($ArticleStatus != 223) && (!$EndOfArticles))
{
$ArticleStatus = &CheckNextArticleStatus;
}
}
sub ComputeNumberOfBatches
{
$NumberOfBatches = int ($gmany / $MaxArticlesPerBatch);
if (($gmany % $MaxArticlesPerBatch) > 0)
{
$NumberOfBatches++;
}
if ($NumberOfBatches > $MaxNumberOfBatches)
{
$NumberOfBatches = $MaxNumberOfBatches;
if (($glast - $gfirst) > ($MaxNumberOfBatches * $MaxArticlesPerBatch))
{
$gfirst = $glast - ($MaxNumberOfBatches * $MaxArticlesPerBatch);
}
}
}
sub ComputeStartAndEndArticles
{
# if total articles are one screenful or less
if ($gmany < $MaxArticlesPerBatch)
{
$EndArticleNum = $glast;
$StartArticleNum = $gfirst;
}
else # total articles are more than one screenful
{
$EndArticleNum = $glast - ($MaxArticlesPerBatch * ($CurrentBatch - 1));
# if current batch is partial (earliest) screenful of articles
if (($EndArticleNum - $gfirst) < $MaxArticlesPerBatch)
{
$StartArticleNum = $gfirst;
}
else # current batch is a full screenful of articles
{
$StartArticleNum = ($EndArticleNum - $MaxArticlesPerBatch) + 1;
}
}
return ($StartArticleNum);
}
sub ReadArticleHeaders
{
$EndOfArticles = 0;
while (($CurrentArticleNum <= $EndArticleNum) && (!$EndOfArticles))
{
if ($ArticleStatus == 223) # article OK
{
$HeadStatus = &CheckHeadStatus;
if ($HeadStatus == 221)
{
&BuildArticleHeaderLine;
}
}
$ArticleStatus = &CheckNextArticleStatus;
}
&PrintBatchOfHeaders;
&PrintBatchPromptLine;
}
sub BuildArticleHeaderLine
{
# read first line of text
$_ = ;
# loop until encounter lone period at start of line
# (.^J ends text in NNTP)
$date = $from = $subject = "";
while (!/^\.[^\.].*$/)
{
# remove trailing CR LF (^M ^J)
chop;
chop;
# get article header
$line = $_."\n";
# restore line feed LF (^J) to end of line
if ($line =~ /^Subject: (.*)$/)
{
$subject = $1;
$subject = &EntifyVariable ($subject);
}
elsif ($line =~ /^From: .*(\(.*\)).*$/)
{
$from = $1;
$from = &EntifyVariable ($from);
}
elsif ($line =~ /^Date: \w+, (\d+) (\w+).*$/)
{
$date = "$2 $1";
$date = &EntifyVariable ($date);
}
# read next line of header
$_ = ;
}
# build article header line to display
if (!$date)
{
$date = "___ __";
}
$ArticleHeaderLine = "\".$date;
# if "Mth N" instead of "Mth NN"
if (length ($date) < $DateLength)
{
$ArticleHeaderLine = $ArticleHeaderLine."_ ";
}
else
{
$ArticleHeaderLine = $ArticleHeaderLine." ";
}
$ArticleHeaderLine = $ArticleHeaderLine."";
$ArticleHeaderLine = $ArticleHeaderLine."$subject $from
\n";
push (@BatchHeaderLines, $ArticleHeaderLine);
}
sub PrintBatchOfHeaders
{
foreach $BatchHeaderLine (@BatchHeaderLines)
{
print "$BatchHeaderLine";
}
}
sub EntifyVariable
{
local ($ToEntify) = @_;
$ToEntify =~ s/</g;
$ToEntify =~ s/>/>/g;
$ToEntify =~ s/"/"/g;
return ($ToEntify);
}
#----------------------------------------------------------------
# send NNTP commands to news server
#----------------------------------------------------------------
sub CheckInitialServerStatus
{
# read server status
$_ = ;
($status, $rest) = split (/ /, $_, 2);
if ($status == 200 || $status == 201)
{
# 200 server ready - posting allowed
# 201 server ready - no posting allowed
# otherwise there has been a problem
if ($debug)
{
if ($status == 200)
{
print "DEBUG: status = $status (posting allowed)\n";
}
elsif ($status == 201)
{
print "DEBUG: status = $status (no posting)\n";
}
}
}
return ($status);
}
sub CheckListStatus
{
print S "LIST\n";
# read reply
$_ = ;
($status, $rest) = split (/ /, $_, 2);
return ($status);
}
sub CheckGroupStatus
{
print S "GROUP $newsgroup\n";
# read reply
$_ = ;
($status, $gmany, $gfirst, $glast, $gname) = split (/ /, $_, 5);
return ($status);
}
sub CheckArticleStatus
{
print S "STAT $CurrentArticleNum\n";
# read reply
$_ = ;
($status, $ArticleNum, $ArticleID, $rest) = split (/ /, $_, 4);
return ($status);
}
sub CheckHeadStatus
{
print S "HEAD\n";
# read reply
$_ = ;
($status, $ArticleNum, $ArticleID, $rest) = split (/ /, $_, 4);
return ($status);
}
sub CheckNextArticleStatus
{
# send next article command to NNTP server
print S "NEXT\n";
# retrieve reply
$_ = ;
($status, $ArticleNum, $ArticleID, $rest) = split (/ /, $_, 4);
if ($status == 223) # article OK
{
$CurrentArticleNum = $ArticleNum;
}
elsif (($status == 420) || ($status == 423) || ($status == 430))
{
# at the moment, don't do anything with these
}
elsif ($status == 421) # no next article in group
{
$EndOfArticles = 1;
}
else # unexpected response
{
$EndOfArticles = 1;
}
return ($status);
}
sub QuitNewsServer
{
print S "QUIT\n";
$_ = ;
($status, $StatusMessage) = split (/ /, $_, 2);
if ($status != 205)
{
$ErrorMessage = "news server status=$status: $StatusMessage\n";
&PrintErrorFooter;
exit 1;
}
return ($status);
}
#----------------------------------------------------------------
# print Web pages
#----------------------------------------------------------------
sub PrintPageTop
{
print <$newsgroup
ENDPRINT
}
sub PrintErrorPageTop
{
# call this only to precede error output before $newsgroup is defined
local ($PrintErrorPageTopTitle) = @_;
print <$PrintErrorPageTopTitle
ENDPRINT
}
sub PrintBatchPromptLine
{
if ($NumberOfBatches > 1)
{
print "
\n";
print "