You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1060 lines
25 KiB
1060 lines
25 KiB
# Net::NNTP.pm
|
|
#
|
|
# Copyright (c) 1995-1997 Graham Barr <[email protected]>. All rights reserved.
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the same terms as Perl itself.
|
|
|
|
package Net::NNTP;
|
|
|
|
use strict;
|
|
use vars qw(@ISA $VERSION $debug);
|
|
use IO::Socket;
|
|
use Net::Cmd;
|
|
use Carp;
|
|
use Time::Local;
|
|
use Net::Config;
|
|
|
|
$VERSION = "2.19"; # $Id: //depot/libnet/Net/NNTP.pm#8$
|
|
@ISA = qw(Net::Cmd IO::Socket::INET);
|
|
|
|
sub new
|
|
{
|
|
my $self = shift;
|
|
my $type = ref($self) || $self;
|
|
my $host = shift if @_ % 2;
|
|
my %arg = @_;
|
|
my $obj;
|
|
|
|
$host ||= $ENV{NNTPSERVER} || $ENV{NEWSHOST};
|
|
|
|
my $hosts = defined $host ? [ $host ] : $NetConfig{nntp_hosts};
|
|
|
|
@{$hosts} = qw(news)
|
|
unless @{$hosts};
|
|
|
|
my $h;
|
|
foreach $h (@{$hosts})
|
|
{
|
|
$obj = $type->SUPER::new(PeerAddr => ($host = $h),
|
|
PeerPort => $arg{Port} || 'nntp(119)',
|
|
Proto => 'tcp',
|
|
Timeout => defined $arg{Timeout}
|
|
? $arg{Timeout}
|
|
: 120
|
|
) and last;
|
|
}
|
|
|
|
return undef
|
|
unless defined $obj;
|
|
|
|
${*$obj}{'net_nntp_host'} = $host;
|
|
|
|
$obj->autoflush(1);
|
|
$obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
|
|
|
|
unless ($obj->response() == CMD_OK)
|
|
{
|
|
$obj->close;
|
|
return undef;
|
|
}
|
|
|
|
my $c = $obj->code;
|
|
my @m = $obj->message;
|
|
|
|
unless(exists $arg{Reader} && $arg{Reader} == 0) {
|
|
# if server is INN and we have transfer rights the we are currently
|
|
# talking to innd not nnrpd
|
|
if($obj->reader)
|
|
{
|
|
# If reader suceeds the we need to consider this code to determine postok
|
|
$c = $obj->code;
|
|
}
|
|
else
|
|
{
|
|
# I want to ignore this failure, so restore the previous status.
|
|
$obj->set_status($c,\@m);
|
|
}
|
|
}
|
|
|
|
${*$obj}{'net_nntp_post'} = $c == 200 ? 1 : 0;
|
|
|
|
$obj;
|
|
}
|
|
|
|
sub debug_text
|
|
{
|
|
my $nntp = shift;
|
|
my $inout = shift;
|
|
my $text = shift;
|
|
|
|
if(($nntp->code == 350 && $text =~ /^(\S+)/)
|
|
|| ($text =~ /^(authinfo\s+pass)/io))
|
|
{
|
|
$text = "$1 ....\n"
|
|
}
|
|
|
|
$text;
|
|
}
|
|
|
|
sub postok
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->postok()';
|
|
my $nntp = shift;
|
|
${*$nntp}{'net_nntp_post'} || 0;
|
|
}
|
|
|
|
sub article
|
|
{
|
|
@_ >= 1 && @_ <= 3 or croak 'usage: $nntp->article( [ MSGID ], [ FH ] )';
|
|
my $nntp = shift;
|
|
my @fh;
|
|
|
|
@fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
|
|
|
|
$nntp->_ARTICLE(@_)
|
|
? $nntp->read_until_dot(@fh)
|
|
: undef;
|
|
}
|
|
|
|
sub authinfo
|
|
{
|
|
@_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
|
|
my($nntp,$user,$pass) = @_;
|
|
|
|
$nntp->_AUTHINFO("USER",$user) == CMD_MORE
|
|
&& $nntp->_AUTHINFO("PASS",$pass) == CMD_OK;
|
|
}
|
|
|
|
sub authinfo_simple
|
|
{
|
|
@_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
|
|
my($nntp,$user,$pass) = @_;
|
|
|
|
$nntp->_AUTHINFO('SIMPLE') == CMD_MORE
|
|
&& $nntp->command($user,$pass)->response == CMD_OK;
|
|
}
|
|
|
|
sub body
|
|
{
|
|
@_ >= 1 && @_ <= 3 or croak 'usage: $nntp->body( [ MSGID ], [ FH ] )';
|
|
my $nntp = shift;
|
|
my @fh;
|
|
|
|
@fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
|
|
|
|
$nntp->_BODY(@_)
|
|
? $nntp->read_until_dot(@fh)
|
|
: undef;
|
|
}
|
|
|
|
sub head
|
|
{
|
|
@_ >= 1 && @_ <= 3 or croak 'usage: $nntp->head( [ MSGID ], [ FH ] )';
|
|
my $nntp = shift;
|
|
my @fh;
|
|
|
|
@fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
|
|
|
|
$nntp->_HEAD(@_)
|
|
? $nntp->read_until_dot(@fh)
|
|
: undef;
|
|
}
|
|
|
|
sub nntpstat
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->nntpstat( [ MSGID ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_STAT(@_) && $nntp->message =~ /(<[^>]+>)/o
|
|
? $1
|
|
: undef;
|
|
}
|
|
|
|
|
|
sub group
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->group( [ GROUP ] )';
|
|
my $nntp = shift;
|
|
my $grp = ${*$nntp}{'net_nntp_group'} || undef;
|
|
|
|
return $grp
|
|
unless(@_ || wantarray);
|
|
|
|
my $newgrp = shift;
|
|
|
|
return wantarray ? () : undef
|
|
unless $nntp->_GROUP($newgrp || $grp || "")
|
|
&& $nntp->message =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\S+)/;
|
|
|
|
my($count,$first,$last,$group) = ($1,$2,$3,$4);
|
|
|
|
# group may be replied as '(current group)'
|
|
$group = ${*$nntp}{'net_nntp_group'}
|
|
if $group =~ /\(/;
|
|
|
|
${*$nntp}{'net_nntp_group'} = $group;
|
|
|
|
wantarray
|
|
? ($count,$first,$last,$group)
|
|
: $group;
|
|
}
|
|
|
|
sub help
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->help()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_HELP
|
|
? $nntp->read_until_dot
|
|
: undef;
|
|
}
|
|
|
|
sub ihave
|
|
{
|
|
@_ >= 2 or croak 'usage: $nntp->ihave( MESSAGE-ID [, MESSAGE ])';
|
|
my $nntp = shift;
|
|
my $mid = shift;
|
|
|
|
$nntp->_IHAVE($mid) && $nntp->datasend(@_)
|
|
? @_ == 0 || $nntp->dataend
|
|
: undef;
|
|
}
|
|
|
|
sub last
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->last()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LAST && $nntp->message =~ /(<[^>]+>)/o
|
|
? $1
|
|
: undef;
|
|
}
|
|
|
|
sub list
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->list()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST
|
|
? $nntp->_grouplist
|
|
: undef;
|
|
}
|
|
|
|
sub newgroups
|
|
{
|
|
@_ >= 2 or croak 'usage: $nntp->newgroups( SINCE [, DISTRIBUTIONS ])';
|
|
my $nntp = shift;
|
|
my $time = _timestr(shift);
|
|
my $dist = shift || "";
|
|
|
|
$dist = join(",", @{$dist})
|
|
if ref($dist);
|
|
|
|
$nntp->_NEWGROUPS($time,$dist)
|
|
? $nntp->_grouplist
|
|
: undef;
|
|
}
|
|
|
|
sub newnews
|
|
{
|
|
@_ >= 2 && @_ <= 4 or
|
|
croak 'usage: $nntp->newnews( SINCE [, GROUPS [, DISTRIBUTIONS ]])';
|
|
my $nntp = shift;
|
|
my $time = _timestr(shift);
|
|
my $grp = @_ ? shift : $nntp->group;
|
|
my $dist = shift || "";
|
|
|
|
$grp ||= "*";
|
|
$grp = join(",", @{$grp})
|
|
if ref($grp);
|
|
|
|
$dist = join(",", @{$dist})
|
|
if ref($dist);
|
|
|
|
$nntp->_NEWNEWS($grp,$time,$dist)
|
|
? $nntp->_articlelist
|
|
: undef;
|
|
}
|
|
|
|
sub next
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->next()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_NEXT && $nntp->message =~ /(<[^>]+>)/o
|
|
? $1
|
|
: undef;
|
|
}
|
|
|
|
sub post
|
|
{
|
|
@_ >= 1 or croak 'usage: $nntp->post( [ MESSAGE ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_POST() && $nntp->datasend(@_)
|
|
? @_ == 0 || $nntp->dataend
|
|
: undef;
|
|
}
|
|
|
|
sub quit
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->quit()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_QUIT;
|
|
$nntp->close;
|
|
}
|
|
|
|
sub slave
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->slave()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_SLAVE;
|
|
}
|
|
|
|
##
|
|
## The following methods are not implemented by all servers
|
|
##
|
|
|
|
sub active
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->active( [ PATTERN ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('ACTIVE',@_)
|
|
? $nntp->_grouplist
|
|
: undef;
|
|
}
|
|
|
|
sub active_times
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->active_times()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('ACTIVE.TIMES')
|
|
? $nntp->_grouplist
|
|
: undef;
|
|
}
|
|
|
|
sub distributions
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->distributions()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('DISTRIBUTIONS')
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub distribution_patterns
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->distributions()';
|
|
my $nntp = shift;
|
|
|
|
my $arr;
|
|
local $_;
|
|
|
|
$nntp->_LIST('DISTRIB.PATS') && ($arr = $nntp->read_until_dot)
|
|
? [grep { /^\d/ && (chomp, $_ = [ split /:/ ]) } @$arr]
|
|
: undef;
|
|
}
|
|
|
|
sub newsgroups
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->newsgroups( [ PATTERN ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('NEWSGROUPS',@_)
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub overview_fmt
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->overview_fmt()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('OVERVIEW.FMT')
|
|
? $nntp->_articlelist
|
|
: undef;
|
|
}
|
|
|
|
sub subscriptions
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->subscriptions()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LIST('SUBSCRIPTIONS')
|
|
? $nntp->_articlelist
|
|
: undef;
|
|
}
|
|
|
|
sub listgroup
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->listgroup( [ GROUP ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_LISTGROUP(@_)
|
|
? $nntp->_articlelist
|
|
: undef;
|
|
}
|
|
|
|
sub reader
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->reader()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_MODE('READER');
|
|
}
|
|
|
|
sub xgtitle
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $nntp->xgtitle( [ PATTERN ] )';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_XGTITLE(@_)
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub xhdr
|
|
{
|
|
@_ >= 2 && @_ <= 4 or croak 'usage: $nntp->xhdr( HEADER, [ MESSAGE-SPEC ] )';
|
|
my $nntp = shift;
|
|
my $hdr = shift;
|
|
my $arg = _msg_arg(@_);
|
|
|
|
$nntp->_XHDR($hdr, $arg)
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub xover
|
|
{
|
|
@_ == 2 || @_ == 3 or croak 'usage: $nntp->xover( MESSAGE-SPEC )';
|
|
my $nntp = shift;
|
|
my $arg = _msg_arg(@_);
|
|
|
|
$nntp->_XOVER($arg)
|
|
? $nntp->_fieldlist
|
|
: undef;
|
|
}
|
|
|
|
sub xpat
|
|
{
|
|
@_ == 4 || @_ == 5 or croak '$nntp->xpat( HEADER, PATTERN, MESSAGE-SPEC )';
|
|
my $nntp = shift;
|
|
my $hdr = shift;
|
|
my $pat = shift;
|
|
my $arg = _msg_arg(@_);
|
|
|
|
$pat = join(" ", @$pat)
|
|
if ref($pat);
|
|
|
|
$nntp->_XPAT($hdr,$arg,$pat)
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub xpath
|
|
{
|
|
@_ == 2 or croak 'usage: $nntp->xpath( MESSAGE-ID )';
|
|
my($nntp,$mid) = @_;
|
|
|
|
return undef
|
|
unless $nntp->_XPATH($mid);
|
|
|
|
my $m; ($m = $nntp->message) =~ s/^\d+\s+//o;
|
|
my @p = split /\s+/, $m;
|
|
|
|
wantarray ? @p : $p[0];
|
|
}
|
|
|
|
sub xrover
|
|
{
|
|
@_ == 2 || @_ == 3 or croak 'usage: $nntp->xrover( MESSAGE-SPEC )';
|
|
my $nntp = shift;
|
|
my $arg = _msg_arg(@_);
|
|
|
|
$nntp->_XROVER($arg)
|
|
? $nntp->_description
|
|
: undef;
|
|
}
|
|
|
|
sub date
|
|
{
|
|
@_ == 1 or croak 'usage: $nntp->date()';
|
|
my $nntp = shift;
|
|
|
|
$nntp->_DATE && $nntp->message =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
|
|
? timegm($6,$5,$4,$3,$2-1,$1 - 1900)
|
|
: undef;
|
|
}
|
|
|
|
|
|
##
|
|
## Private subroutines
|
|
##
|
|
|
|
sub _msg_arg
|
|
{
|
|
my $spec = shift;
|
|
my $arg = "";
|
|
|
|
if(@_)
|
|
{
|
|
carp "Depriciated passing of two message numbers, "
|
|
. "pass a reference"
|
|
if $^W;
|
|
$spec = [ $spec, $_[0] ];
|
|
}
|
|
|
|
if(defined $spec)
|
|
{
|
|
if(ref($spec))
|
|
{
|
|
$arg = $spec->[0] . "-";
|
|
$arg .= $spec->[1]
|
|
if defined $spec->[1] && $spec->[1] > $spec->[0];
|
|
}
|
|
else
|
|
{
|
|
$arg = $spec;
|
|
}
|
|
}
|
|
|
|
$arg;
|
|
}
|
|
|
|
sub _timestr
|
|
{
|
|
my $time = shift;
|
|
my @g = reverse((gmtime($time))[0..5]);
|
|
$g[1] += 1;
|
|
$g[0] %= 100;
|
|
sprintf "%02d%02d%02d %02d%02d%02d GMT", @g;
|
|
}
|
|
|
|
sub _grouplist
|
|
{
|
|
my $nntp = shift;
|
|
my $arr = $nntp->read_until_dot or
|
|
return undef;
|
|
|
|
my $hash = {};
|
|
my $ln;
|
|
|
|
foreach $ln (@$arr)
|
|
{
|
|
my @a = split(/[\s\n]+/,$ln);
|
|
$hash->{$a[0]} = [ @a[1,2,3] ];
|
|
}
|
|
|
|
$hash;
|
|
}
|
|
|
|
sub _fieldlist
|
|
{
|
|
my $nntp = shift;
|
|
my $arr = $nntp->read_until_dot or
|
|
return undef;
|
|
|
|
my $hash = {};
|
|
my $ln;
|
|
|
|
foreach $ln (@$arr)
|
|
{
|
|
my @a = split(/[\t\n]/,$ln);
|
|
my $m = shift @a;
|
|
$hash->{$m} = [ @a ];
|
|
}
|
|
|
|
$hash;
|
|
}
|
|
|
|
sub _articlelist
|
|
{
|
|
my $nntp = shift;
|
|
my $arr = $nntp->read_until_dot;
|
|
|
|
chomp(@$arr)
|
|
if $arr;
|
|
|
|
$arr;
|
|
}
|
|
|
|
sub _description
|
|
{
|
|
my $nntp = shift;
|
|
my $arr = $nntp->read_until_dot or
|
|
return undef;
|
|
|
|
my $hash = {};
|
|
my $ln;
|
|
|
|
foreach $ln (@$arr)
|
|
{
|
|
chomp($ln);
|
|
|
|
$hash->{$1} = $ln
|
|
if $ln =~ s/^\s*(\S+)\s*//o;
|
|
}
|
|
|
|
$hash;
|
|
|
|
}
|
|
|
|
##
|
|
## The commands
|
|
##
|
|
|
|
sub _ARTICLE { shift->command('ARTICLE',@_)->response == CMD_OK }
|
|
sub _AUTHINFO { shift->command('AUTHINFO',@_)->response }
|
|
sub _BODY { shift->command('BODY',@_)->response == CMD_OK }
|
|
sub _DATE { shift->command('DATE')->response == CMD_INFO }
|
|
sub _GROUP { shift->command('GROUP',@_)->response == CMD_OK }
|
|
sub _HEAD { shift->command('HEAD',@_)->response == CMD_OK }
|
|
sub _HELP { shift->command('HELP',@_)->response == CMD_INFO }
|
|
sub _IHAVE { shift->command('IHAVE',@_)->response == CMD_MORE }
|
|
sub _LAST { shift->command('LAST')->response == CMD_OK }
|
|
sub _LIST { shift->command('LIST',@_)->response == CMD_OK }
|
|
sub _LISTGROUP { shift->command('LISTGROUP',@_)->response == CMD_OK }
|
|
sub _NEWGROUPS { shift->command('NEWGROUPS',@_)->response == CMD_OK }
|
|
sub _NEWNEWS { shift->command('NEWNEWS',@_)->response == CMD_OK }
|
|
sub _NEXT { shift->command('NEXT')->response == CMD_OK }
|
|
sub _POST { shift->command('POST',@_)->response == CMD_MORE }
|
|
sub _QUIT { shift->command('QUIT',@_)->response == CMD_OK }
|
|
sub _SLAVE { shift->command('SLAVE',@_)->response == CMD_OK }
|
|
sub _STAT { shift->command('STAT',@_)->response == CMD_OK }
|
|
sub _MODE { shift->command('MODE',@_)->response == CMD_OK }
|
|
sub _XGTITLE { shift->command('XGTITLE',@_)->response == CMD_OK }
|
|
sub _XHDR { shift->command('XHDR',@_)->response == CMD_OK }
|
|
sub _XPAT { shift->command('XPAT',@_)->response == CMD_OK }
|
|
sub _XPATH { shift->command('XPATH',@_)->response == CMD_OK }
|
|
sub _XOVER { shift->command('XOVER',@_)->response == CMD_OK }
|
|
sub _XROVER { shift->command('XROVER',@_)->response == CMD_OK }
|
|
sub _XTHREAD { shift->unsupported }
|
|
sub _XSEARCH { shift->unsupported }
|
|
sub _XINDEX { shift->unsupported }
|
|
|
|
##
|
|
## IO/perl methods
|
|
##
|
|
|
|
sub DESTROY
|
|
{
|
|
my $nntp = shift;
|
|
defined(fileno($nntp)) && $nntp->quit
|
|
}
|
|
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Net::NNTP - NNTP Client class
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Net::NNTP;
|
|
|
|
$nntp = Net::NNTP->new("some.host.name");
|
|
$nntp->quit;
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
C<Net::NNTP> is a class implementing a simple NNTP client in Perl as described
|
|
in RFC977. C<Net::NNTP> inherits its communication methods from C<Net::Cmd>
|
|
|
|
=head1 CONSTRUCTOR
|
|
|
|
=over 4
|
|
|
|
=item new ( [ HOST ] [, OPTIONS ])
|
|
|
|
This is the constructor for a new Net::NNTP object. C<HOST> is the
|
|
name of the remote host to which a NNTP connection is required. If not
|
|
given two environment variables are checked, first C<NNTPSERVER> then
|
|
C<NEWSHOST>, then C<Net::Config> is checked, and if a host is not found
|
|
then C<news> is used.
|
|
|
|
C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
|
|
Possible options are:
|
|
|
|
B<Timeout> - Maximum time, in seconds, to wait for a response from the
|
|
NNTP server, a value of zero will cause all IO operations to block.
|
|
(default: 120)
|
|
|
|
B<Debug> - Enable the printing of debugging information to STDERR
|
|
|
|
B<Reader> - If the remote server is INN then initially the connection
|
|
will be to nnrpd, by default C<Net::NNTP> will issue a C<MODE READER> command
|
|
so that the remote server becomes innd. If the C<Reader> option is given
|
|
with a value of zero, then this command will not be sent and the
|
|
connection will be left talking to nnrpd.
|
|
|
|
=back
|
|
|
|
=head1 METHODS
|
|
|
|
Unless otherwise stated all methods return either a I<true> or I<false>
|
|
value, with I<true> meaning that the operation was a success. When a method
|
|
states that it returns a value, failure will be returned as I<undef> or an
|
|
empty list.
|
|
|
|
=over 4
|
|
|
|
=item article ( [ MSGID|MSGNUM ], [FH] )
|
|
|
|
Retrieve the header, a blank line, then the body (text) of the
|
|
specified article.
|
|
|
|
If C<FH> is specified then it is expected to be a valid filehandle
|
|
and the result will be printed to it, on sucess a true value will be
|
|
returned. If C<FH> is not specified then the return value, on sucess,
|
|
will be a reference to an array containg the article requested, each
|
|
entry in the array will contain one line of the article.
|
|
|
|
If no arguments are passed then the current article in the currently
|
|
selected newsgroup is fetched.
|
|
|
|
C<MSGNUM> is a numeric id of an article in the current newsgroup, and
|
|
will change the current article pointer. C<MSGID> is the message id of
|
|
an article as shown in that article's header. It is anticipated that the
|
|
client will obtain the C<MSGID> from a list provided by the C<newnews>
|
|
command, from references contained within another article, or from the
|
|
message-id provided in the response to some other commands.
|
|
|
|
If there is an error then C<undef> will be returned.
|
|
|
|
=item body ( [ MSGID|MSGNUM ], [FH] )
|
|
|
|
Like C<article> but only fetches the body of the article.
|
|
|
|
=item head ( [ MSGID|MSGNUM ], [FH] )
|
|
|
|
Like C<article> but only fetches the headers for the article.
|
|
|
|
=item nntpstat ( [ MSGID|MSGNUM ] )
|
|
|
|
The C<nntpstat> command is similar to the C<article> command except that no
|
|
text is returned. When selecting by message number within a group,
|
|
the C<nntpstat> command serves to set the "current article pointer" without
|
|
sending text.
|
|
|
|
Using the C<nntpstat> command to
|
|
select by message-id is valid but of questionable value, since a
|
|
selection by message-id does B<not> alter the "current article pointer".
|
|
|
|
Returns the message-id of the "current article".
|
|
|
|
=item group ( [ GROUP ] )
|
|
|
|
Set and/or get the current group. If C<GROUP> is not given then information
|
|
is returned on the current group.
|
|
|
|
In a scalar context it returns the group name.
|
|
|
|
In an array context the return value is a list containing, the number
|
|
of articles in the group, the number of the first article, the number
|
|
of the last article and the group name.
|
|
|
|
=item ihave ( MSGID [, MESSAGE ])
|
|
|
|
The C<ihave> command informs the server that the client has an article
|
|
whose id is C<MSGID>. If the server desires a copy of that
|
|
article, and C<MESSAGE> has been given the it will be sent.
|
|
|
|
Returns I<true> if the server desires the article and C<MESSAGE> was
|
|
successfully sent,if specified.
|
|
|
|
If C<MESSAGE> is not specified then the message must be sent using the
|
|
C<datasend> and C<dataend> methods from L<Net::Cmd>
|
|
|
|
C<MESSAGE> can be either an array of lines or a reference to an array.
|
|
|
|
=item last ()
|
|
|
|
Set the "current article pointer" to the previous article in the current
|
|
newsgroup.
|
|
|
|
Returns the message-id of the article.
|
|
|
|
=item date ()
|
|
|
|
Returns the date on the remote server. This date will be in a UNIX time
|
|
format (seconds since 1970)
|
|
|
|
=item postok ()
|
|
|
|
C<postok> will return I<true> if the servers initial response indicated
|
|
that it will allow posting.
|
|
|
|
=item authinfo ( USER, PASS )
|
|
|
|
=item list ()
|
|
|
|
Obtain information about all the active newsgroups. The results is a reference
|
|
to a hash where the key is a group name and each value is a reference to an
|
|
array. The elements in this array are:- the first article number in the group,
|
|
the last article number in the group and any information flags about the group.
|
|
|
|
=item newgroups ( SINCE [, DISTRIBUTIONS ])
|
|
|
|
C<SINCE> is a time value and C<DISTRIBUTIONS> is either a distribution
|
|
pattern or a reference to a list of distribution patterns.
|
|
The result is the same as C<list>, but the
|
|
groups return will be limited to those created after C<SINCE> and, if
|
|
specified, in one of the distribution areas in C<DISTRIBUTIONS>.
|
|
|
|
=item newnews ( SINCE [, GROUPS [, DISTRIBUTIONS ]])
|
|
|
|
C<SINCE> is a time value. C<GROUPS> is either a group pattern or a reference
|
|
to a list of group patterns. C<DISTRIBUTIONS> is either a distribution
|
|
pattern or a reference to a list of distribution patterns.
|
|
|
|
Returns a reference to a list which contains the message-ids of all news posted
|
|
after C<SINCE>, that are in a groups which matched C<GROUPS> and a
|
|
distribution which matches C<DISTRIBUTIONS>.
|
|
|
|
=item next ()
|
|
|
|
Set the "current article pointer" to the next article in the current
|
|
newsgroup.
|
|
|
|
Returns the message-id of the article.
|
|
|
|
=item post ( [ MESSAGE ] )
|
|
|
|
Post a new article to the news server. If C<MESSAGE> is specified and posting
|
|
is allowed then the message will be sent.
|
|
|
|
If C<MESSAGE> is not specified then the message must be sent using the
|
|
C<datasend> and C<dataend> methods from L<Net::Cmd>
|
|
|
|
C<MESSAGE> can be either an array of lines or a reference to an array.
|
|
|
|
=item slave ()
|
|
|
|
Tell the remote server that I am not a user client, but probably another
|
|
news server.
|
|
|
|
=item quit ()
|
|
|
|
Quit the remote server and close the socket connection.
|
|
|
|
=back
|
|
|
|
=head2 Extension methods
|
|
|
|
These methods use commands that are not part of the RFC977 documentation. Some
|
|
servers may not support all of them.
|
|
|
|
=over 4
|
|
|
|
=item newsgroups ( [ PATTERN ] )
|
|
|
|
Returns a reference to a hash where the keys are all the group names which
|
|
match C<PATTERN>, or all of the groups if no pattern is specified, and
|
|
each value contains the description text for the group.
|
|
|
|
=item distributions ()
|
|
|
|
Returns a reference to a hash where the keys are all the possible
|
|
distribution names and the values are the distribution descriptions.
|
|
|
|
=item subscriptions ()
|
|
|
|
Returns a reference to a list which contains a list of groups which
|
|
are recommended for a new user to subscribe to.
|
|
|
|
=item overview_fmt ()
|
|
|
|
Returns a reference to an array which contain the names of the fields returned
|
|
by C<xover>.
|
|
|
|
=item active_times ()
|
|
|
|
Returns a reference to a hash where the keys are the group names and each
|
|
value is a reference to an array containing the time the groups was created
|
|
and an identifier, possibly an Email address, of the creator.
|
|
|
|
=item active ( [ PATTERN ] )
|
|
|
|
Similar to C<list> but only active groups that match the pattern are returned.
|
|
C<PATTERN> can be a group pattern.
|
|
|
|
=item xgtitle ( PATTERN )
|
|
|
|
Returns a reference to a hash where the keys are all the group names which
|
|
match C<PATTERN> and each value is the description text for the group.
|
|
|
|
=item xhdr ( HEADER, MESSAGE-SPEC )
|
|
|
|
Obtain the header field C<HEADER> for all the messages specified.
|
|
|
|
The return value will be a reference
|
|
to a hash where the keys are the message numbers and each value contains
|
|
the text of the requested header for that message.
|
|
|
|
=item xover ( MESSAGE-SPEC )
|
|
|
|
The return value will be a reference
|
|
to a hash where the keys are the message numbers and each value contains
|
|
a reference to an array which contains the overview fields for that
|
|
message.
|
|
|
|
The names of the fields can be obtained by calling C<overview_fmt>.
|
|
|
|
=item xpath ( MESSAGE-ID )
|
|
|
|
Returns the path name to the file on the server which contains the specified
|
|
message.
|
|
|
|
=item xpat ( HEADER, PATTERN, MESSAGE-SPEC)
|
|
|
|
The result is the same as C<xhdr> except the is will be restricted to
|
|
headers where the text of the header matches C<PATTERN>
|
|
|
|
=item xrover
|
|
|
|
The XROVER command returns reference information for the article(s)
|
|
specified.
|
|
|
|
Returns a reference to a HASH where the keys are the message numbers and the
|
|
values are the References: lines from the articles
|
|
|
|
=item listgroup ( [ GROUP ] )
|
|
|
|
Returns a reference to a list of all the active messages in C<GROUP>, or
|
|
the current group if C<GROUP> is not specified.
|
|
|
|
=item reader
|
|
|
|
Tell the server that you are a reader and not another server.
|
|
|
|
This is required by some servers. For example if you are connecting to
|
|
an INN server and you have transfer permission your connection will
|
|
be connected to the transfer daemon, not the NNTP daemon. Issuing
|
|
this command will cause the transfer daemon to hand over control
|
|
to the NNTP daemon.
|
|
|
|
Some servers do not understand this command, but issuing it and ignoring
|
|
the response is harmless.
|
|
|
|
=back
|
|
|
|
=head1 UNSUPPORTED
|
|
|
|
The following NNTP command are unsupported by the package, and there are
|
|
no plans to do so.
|
|
|
|
AUTHINFO GENERIC
|
|
XTHREAD
|
|
XSEARCH
|
|
XINDEX
|
|
|
|
=head1 DEFINITIONS
|
|
|
|
=over 4
|
|
|
|
=item MESSAGE-SPEC
|
|
|
|
C<MESSAGE-SPEC> is either a single message-id, a single message number, or
|
|
a reference to a list of two message numbers.
|
|
|
|
If C<MESSAGE-SPEC> is a reference to a list of two message numbers and the
|
|
second number in a range is less than or equal to the first then the range
|
|
represents all messages in the group after the first message number.
|
|
|
|
B<NOTE> For compatibility reasons only with earlier versions of Net::NNTP
|
|
a message spec can be passed as a list of two numbers, this is deprecated
|
|
and a reference to the list should now be passed
|
|
|
|
=item PATTERN
|
|
|
|
The C<NNTP> protocol uses the C<WILDMAT> format for patterns.
|
|
The WILDMAT format was first developed by Rich Salz based on
|
|
the format used in the UNIX "find" command to articulate
|
|
file names. It was developed to provide a uniform mechanism
|
|
for matching patterns in the same manner that the UNIX shell
|
|
matches filenames.
|
|
|
|
Patterns are implicitly anchored at the
|
|
beginning and end of each string when testing for a match.
|
|
|
|
There are five pattern matching operations other than a strict
|
|
one-to-one match between the pattern and the source to be
|
|
checked for a match.
|
|
|
|
The first is an asterisk C<*> to match any sequence of zero or more
|
|
characters.
|
|
|
|
The second is a question mark C<?> to match any single character. The
|
|
third specifies a specific set of characters.
|
|
|
|
The set is specified as a list of characters, or as a range of characters
|
|
where the beginning and end of the range are separated by a minus (or dash)
|
|
character, or as any combination of lists and ranges. The dash can
|
|
also be included in the set as a character it if is the beginning
|
|
or end of the set. This set is enclosed in square brackets. The
|
|
close square bracket C<]> may be used in a set if it is the first
|
|
character in the set.
|
|
|
|
The fourth operation is the same as the
|
|
logical not of the third operation and is specified the same
|
|
way as the third with the addition of a caret character C<^> at
|
|
the beginning of the test string just inside the open square
|
|
bracket.
|
|
|
|
The final operation uses the backslash character to
|
|
invalidate the special meaning of the a open square bracket C<[>,
|
|
the asterisk, backslash or the question mark. Two backslashes in
|
|
sequence will result in the evaluation of the backslash as a
|
|
character with no special meaning.
|
|
|
|
=over 4
|
|
|
|
=item Examples
|
|
|
|
=item C<[^]-]>
|
|
|
|
matches any single character other than a close square
|
|
bracket or a minus sign/dash.
|
|
|
|
=item C<*bdc>
|
|
|
|
matches any string that ends with the string "bdc"
|
|
including the string "bdc" (without quotes).
|
|
|
|
=item C<[0-9a-zA-Z]>
|
|
|
|
matches any single printable alphanumeric ASCII character.
|
|
|
|
=item C<a??d>
|
|
|
|
matches any four character string which begins
|
|
with a and ends with d.
|
|
|
|
=back
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Net::Cmd>
|
|
|
|
=head1 AUTHOR
|
|
|
|
Graham Barr <[email protected]>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (c) 1995-1997 Graham Barr. All rights reserved.
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the same terms as Perl itself.
|
|
|
|
=cut
|