3 # Copyright (c) 1995-1997 Graham Barr <gbarr@pobox.com>. All rights reserved.
4 # This program is free software; you can redistribute it and/or
5 # modify it under the same terms as Perl itself.
10 use vars qw(@ISA $VERSION $debug);
17 $VERSION = "2.22"; # $Id: //depot/libnet/Net/NNTP.pm#18 $
18 @ISA = qw(Net::Cmd IO::Socket::INET);
23 my $type = ref($self) || $self;
25 $host = shift if @_ % 2;
29 $host ||= $ENV{NNTPSERVER} || $ENV{NEWSHOST};
31 my $hosts = defined $host ? [ $host ] : $NetConfig{nntp_hosts};
37 foreach $h (@{$hosts})
39 $obj = $type->SUPER::new(PeerAddr => ($host = $h),
40 PeerPort => $arg{Port} || 'nntp(119)',
42 Timeout => defined $arg{Timeout}
51 ${*$obj}{'net_nntp_host'} = $host;
54 $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
56 unless ($obj->response() == CMD_OK)
63 my @m = $obj->message;
65 unless(exists $arg{Reader} && $arg{Reader} == 0) {
66 # if server is INN and we have transfer rights the we are currently
67 # talking to innd not nnrpd
70 # If reader suceeds the we need to consider this code to determine postok
75 # I want to ignore this failure, so restore the previous status.
76 $obj->set_status($c,\@m);
80 ${*$obj}{'net_nntp_post'} = $c == 200 ? 1 : 0;
91 if((ref($nntp) and $nntp->code == 350 and $text =~ /^(\S+)/)
92 || ($text =~ /^(authinfo\s+pass)/io))
102 @_ == 1 or croak 'usage: $nntp->postok()';
104 ${*$nntp}{'net_nntp_post'} || 0;
109 @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->article( [ MSGID ], [ FH ] )';
113 @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
116 ? $nntp->read_until_dot(@fh)
121 @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->articlefh( [ MSGID ] )';
124 return unless $nntp->_ARTICLE(@_);
125 return $nntp->tied_fh;
130 @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
131 my($nntp,$user,$pass) = @_;
133 $nntp->_AUTHINFO("USER",$user) == CMD_MORE
134 && $nntp->_AUTHINFO("PASS",$pass) == CMD_OK;
139 @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
140 my($nntp,$user,$pass) = @_;
142 $nntp->_AUTHINFO('SIMPLE') == CMD_MORE
143 && $nntp->command($user,$pass)->response == CMD_OK;
148 @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->body( [ MSGID ], [ FH ] )';
152 @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
155 ? $nntp->read_until_dot(@fh)
161 @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->bodyfh( [ MSGID ] )';
163 return unless $nntp->_BODY(@_);
164 return $nntp->tied_fh;
169 @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->head( [ MSGID ], [ FH ] )';
173 @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
176 ? $nntp->read_until_dot(@fh)
182 @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->headfh( [ MSGID ] )';
184 return unless $nntp->_HEAD(@_);
185 return $nntp->tied_fh;
190 @_ == 1 || @_ == 2 or croak 'usage: $nntp->nntpstat( [ MSGID ] )';
193 $nntp->_STAT(@_) && $nntp->message =~ /(<[^>]+>)/o
201 @_ == 1 || @_ == 2 or croak 'usage: $nntp->group( [ GROUP ] )';
203 my $grp = ${*$nntp}{'net_nntp_group'} || undef;
206 unless(@_ || wantarray);
210 return wantarray ? () : undef
211 unless $nntp->_GROUP($newgrp || $grp || "")
212 && $nntp->message =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\S+)/;
214 my($count,$first,$last,$group) = ($1,$2,$3,$4);
216 # group may be replied as '(current group)'
217 $group = ${*$nntp}{'net_nntp_group'}
220 ${*$nntp}{'net_nntp_group'} = $group;
223 ? ($count,$first,$last,$group)
229 @_ == 1 or croak 'usage: $nntp->help()';
233 ? $nntp->read_until_dot
239 @_ >= 2 or croak 'usage: $nntp->ihave( MESSAGE-ID [, MESSAGE ])';
243 $nntp->_IHAVE($mid) && $nntp->datasend(@_)
244 ? @_ == 0 || $nntp->dataend
250 @_ == 1 or croak 'usage: $nntp->last()';
253 $nntp->_LAST && $nntp->message =~ /(<[^>]+>)/o
260 @_ == 1 or croak 'usage: $nntp->list()';
270 @_ >= 2 or croak 'usage: $nntp->newgroups( SINCE [, DISTRIBUTIONS ])';
272 my $time = _timestr(shift);
273 my $dist = shift || "";
275 $dist = join(",", @{$dist})
278 $nntp->_NEWGROUPS($time,$dist)
285 @_ >= 2 && @_ <= 4 or
286 croak 'usage: $nntp->newnews( SINCE [, GROUPS [, DISTRIBUTIONS ]])';
288 my $time = _timestr(shift);
289 my $grp = @_ ? shift : $nntp->group;
290 my $dist = shift || "";
293 $grp = join(",", @{$grp})
296 $dist = join(",", @{$dist})
299 $nntp->_NEWNEWS($grp,$time,$dist)
300 ? $nntp->_articlelist
306 @_ == 1 or croak 'usage: $nntp->next()';
309 $nntp->_NEXT && $nntp->message =~ /(<[^>]+>)/o
316 @_ >= 1 or croak 'usage: $nntp->post( [ MESSAGE ] )';
319 $nntp->_POST() && $nntp->datasend(@_)
320 ? @_ == 0 || $nntp->dataend
326 return unless $nntp->_POST();
327 return $nntp->tied_fh;
332 @_ == 1 or croak 'usage: $nntp->quit()';
341 @_ == 1 or croak 'usage: $nntp->slave()';
348 ## The following methods are not implemented by all servers
353 @_ == 1 || @_ == 2 or croak 'usage: $nntp->active( [ PATTERN ] )';
356 $nntp->_LIST('ACTIVE',@_)
363 @_ == 1 or croak 'usage: $nntp->active_times()';
366 $nntp->_LIST('ACTIVE.TIMES')
373 @_ == 1 or croak 'usage: $nntp->distributions()';
376 $nntp->_LIST('DISTRIBUTIONS')
377 ? $nntp->_description
381 sub distribution_patterns
383 @_ == 1 or croak 'usage: $nntp->distributions()';
389 $nntp->_LIST('DISTRIB.PATS') && ($arr = $nntp->read_until_dot)
390 ? [grep { /^\d/ && (chomp, $_ = [ split /:/ ]) } @$arr]
396 @_ == 1 || @_ == 2 or croak 'usage: $nntp->newsgroups( [ PATTERN ] )';
399 $nntp->_LIST('NEWSGROUPS',@_)
400 ? $nntp->_description
406 @_ == 1 or croak 'usage: $nntp->overview_fmt()';
409 $nntp->_LIST('OVERVIEW.FMT')
410 ? $nntp->_articlelist
416 @_ == 1 or croak 'usage: $nntp->subscriptions()';
419 $nntp->_LIST('SUBSCRIPTIONS')
420 ? $nntp->_articlelist
426 @_ == 1 || @_ == 2 or croak 'usage: $nntp->listgroup( [ GROUP ] )';
429 $nntp->_LISTGROUP(@_)
430 ? $nntp->_articlelist
436 @_ == 1 or croak 'usage: $nntp->reader()';
439 $nntp->_MODE('READER');
444 @_ == 1 || @_ == 2 or croak 'usage: $nntp->xgtitle( [ PATTERN ] )';
448 ? $nntp->_description
454 @_ >= 2 && @_ <= 4 or croak 'usage: $nntp->xhdr( HEADER, [ MESSAGE-SPEC ] )';
457 my $arg = _msg_arg(@_);
459 $nntp->_XHDR($hdr, $arg)
460 ? $nntp->_description
466 @_ == 2 || @_ == 3 or croak 'usage: $nntp->xover( MESSAGE-SPEC )';
468 my $arg = _msg_arg(@_);
477 @_ == 4 || @_ == 5 or croak '$nntp->xpat( HEADER, PATTERN, MESSAGE-SPEC )';
481 my $arg = _msg_arg(@_);
483 $pat = join(" ", @$pat)
486 $nntp->_XPAT($hdr,$arg,$pat)
487 ? $nntp->_description
493 @_ == 2 or croak 'usage: $nntp->xpath( MESSAGE-ID )';
497 unless $nntp->_XPATH($mid);
499 my $m; ($m = $nntp->message) =~ s/^\d+\s+//o;
500 my @p = split /\s+/, $m;
502 wantarray ? @p : $p[0];
507 @_ == 2 || @_ == 3 or croak 'usage: $nntp->xrover( MESSAGE-SPEC )';
509 my $arg = _msg_arg(@_);
512 ? $nntp->_description
518 @_ == 1 or croak 'usage: $nntp->date()';
521 $nntp->_DATE && $nntp->message =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
522 ? timegm($6,$5,$4,$3,$2-1,$1 - 1900)
528 ## Private subroutines
538 carp "Depriciated passing of two message numbers, "
541 $spec = [ $spec, $_[0] ];
549 if(defined $spec->[1])
552 if $spec->[1] != $spec->[0];
554 if $spec->[1] > $spec->[0];
569 my @g = reverse((gmtime($time))[0..5]);
572 sprintf "%02d%02d%02d %02d%02d%02d GMT", @g;
578 my $arr = $nntp->read_until_dot or
586 my @a = split(/[\s\n]+/,$ln);
587 $hash->{$a[0]} = [ @a[1,2,3] ];
596 my $arr = $nntp->read_until_dot or
604 my @a = split(/[\t\n]/,$ln);
606 $hash->{$m} = [ @a ];
615 my $arr = $nntp->read_until_dot;
626 my $arr = $nntp->read_until_dot or
637 if $ln =~ s/^\s*(\S+)\s*//o;
648 sub _ARTICLE { shift->command('ARTICLE',@_)->response == CMD_OK }
649 sub _AUTHINFO { shift->command('AUTHINFO',@_)->response }
650 sub _BODY { shift->command('BODY',@_)->response == CMD_OK }
651 sub _DATE { shift->command('DATE')->response == CMD_INFO }
652 sub _GROUP { shift->command('GROUP',@_)->response == CMD_OK }
653 sub _HEAD { shift->command('HEAD',@_)->response == CMD_OK }
654 sub _HELP { shift->command('HELP',@_)->response == CMD_INFO }
655 sub _IHAVE { shift->command('IHAVE',@_)->response == CMD_MORE }
656 sub _LAST { shift->command('LAST')->response == CMD_OK }
657 sub _LIST { shift->command('LIST',@_)->response == CMD_OK }
658 sub _LISTGROUP { shift->command('LISTGROUP',@_)->response == CMD_OK }
659 sub _NEWGROUPS { shift->command('NEWGROUPS',@_)->response == CMD_OK }
660 sub _NEWNEWS { shift->command('NEWNEWS',@_)->response == CMD_OK }
661 sub _NEXT { shift->command('NEXT')->response == CMD_OK }
662 sub _POST { shift->command('POST',@_)->response == CMD_MORE }
663 sub _QUIT { shift->command('QUIT',@_)->response == CMD_OK }
664 sub _SLAVE { shift->command('SLAVE',@_)->response == CMD_OK }
665 sub _STAT { shift->command('STAT',@_)->response == CMD_OK }
666 sub _MODE { shift->command('MODE',@_)->response == CMD_OK }
667 sub _XGTITLE { shift->command('XGTITLE',@_)->response == CMD_OK }
668 sub _XHDR { shift->command('XHDR',@_)->response == CMD_OK }
669 sub _XPAT { shift->command('XPAT',@_)->response == CMD_OK }
670 sub _XPATH { shift->command('XPATH',@_)->response == CMD_OK }
671 sub _XOVER { shift->command('XOVER',@_)->response == CMD_OK }
672 sub _XROVER { shift->command('XROVER',@_)->response == CMD_OK }
673 sub _XTHREAD { shift->unsupported }
674 sub _XSEARCH { shift->unsupported }
675 sub _XINDEX { shift->unsupported }
684 defined(fileno($nntp)) && $nntp->quit
694 Net::NNTP - NNTP Client class
700 $nntp = Net::NNTP->new("some.host.name");
705 C<Net::NNTP> is a class implementing a simple NNTP client in Perl as described
706 in RFC977. C<Net::NNTP> inherits its communication methods from C<Net::Cmd>
712 =item new ( [ HOST ] [, OPTIONS ])
714 This is the constructor for a new Net::NNTP object. C<HOST> is the
715 name of the remote host to which a NNTP connection is required. If not
716 given two environment variables are checked, first C<NNTPSERVER> then
717 C<NEWSHOST>, then C<Net::Config> is checked, and if a host is not found
718 then C<news> is used.
720 C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
721 Possible options are:
723 B<Timeout> - Maximum time, in seconds, to wait for a response from the
724 NNTP server, a value of zero will cause all IO operations to block.
727 B<Debug> - Enable the printing of debugging information to STDERR
729 B<Reader> - If the remote server is INN then initially the connection
730 will be to nnrpd, by default C<Net::NNTP> will issue a C<MODE READER> command
731 so that the remote server becomes innd. If the C<Reader> option is given
732 with a value of zero, then this command will not be sent and the
733 connection will be left talking to nnrpd.
739 Unless otherwise stated all methods return either a I<true> or I<false>
740 value, with I<true> meaning that the operation was a success. When a method
741 states that it returns a value, failure will be returned as I<undef> or an
746 =item article ( [ MSGID|MSGNUM ], [FH] )
748 Retrieve the header, a blank line, then the body (text) of the
751 If C<FH> is specified then it is expected to be a valid filehandle
752 and the result will be printed to it, on success a true value will be
753 returned. If C<FH> is not specified then the return value, on success,
754 will be a reference to an array containg the article requested, each
755 entry in the array will contain one line of the article.
757 If no arguments are passed then the current article in the currently
758 selected newsgroup is fetched.
760 C<MSGNUM> is a numeric id of an article in the current newsgroup, and
761 will change the current article pointer. C<MSGID> is the message id of
762 an article as shown in that article's header. It is anticipated that the
763 client will obtain the C<MSGID> from a list provided by the C<newnews>
764 command, from references contained within another article, or from the
765 message-id provided in the response to some other commands.
767 If there is an error then C<undef> will be returned.
769 =item body ( [ MSGID|MSGNUM ], [FH] )
771 Like C<article> but only fetches the body of the article.
773 =item head ( [ MSGID|MSGNUM ], [FH] )
775 Like C<article> but only fetches the headers for the article.
777 =item articlefh ( [ MSGID|MSGNUM ] )
779 =item bodyfh ( [ MSGID|MSGNUM ] )
781 =item headfh ( [ MSGID|MSGNUM ] )
783 These are similar to article(), body() and head(), but rather than
784 returning the requested data directly, they return a tied filehandle
785 from which to read the article.
787 =item nntpstat ( [ MSGID|MSGNUM ] )
789 The C<nntpstat> command is similar to the C<article> command except that no
790 text is returned. When selecting by message number within a group,
791 the C<nntpstat> command serves to set the "current article pointer" without
794 Using the C<nntpstat> command to
795 select by message-id is valid but of questionable value, since a
796 selection by message-id does B<not> alter the "current article pointer".
798 Returns the message-id of the "current article".
800 =item group ( [ GROUP ] )
802 Set and/or get the current group. If C<GROUP> is not given then information
803 is returned on the current group.
805 In a scalar context it returns the group name.
807 In an array context the return value is a list containing, the number
808 of articles in the group, the number of the first article, the number
809 of the last article and the group name.
811 =item ihave ( MSGID [, MESSAGE ])
813 The C<ihave> command informs the server that the client has an article
814 whose id is C<MSGID>. If the server desires a copy of that
815 article, and C<MESSAGE> has been given the it will be sent.
817 Returns I<true> if the server desires the article and C<MESSAGE> was
818 successfully sent,if specified.
820 If C<MESSAGE> is not specified then the message must be sent using the
821 C<datasend> and C<dataend> methods from L<Net::Cmd>
823 C<MESSAGE> can be either an array of lines or a reference to an array.
827 Set the "current article pointer" to the previous article in the current
830 Returns the message-id of the article.
834 Returns the date on the remote server. This date will be in a UNIX time
835 format (seconds since 1970)
839 C<postok> will return I<true> if the servers initial response indicated
840 that it will allow posting.
842 =item authinfo ( USER, PASS )
846 Obtain information about all the active newsgroups. The results is a reference
847 to a hash where the key is a group name and each value is a reference to an
848 array. The elements in this array are:- the last article number in the group,
849 the first article number in the group and any information flags about the group.
851 =item newgroups ( SINCE [, DISTRIBUTIONS ])
853 C<SINCE> is a time value and C<DISTRIBUTIONS> is either a distribution
854 pattern or a reference to a list of distribution patterns.
855 The result is the same as C<list>, but the
856 groups return will be limited to those created after C<SINCE> and, if
857 specified, in one of the distribution areas in C<DISTRIBUTIONS>.
859 =item newnews ( SINCE [, GROUPS [, DISTRIBUTIONS ]])
861 C<SINCE> is a time value. C<GROUPS> is either a group pattern or a reference
862 to a list of group patterns. C<DISTRIBUTIONS> is either a distribution
863 pattern or a reference to a list of distribution patterns.
865 Returns a reference to a list which contains the message-ids of all news posted
866 after C<SINCE>, that are in a groups which matched C<GROUPS> and a
867 distribution which matches C<DISTRIBUTIONS>.
871 Set the "current article pointer" to the next article in the current
874 Returns the message-id of the article.
876 =item post ( [ MESSAGE ] )
878 Post a new article to the news server. If C<MESSAGE> is specified and posting
879 is allowed then the message will be sent.
881 If C<MESSAGE> is not specified then the message must be sent using the
882 C<datasend> and C<dataend> methods from L<Net::Cmd>
884 C<MESSAGE> can be either an array of lines or a reference to an array.
886 The message, either sent via C<datasend> or as the C<MESSAGE>
887 parameter, must be in the format as described by RFC822 and must
888 contain From:, Newsgroups: and Subject: headers.
892 Post a new article to the news server using a tied filehandle. If
893 posting is allowed, this method will return a tied filehandle that you
894 can print() the contents of the article to be posted. You must
895 explicitly close() the filehandle when you are finished posting the
896 article, and the return value from the close() call will indicate
897 whether the message was successfully posted.
901 Tell the remote server that I am not a user client, but probably another
906 Quit the remote server and close the socket connection.
910 =head2 Extension methods
912 These methods use commands that are not part of the RFC977 documentation. Some
913 servers may not support all of them.
917 =item newsgroups ( [ PATTERN ] )
919 Returns a reference to a hash where the keys are all the group names which
920 match C<PATTERN>, or all of the groups if no pattern is specified, and
921 each value contains the description text for the group.
923 =item distributions ()
925 Returns a reference to a hash where the keys are all the possible
926 distribution names and the values are the distribution descriptions.
928 =item subscriptions ()
930 Returns a reference to a list which contains a list of groups which
931 are recommended for a new user to subscribe to.
933 =item overview_fmt ()
935 Returns a reference to an array which contain the names of the fields returned
938 =item active_times ()
940 Returns a reference to a hash where the keys are the group names and each
941 value is a reference to an array containing the time the groups was created
942 and an identifier, possibly an Email address, of the creator.
944 =item active ( [ PATTERN ] )
946 Similar to C<list> but only active groups that match the pattern are returned.
947 C<PATTERN> can be a group pattern.
949 =item xgtitle ( PATTERN )
951 Returns a reference to a hash where the keys are all the group names which
952 match C<PATTERN> and each value is the description text for the group.
954 =item xhdr ( HEADER, MESSAGE-SPEC )
956 Obtain the header field C<HEADER> for all the messages specified.
958 The return value will be a reference
959 to a hash where the keys are the message numbers and each value contains
960 the text of the requested header for that message.
962 =item xover ( MESSAGE-SPEC )
964 The return value will be a reference
965 to a hash where the keys are the message numbers and each value contains
966 a reference to an array which contains the overview fields for that
969 The names of the fields can be obtained by calling C<overview_fmt>.
971 =item xpath ( MESSAGE-ID )
973 Returns the path name to the file on the server which contains the specified
976 =item xpat ( HEADER, PATTERN, MESSAGE-SPEC)
978 The result is the same as C<xhdr> except the is will be restricted to
979 headers where the text of the header matches C<PATTERN>
983 The XROVER command returns reference information for the article(s)
986 Returns a reference to a HASH where the keys are the message numbers and the
987 values are the References: lines from the articles
989 =item listgroup ( [ GROUP ] )
991 Returns a reference to a list of all the active messages in C<GROUP>, or
992 the current group if C<GROUP> is not specified.
996 Tell the server that you are a reader and not another server.
998 This is required by some servers. For example if you are connecting to
999 an INN server and you have transfer permission your connection will
1000 be connected to the transfer daemon, not the NNTP daemon. Issuing
1001 this command will cause the transfer daemon to hand over control
1004 Some servers do not understand this command, but issuing it and ignoring
1005 the response is harmless.
1011 The following NNTP command are unsupported by the package, and there are
1025 C<MESSAGE-SPEC> is either a single message-id, a single message number, or
1026 a reference to a list of two message numbers.
1028 If C<MESSAGE-SPEC> is a reference to a list of two message numbers and the
1029 second number in a range is less than or equal to the first then the range
1030 represents all messages in the group after the first message number.
1032 B<NOTE> For compatibility reasons only with earlier versions of Net::NNTP
1033 a message spec can be passed as a list of two numbers, this is deprecated
1034 and a reference to the list should now be passed
1038 The C<NNTP> protocol uses the C<WILDMAT> format for patterns.
1039 The WILDMAT format was first developed by Rich Salz based on
1040 the format used in the UNIX "find" command to articulate
1041 file names. It was developed to provide a uniform mechanism
1042 for matching patterns in the same manner that the UNIX shell
1045 Patterns are implicitly anchored at the
1046 beginning and end of each string when testing for a match.
1048 There are five pattern matching operations other than a strict
1049 one-to-one match between the pattern and the source to be
1050 checked for a match.
1052 The first is an asterisk C<*> to match any sequence of zero or more
1055 The second is a question mark C<?> to match any single character. The
1056 third specifies a specific set of characters.
1058 The set is specified as a list of characters, or as a range of characters
1059 where the beginning and end of the range are separated by a minus (or dash)
1060 character, or as any combination of lists and ranges. The dash can
1061 also be included in the set as a character it if is the beginning
1062 or end of the set. This set is enclosed in square brackets. The
1063 close square bracket C<]> may be used in a set if it is the first
1064 character in the set.
1066 The fourth operation is the same as the
1067 logical not of the third operation and is specified the same
1068 way as the third with the addition of a caret character C<^> at
1069 the beginning of the test string just inside the open square
1072 The final operation uses the backslash character to
1073 invalidate the special meaning of an open square bracket C<[>,
1074 the asterisk, backslash or the question mark. Two backslashes in
1075 sequence will result in the evaluation of the backslash as a
1076 character with no special meaning.
1084 matches any single character other than a close square
1085 bracket or a minus sign/dash.
1089 matches any string that ends with the string "bdc"
1090 including the string "bdc" (without quotes).
1092 =item C<[0-9a-zA-Z]>
1094 matches any single printable alphanumeric ASCII character.
1098 matches any four character string which begins
1099 with a and ends with d.
1111 Graham Barr <gbarr@pobox.com>
1115 Copyright (c) 1995-1997 Graham Barr. All rights reserved.
1116 This program is free software; you can redistribute it and/or modify
1117 it under the same terms as Perl itself.
1121 I<$Id: //depot/libnet/Net/NNTP.pm#18 $>