Move libnet Net::* to the top level, to mirror the CPAN distribution.
[perl.git] / ext / libnet / Net / NNTP.pm
1 # Net::NNTP.pm
2 #
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.
6
7 package Net::NNTP;
8
9 use strict;
10 use vars qw(@ISA $VERSION $debug);
11 use IO::Socket;
12 use Net::Cmd;
13 use Carp;
14 use Time::Local;
15 use Net::Config;
16
17 $VERSION = "2.24";
18 @ISA     = qw(Net::Cmd IO::Socket::INET);
19
20
21 sub new {
22   my $self = shift;
23   my $type = ref($self) || $self;
24   my ($host, %arg);
25   if (@_ % 2) {
26     $host = shift;
27     %arg  = @_;
28   }
29   else {
30     %arg  = @_;
31     $host = delete $arg{Host};
32   }
33   my $obj;
34
35   $host ||= $ENV{NNTPSERVER} || $ENV{NEWSHOST};
36
37   my $hosts = defined $host ? [$host] : $NetConfig{nntp_hosts};
38
39   @{$hosts} = qw(news)
40     unless @{$hosts};
41
42   my $h;
43   foreach $h (@{$hosts}) {
44     $obj = $type->SUPER::new(
45       PeerAddr => ($host = $h),
46       PeerPort => $arg{Port} || 'nntp(119)',
47       Proto => 'tcp',
48       Timeout => defined $arg{Timeout}
49       ? $arg{Timeout}
50       : 120
51       )
52       and last;
53   }
54
55   return undef
56     unless defined $obj;
57
58   ${*$obj}{'net_nntp_host'} = $host;
59
60   $obj->autoflush(1);
61   $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
62
63   unless ($obj->response() == CMD_OK) {
64     $obj->close;
65     return undef;
66   }
67
68   my $c = $obj->code;
69   my @m = $obj->message;
70
71   unless (exists $arg{Reader} && $arg{Reader} == 0) {
72
73     # if server is INN and we have transfer rights the we are currently
74     # talking to innd not nnrpd
75     if ($obj->reader) {
76
77       # If reader suceeds the we need to consider this code to determine postok
78       $c = $obj->code;
79     }
80     else {
81
82       # I want to ignore this failure, so restore the previous status.
83       $obj->set_status($c, \@m);
84     }
85   }
86
87   ${*$obj}{'net_nntp_post'} = $c == 200 ? 1 : 0;
88
89   $obj;
90 }
91
92
93 sub host {
94   my $me = shift;
95   ${*$me}{'net_nntp_host'};
96 }
97
98
99 sub debug_text {
100   my $nntp  = shift;
101   my $inout = shift;
102   my $text  = shift;
103
104   if ( (ref($nntp) and $nntp->code == 350 and $text =~ /^(\S+)/)
105     || ($text =~ /^(authinfo\s+pass)/io))
106   {
107     $text = "$1 ....\n";
108   }
109
110   $text;
111 }
112
113
114 sub postok {
115   @_ == 1 or croak 'usage: $nntp->postok()';
116   my $nntp = shift;
117   ${*$nntp}{'net_nntp_post'} || 0;
118 }
119
120
121 sub article {
122   @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->article( [ MSGID ], [ FH ] )';
123   my $nntp = shift;
124   my @fh;
125
126   @fh = (pop) if @_ == 2 || (@_ && (ref($_[0]) || ref(\$_[0]) eq 'GLOB'));
127
128   $nntp->_ARTICLE(@_)
129     ? $nntp->read_until_dot(@fh)
130     : undef;
131 }
132
133
134 sub articlefh {
135   @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->articlefh( [ MSGID ] )';
136   my $nntp = shift;
137
138   return unless $nntp->_ARTICLE(@_);
139   return $nntp->tied_fh;
140 }
141
142
143 sub authinfo {
144   @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
145   my ($nntp, $user, $pass) = @_;
146
147   $nntp->_AUTHINFO("USER",      $user) == CMD_MORE
148     && $nntp->_AUTHINFO("PASS", $pass) == CMD_OK;
149 }
150
151
152 sub authinfo_simple {
153   @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
154   my ($nntp, $user, $pass) = @_;
155
156   $nntp->_AUTHINFO('SIMPLE') == CMD_MORE
157     && $nntp->command($user, $pass)->response == CMD_OK;
158 }
159
160
161 sub body {
162   @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->body( [ MSGID ], [ FH ] )';
163   my $nntp = shift;
164   my @fh;
165
166   @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
167
168   $nntp->_BODY(@_)
169     ? $nntp->read_until_dot(@fh)
170     : undef;
171 }
172
173
174 sub bodyfh {
175   @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->bodyfh( [ MSGID ] )';
176   my $nntp = shift;
177   return unless $nntp->_BODY(@_);
178   return $nntp->tied_fh;
179 }
180
181
182 sub head {
183   @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->head( [ MSGID ], [ FH ] )';
184   my $nntp = shift;
185   my @fh;
186
187   @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
188
189   $nntp->_HEAD(@_)
190     ? $nntp->read_until_dot(@fh)
191     : undef;
192 }
193
194
195 sub headfh {
196   @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->headfh( [ MSGID ] )';
197   my $nntp = shift;
198   return unless $nntp->_HEAD(@_);
199   return $nntp->tied_fh;
200 }
201
202
203 sub nntpstat {
204   @_ == 1 || @_ == 2 or croak 'usage: $nntp->nntpstat( [ MSGID ] )';
205   my $nntp = shift;
206
207   $nntp->_STAT(@_) && $nntp->message =~ /(<[^>]+>)/o
208     ? $1
209     : undef;
210 }
211
212
213 sub group {
214   @_ == 1 || @_ == 2 or croak 'usage: $nntp->group( [ GROUP ] )';
215   my $nntp = shift;
216   my $grp  = ${*$nntp}{'net_nntp_group'} || undef;
217
218   return $grp
219     unless (@_ || wantarray);
220
221   my $newgrp = shift;
222
223   return wantarray ? () : undef
224     unless $nntp->_GROUP($newgrp || $grp || "")
225     && $nntp->message =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\S+)/;
226
227   my ($count, $first, $last, $group) = ($1, $2, $3, $4);
228
229   # group may be replied as '(current group)'
230   $group = ${*$nntp}{'net_nntp_group'}
231     if $group =~ /\(/;
232
233   ${*$nntp}{'net_nntp_group'} = $group;
234
235   wantarray
236     ? ($count, $first, $last, $group)
237     : $group;
238 }
239
240
241 sub help {
242   @_ == 1 or croak 'usage: $nntp->help()';
243   my $nntp = shift;
244
245   $nntp->_HELP
246     ? $nntp->read_until_dot
247     : undef;
248 }
249
250
251 sub ihave {
252   @_ >= 2 or croak 'usage: $nntp->ihave( MESSAGE-ID [, MESSAGE ])';
253   my $nntp = shift;
254   my $mid  = shift;
255
256   $nntp->_IHAVE($mid) && $nntp->datasend(@_)
257     ? @_ == 0 || $nntp->dataend
258     : undef;
259 }
260
261
262 sub last {
263   @_ == 1 or croak 'usage: $nntp->last()';
264   my $nntp = shift;
265
266   $nntp->_LAST && $nntp->message =~ /(<[^>]+>)/o
267     ? $1
268     : undef;
269 }
270
271
272 sub list {
273   @_ == 1 or croak 'usage: $nntp->list()';
274   my $nntp = shift;
275
276   $nntp->_LIST
277     ? $nntp->_grouplist
278     : undef;
279 }
280
281
282 sub newgroups {
283   @_ >= 2 or croak 'usage: $nntp->newgroups( SINCE [, DISTRIBUTIONS ])';
284   my $nntp = shift;
285   my $time = _timestr(shift);
286   my $dist = shift || "";
287
288   $dist = join(",", @{$dist})
289     if ref($dist);
290
291   $nntp->_NEWGROUPS($time, $dist)
292     ? $nntp->_grouplist
293     : undef;
294 }
295
296
297 sub newnews {
298   @_ >= 2 && @_ <= 4
299     or croak 'usage: $nntp->newnews( SINCE [, GROUPS [, DISTRIBUTIONS ]])';
300   my $nntp = shift;
301   my $time = _timestr(shift);
302   my $grp  = @_ ? shift: $nntp->group;
303   my $dist = shift || "";
304
305   $grp ||= "*";
306   $grp = join(",", @{$grp})
307     if ref($grp);
308
309   $dist = join(",", @{$dist})
310     if ref($dist);
311
312   $nntp->_NEWNEWS($grp, $time, $dist)
313     ? $nntp->_articlelist
314     : undef;
315 }
316
317
318 sub next {
319   @_ == 1 or croak 'usage: $nntp->next()';
320   my $nntp = shift;
321
322   $nntp->_NEXT && $nntp->message =~ /(<[^>]+>)/o
323     ? $1
324     : undef;
325 }
326
327
328 sub post {
329   @_ >= 1 or croak 'usage: $nntp->post( [ MESSAGE ] )';
330   my $nntp = shift;
331
332   $nntp->_POST() && $nntp->datasend(@_)
333     ? @_ == 0 || $nntp->dataend
334     : undef;
335 }
336
337
338 sub postfh {
339   my $nntp = shift;
340   return unless $nntp->_POST();
341   return $nntp->tied_fh;
342 }
343
344
345 sub quit {
346   @_ == 1 or croak 'usage: $nntp->quit()';
347   my $nntp = shift;
348
349   $nntp->_QUIT;
350   $nntp->close;
351 }
352
353
354 sub slave {
355   @_ == 1 or croak 'usage: $nntp->slave()';
356   my $nntp = shift;
357
358   $nntp->_SLAVE;
359 }
360
361 ##
362 ## The following methods are not implemented by all servers
363 ##
364
365
366 sub active {
367   @_ == 1 || @_ == 2 or croak 'usage: $nntp->active( [ PATTERN ] )';
368   my $nntp = shift;
369
370   $nntp->_LIST('ACTIVE', @_)
371     ? $nntp->_grouplist
372     : undef;
373 }
374
375
376 sub active_times {
377   @_ == 1 or croak 'usage: $nntp->active_times()';
378   my $nntp = shift;
379
380   $nntp->_LIST('ACTIVE.TIMES')
381     ? $nntp->_grouplist
382     : undef;
383 }
384
385
386 sub distributions {
387   @_ == 1 or croak 'usage: $nntp->distributions()';
388   my $nntp = shift;
389
390   $nntp->_LIST('DISTRIBUTIONS')
391     ? $nntp->_description
392     : undef;
393 }
394
395
396 sub distribution_patterns {
397   @_ == 1 or croak 'usage: $nntp->distributions()';
398   my $nntp = shift;
399
400   my $arr;
401   local $_;
402
403   $nntp->_LIST('DISTRIB.PATS')
404     && ($arr = $nntp->read_until_dot)
405     ? [grep { /^\d/ && (chomp, $_ = [split /:/]) } @$arr]
406     : undef;
407 }
408
409
410 sub newsgroups {
411   @_ == 1 || @_ == 2 or croak 'usage: $nntp->newsgroups( [ PATTERN ] )';
412   my $nntp = shift;
413
414   $nntp->_LIST('NEWSGROUPS', @_)
415     ? $nntp->_description
416     : undef;
417 }
418
419
420 sub overview_fmt {
421   @_ == 1 or croak 'usage: $nntp->overview_fmt()';
422   my $nntp = shift;
423
424   $nntp->_LIST('OVERVIEW.FMT')
425     ? $nntp->_articlelist
426     : undef;
427 }
428
429
430 sub subscriptions {
431   @_ == 1 or croak 'usage: $nntp->subscriptions()';
432   my $nntp = shift;
433
434   $nntp->_LIST('SUBSCRIPTIONS')
435     ? $nntp->_articlelist
436     : undef;
437 }
438
439
440 sub listgroup {
441   @_ == 1 || @_ == 2 or croak 'usage: $nntp->listgroup( [ GROUP ] )';
442   my $nntp = shift;
443
444   $nntp->_LISTGROUP(@_)
445     ? $nntp->_articlelist
446     : undef;
447 }
448
449
450 sub reader {
451   @_ == 1 or croak 'usage: $nntp->reader()';
452   my $nntp = shift;
453
454   $nntp->_MODE('READER');
455 }
456
457
458 sub xgtitle {
459   @_ == 1 || @_ == 2 or croak 'usage: $nntp->xgtitle( [ PATTERN ] )';
460   my $nntp = shift;
461
462   $nntp->_XGTITLE(@_)
463     ? $nntp->_description
464     : undef;
465 }
466
467
468 sub xhdr {
469   @_ >= 2 && @_ <= 4 or croak 'usage: $nntp->xhdr( HEADER, [ MESSAGE-SPEC ] )';
470   my $nntp = shift;
471   my $hdr  = shift;
472   my $arg  = _msg_arg(@_);
473
474   $nntp->_XHDR($hdr, $arg)
475     ? $nntp->_description
476     : undef;
477 }
478
479
480 sub xover {
481   @_ == 2 || @_ == 3 or croak 'usage: $nntp->xover( MESSAGE-SPEC )';
482   my $nntp = shift;
483   my $arg  = _msg_arg(@_);
484
485   $nntp->_XOVER($arg)
486     ? $nntp->_fieldlist
487     : undef;
488 }
489
490
491 sub xpat {
492   @_ == 4 || @_ == 5 or croak '$nntp->xpat( HEADER, PATTERN, MESSAGE-SPEC )';
493   my $nntp = shift;
494   my $hdr  = shift;
495   my $pat  = shift;
496   my $arg  = _msg_arg(@_);
497
498   $pat = join(" ", @$pat)
499     if ref($pat);
500
501   $nntp->_XPAT($hdr, $arg, $pat)
502     ? $nntp->_description
503     : undef;
504 }
505
506
507 sub xpath {
508   @_ == 2 or croak 'usage: $nntp->xpath( MESSAGE-ID )';
509   my ($nntp, $mid) = @_;
510
511   return undef
512     unless $nntp->_XPATH($mid);
513
514   my $m;
515   ($m = $nntp->message) =~ s/^\d+\s+//o;
516   my @p = split /\s+/, $m;
517
518   wantarray ? @p : $p[0];
519 }
520
521
522 sub xrover {
523   @_ == 2 || @_ == 3 or croak 'usage: $nntp->xrover( MESSAGE-SPEC )';
524   my $nntp = shift;
525   my $arg  = _msg_arg(@_);
526
527   $nntp->_XROVER($arg)
528     ? $nntp->_description
529     : undef;
530 }
531
532
533 sub date {
534   @_ == 1 or croak 'usage: $nntp->date()';
535   my $nntp = shift;
536
537   $nntp->_DATE
538     && $nntp->message =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
539     ? timegm($6, $5, $4, $3, $2 - 1, $1 - 1900)
540     : undef;
541 }
542
543
544 ##
545 ## Private subroutines
546 ##
547
548
549 sub _msg_arg {
550   my $spec = shift;
551   my $arg  = "";
552
553   if (@_) {
554     carp "Depriciated passing of two message numbers, " . "pass a reference"
555       if $^W;
556     $spec = [$spec, $_[0]];
557   }
558
559   if (defined $spec) {
560     if (ref($spec)) {
561       $arg = $spec->[0];
562       if (defined $spec->[1]) {
563         $arg .= "-"
564           if $spec->[1] != $spec->[0];
565         $arg .= $spec->[1]
566           if $spec->[1] > $spec->[0];
567       }
568     }
569     else {
570       $arg = $spec;
571     }
572   }
573
574   $arg;
575 }
576
577
578 sub _timestr {
579   my $time = shift;
580   my @g    = reverse((gmtime($time))[0 .. 5]);
581   $g[1] += 1;
582   $g[0] %= 100;
583   sprintf "%02d%02d%02d %02d%02d%02d GMT", @g;
584 }
585
586
587 sub _grouplist {
588   my $nntp = shift;
589   my $arr  = $nntp->read_until_dot
590     or return undef;
591
592   my $hash = {};
593   my $ln;
594
595   foreach $ln (@$arr) {
596     my @a = split(/[\s\n]+/, $ln);
597     $hash->{$a[0]} = [@a[1, 2, 3]];
598   }
599
600   $hash;
601 }
602
603
604 sub _fieldlist {
605   my $nntp = shift;
606   my $arr  = $nntp->read_until_dot
607     or return undef;
608
609   my $hash = {};
610   my $ln;
611
612   foreach $ln (@$arr) {
613     my @a = split(/[\t\n]/, $ln);
614     my $m = shift @a;
615     $hash->{$m} = [@a];
616   }
617
618   $hash;
619 }
620
621
622 sub _articlelist {
623   my $nntp = shift;
624   my $arr  = $nntp->read_until_dot;
625
626   chomp(@$arr)
627     if $arr;
628
629   $arr;
630 }
631
632
633 sub _description {
634   my $nntp = shift;
635   my $arr  = $nntp->read_until_dot
636     or return undef;
637
638   my $hash = {};
639   my $ln;
640
641   foreach $ln (@$arr) {
642     chomp($ln);
643
644     $hash->{$1} = $ln
645       if $ln =~ s/^\s*(\S+)\s*//o;
646   }
647
648   $hash;
649
650 }
651
652 ##
653 ## The commands
654 ##
655
656
657 sub _ARTICLE  { shift->command('ARTICLE',  @_)->response == CMD_OK }
658 sub _AUTHINFO { shift->command('AUTHINFO', @_)->response }
659 sub _BODY     { shift->command('BODY',     @_)->response == CMD_OK }
660 sub _DATE      { shift->command('DATE')->response == CMD_INFO }
661 sub _GROUP     { shift->command('GROUP', @_)->response == CMD_OK }
662 sub _HEAD      { shift->command('HEAD', @_)->response == CMD_OK }
663 sub _HELP      { shift->command('HELP', @_)->response == CMD_INFO }
664 sub _IHAVE     { shift->command('IHAVE', @_)->response == CMD_MORE }
665 sub _LAST      { shift->command('LAST')->response == CMD_OK }
666 sub _LIST      { shift->command('LIST', @_)->response == CMD_OK }
667 sub _LISTGROUP { shift->command('LISTGROUP', @_)->response == CMD_OK }
668 sub _NEWGROUPS { shift->command('NEWGROUPS', @_)->response == CMD_OK }
669 sub _NEWNEWS   { shift->command('NEWNEWS', @_)->response == CMD_OK }
670 sub _NEXT      { shift->command('NEXT')->response == CMD_OK }
671 sub _POST      { shift->command('POST', @_)->response == CMD_MORE }
672 sub _QUIT      { shift->command('QUIT', @_)->response == CMD_OK }
673 sub _SLAVE     { shift->command('SLAVE', @_)->response == CMD_OK }
674 sub _STAT      { shift->command('STAT', @_)->response == CMD_OK }
675 sub _MODE      { shift->command('MODE', @_)->response == CMD_OK }
676 sub _XGTITLE   { shift->command('XGTITLE', @_)->response == CMD_OK }
677 sub _XHDR      { shift->command('XHDR', @_)->response == CMD_OK }
678 sub _XPAT      { shift->command('XPAT', @_)->response == CMD_OK }
679 sub _XPATH     { shift->command('XPATH', @_)->response == CMD_OK }
680 sub _XOVER     { shift->command('XOVER', @_)->response == CMD_OK }
681 sub _XROVER    { shift->command('XROVER', @_)->response == CMD_OK }
682 sub _XTHREAD   { shift->unsupported }
683 sub _XSEARCH   { shift->unsupported }
684 sub _XINDEX    { shift->unsupported }
685
686 ##
687 ## IO/perl methods
688 ##
689
690
691 sub DESTROY {
692   my $nntp = shift;
693   defined(fileno($nntp)) && $nntp->quit;
694 }
695
696
697 1;
698
699 __END__
700
701 =head1 NAME
702
703 Net::NNTP - NNTP Client class
704
705 =head1 SYNOPSIS
706
707     use Net::NNTP;
708
709     $nntp = Net::NNTP->new("some.host.name");
710     $nntp->quit;
711
712 =head1 DESCRIPTION
713
714 C<Net::NNTP> is a class implementing a simple NNTP client in Perl as described
715 in RFC977. C<Net::NNTP> inherits its communication methods from C<Net::Cmd>
716
717 =head1 CONSTRUCTOR
718
719 =over 4
720
721 =item new ( [ HOST ] [, OPTIONS ])
722
723 This is the constructor for a new Net::NNTP object. C<HOST> is the
724 name of the remote host to which a NNTP connection is required. If not
725 given then it may be passed as the C<Host> option described below. If no host is passed
726 then two environment variables are checked, first C<NNTPSERVER> then
727 C<NEWSHOST>, then C<Net::Config> is checked, and if a host is not found
728 then C<news> is used.
729
730 C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
731 Possible options are:
732
733 B<Host> - NNTP host to connect to. It may be a single scalar, as defined for
734 the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
735 an array with hosts to try in turn. The L</host> method will return the value
736 which was used to connect to the host.
737
738 B<Timeout> - Maximum time, in seconds, to wait for a response from the
739 NNTP server, a value of zero will cause all IO operations to block.
740 (default: 120)
741
742 B<Debug> - Enable the printing of debugging information to STDERR
743
744 B<Reader> - If the remote server is INN then initially the connection
745 will be to nnrpd, by default C<Net::NNTP> will issue a C<MODE READER> command
746 so that the remote server becomes innd. If the C<Reader> option is given
747 with a value of zero, then this command will not be sent and the
748 connection will be left talking to nnrpd.
749
750 =back
751
752 =head1 METHODS
753
754 Unless otherwise stated all methods return either a I<true> or I<false>
755 value, with I<true> meaning that the operation was a success. When a method
756 states that it returns a value, failure will be returned as I<undef> or an
757 empty list.
758
759 =over 4
760
761 =item article ( [ MSGID|MSGNUM ], [FH] )
762
763 Retrieve the header, a blank line, then the body (text) of the
764 specified article. 
765
766 If C<FH> is specified then it is expected to be a valid filehandle
767 and the result will be printed to it, on success a true value will be
768 returned. If C<FH> is not specified then the return value, on success,
769 will be a reference to an array containing the article requested, each
770 entry in the array will contain one line of the article.
771
772 If no arguments are passed then the current article in the currently
773 selected newsgroup is fetched.
774
775 C<MSGNUM> is a numeric id of an article in the current newsgroup, and
776 will change the current article pointer.  C<MSGID> is the message id of
777 an article as shown in that article's header.  It is anticipated that the
778 client will obtain the C<MSGID> from a list provided by the C<newnews>
779 command, from references contained within another article, or from the
780 message-id provided in the response to some other commands.
781
782 If there is an error then C<undef> will be returned.
783
784 =item body ( [ MSGID|MSGNUM ], [FH] )
785
786 Like C<article> but only fetches the body of the article.
787
788 =item head ( [ MSGID|MSGNUM ], [FH] )
789
790 Like C<article> but only fetches the headers for the article.
791
792 =item articlefh ( [ MSGID|MSGNUM ] )
793
794 =item bodyfh ( [ MSGID|MSGNUM ] )
795
796 =item headfh ( [ MSGID|MSGNUM ] )
797
798 These are similar to article(), body() and head(), but rather than
799 returning the requested data directly, they return a tied filehandle
800 from which to read the article.
801
802 =item nntpstat ( [ MSGID|MSGNUM ] )
803
804 The C<nntpstat> command is similar to the C<article> command except that no
805 text is returned.  When selecting by message number within a group,
806 the C<nntpstat> command serves to set the "current article pointer" without
807 sending text.
808
809 Using the C<nntpstat> command to
810 select by message-id is valid but of questionable value, since a
811 selection by message-id does B<not> alter the "current article pointer".
812
813 Returns the message-id of the "current article".
814
815 =item group ( [ GROUP ] )
816
817 Set and/or get the current group. If C<GROUP> is not given then information
818 is returned on the current group.
819
820 In a scalar context it returns the group name.
821
822 In an array context the return value is a list containing, the number
823 of articles in the group, the number of the first article, the number
824 of the last article and the group name.
825
826 =item ihave ( MSGID [, MESSAGE ])
827
828 The C<ihave> command informs the server that the client has an article
829 whose id is C<MSGID>.  If the server desires a copy of that
830 article, and C<MESSAGE> has been given the it will be sent.
831
832 Returns I<true> if the server desires the article and C<MESSAGE> was
833 successfully sent,if specified.
834
835 If C<MESSAGE> is not specified then the message must be sent using the
836 C<datasend> and C<dataend> methods from L<Net::Cmd>
837
838 C<MESSAGE> can be either an array of lines or a reference to an array.
839
840 =item last ()
841
842 Set the "current article pointer" to the previous article in the current
843 newsgroup.
844
845 Returns the message-id of the article.
846
847 =item date ()
848
849 Returns the date on the remote server. This date will be in a UNIX time
850 format (seconds since 1970)
851
852 =item postok ()
853
854 C<postok> will return I<true> if the servers initial response indicated
855 that it will allow posting.
856
857 =item authinfo ( USER, PASS )
858
859 Authenticates to the server (using AUTHINFO USER / AUTHINFO PASS)
860 using the supplied username and password.  Please note that the
861 password is sent in clear text to the server.  This command should not
862 be used with valuable passwords unless the connection to the server is
863 somehow protected.
864
865 =item list ()
866
867 Obtain information about all the active newsgroups. The results is a reference
868 to a hash where the key is a group name and each value is a reference to an
869 array. The elements in this array are:- the last article number in the group,
870 the first article number in the group and any information flags about the group.
871
872 =item newgroups ( SINCE [, DISTRIBUTIONS ])
873
874 C<SINCE> is a time value and C<DISTRIBUTIONS> is either a distribution
875 pattern or a reference to a list of distribution patterns.
876 The result is the same as C<list>, but the
877 groups return will be limited to those created after C<SINCE> and, if
878 specified, in one of the distribution areas in C<DISTRIBUTIONS>. 
879
880 =item newnews ( SINCE [, GROUPS [, DISTRIBUTIONS ]])
881
882 C<SINCE> is a time value. C<GROUPS> is either a group pattern or a reference
883 to a list of group patterns. C<DISTRIBUTIONS> is either a distribution
884 pattern or a reference to a list of distribution patterns.
885
886 Returns a reference to a list which contains the message-ids of all news posted
887 after C<SINCE>, that are in a groups which matched C<GROUPS> and a
888 distribution which matches C<DISTRIBUTIONS>.
889
890 =item next ()
891
892 Set the "current article pointer" to the next article in the current
893 newsgroup.
894
895 Returns the message-id of the article.
896
897 =item post ( [ MESSAGE ] )
898
899 Post a new article to the news server. If C<MESSAGE> is specified and posting
900 is allowed then the message will be sent.
901
902 If C<MESSAGE> is not specified then the message must be sent using the
903 C<datasend> and C<dataend> methods from L<Net::Cmd>
904
905 C<MESSAGE> can be either an array of lines or a reference to an array.
906
907 The message, either sent via C<datasend> or as the C<MESSAGE>
908 parameter, must be in the format as described by RFC822 and must
909 contain From:, Newsgroups: and Subject: headers.
910
911 =item postfh ()
912
913 Post a new article to the news server using a tied filehandle.  If
914 posting is allowed, this method will return a tied filehandle that you
915 can print() the contents of the article to be posted.  You must
916 explicitly close() the filehandle when you are finished posting the
917 article, and the return value from the close() call will indicate
918 whether the message was successfully posted.
919
920 =item slave ()
921
922 Tell the remote server that I am not a user client, but probably another
923 news server.
924
925 =item quit ()
926
927 Quit the remote server and close the socket connection.
928
929 =back
930
931 =head2 Extension methods
932
933 These methods use commands that are not part of the RFC977 documentation. Some
934 servers may not support all of them.
935
936 =over 4
937
938 =item newsgroups ( [ PATTERN ] )
939
940 Returns a reference to a hash where the keys are all the group names which
941 match C<PATTERN>, or all of the groups if no pattern is specified, and
942 each value contains the description text for the group.
943
944 =item distributions ()
945
946 Returns a reference to a hash where the keys are all the possible
947 distribution names and the values are the distribution descriptions.
948
949 =item subscriptions ()
950
951 Returns a reference to a list which contains a list of groups which
952 are recommended for a new user to subscribe to.
953
954 =item overview_fmt ()
955
956 Returns a reference to an array which contain the names of the fields returned
957 by C<xover>.
958
959 =item active_times ()
960
961 Returns a reference to a hash where the keys are the group names and each
962 value is a reference to an array containing the time the groups was created
963 and an identifier, possibly an Email address, of the creator.
964
965 =item active ( [ PATTERN ] )
966
967 Similar to C<list> but only active groups that match the pattern are returned.
968 C<PATTERN> can be a group pattern.
969
970 =item xgtitle ( PATTERN )
971
972 Returns a reference to a hash where the keys are all the group names which
973 match C<PATTERN> and each value is the description text for the group.
974
975 =item xhdr ( HEADER, MESSAGE-SPEC )
976
977 Obtain the header field C<HEADER> for all the messages specified. 
978
979 The return value will be a reference
980 to a hash where the keys are the message numbers and each value contains
981 the text of the requested header for that message.
982
983 =item xover ( MESSAGE-SPEC )
984
985 The return value will be a reference
986 to a hash where the keys are the message numbers and each value contains
987 a reference to an array which contains the overview fields for that
988 message.
989
990 The names of the fields can be obtained by calling C<overview_fmt>.
991
992 =item xpath ( MESSAGE-ID )
993
994 Returns the path name to the file on the server which contains the specified
995 message.
996
997 =item xpat ( HEADER, PATTERN, MESSAGE-SPEC)
998
999 The result is the same as C<xhdr> except the is will be restricted to
1000 headers where the text of the header matches C<PATTERN>
1001
1002 =item xrover
1003
1004 The XROVER command returns reference information for the article(s)
1005 specified.
1006
1007 Returns a reference to a HASH where the keys are the message numbers and the
1008 values are the References: lines from the articles
1009
1010 =item listgroup ( [ GROUP ] )
1011
1012 Returns a reference to a list of all the active messages in C<GROUP>, or
1013 the current group if C<GROUP> is not specified.
1014
1015 =item reader
1016
1017 Tell the server that you are a reader and not another server.
1018
1019 This is required by some servers. For example if you are connecting to
1020 an INN server and you have transfer permission your connection will
1021 be connected to the transfer daemon, not the NNTP daemon. Issuing
1022 this command will cause the transfer daemon to hand over control
1023 to the NNTP daemon.
1024
1025 Some servers do not understand this command, but issuing it and ignoring
1026 the response is harmless.
1027
1028 =back
1029
1030 =head1 UNSUPPORTED
1031
1032 The following NNTP command are unsupported by the package, and there are
1033 no plans to do so.
1034
1035     AUTHINFO GENERIC
1036     XTHREAD
1037     XSEARCH
1038     XINDEX
1039
1040 =head1 DEFINITIONS
1041
1042 =over 4
1043
1044 =item MESSAGE-SPEC
1045
1046 C<MESSAGE-SPEC> is either a single message-id, a single message number, or
1047 a reference to a list of two message numbers.
1048
1049 If C<MESSAGE-SPEC> is a reference to a list of two message numbers and the
1050 second number in a range is less than or equal to the first then the range
1051 represents all messages in the group after the first message number.
1052
1053 B<NOTE> For compatibility reasons only with earlier versions of Net::NNTP
1054 a message spec can be passed as a list of two numbers, this is deprecated
1055 and a reference to the list should now be passed
1056
1057 =item PATTERN
1058
1059 The C<NNTP> protocol uses the C<WILDMAT> format for patterns.
1060 The WILDMAT format was first developed by Rich Salz based on
1061 the format used in the UNIX "find" command to articulate
1062 file names. It was developed to provide a uniform mechanism
1063 for matching patterns in the same manner that the UNIX shell
1064 matches filenames.
1065
1066 Patterns are implicitly anchored at the
1067 beginning and end of each string when testing for a match.
1068
1069 There are five pattern matching operations other than a strict
1070 one-to-one match between the pattern and the source to be
1071 checked for a match.
1072
1073 The first is an asterisk C<*> to match any sequence of zero or more
1074 characters.
1075
1076 The second is a question mark C<?> to match any single character. The
1077 third specifies a specific set of characters.
1078
1079 The set is specified as a list of characters, or as a range of characters
1080 where the beginning and end of the range are separated by a minus (or dash)
1081 character, or as any combination of lists and ranges. The dash can
1082 also be included in the set as a character it if is the beginning
1083 or end of the set. This set is enclosed in square brackets. The
1084 close square bracket C<]> may be used in a set if it is the first
1085 character in the set.
1086
1087 The fourth operation is the same as the
1088 logical not of the third operation and is specified the same
1089 way as the third with the addition of a caret character C<^> at
1090 the beginning of the test string just inside the open square
1091 bracket.
1092
1093 The final operation uses the backslash character to
1094 invalidate the special meaning of an open square bracket C<[>,
1095 the asterisk, backslash or the question mark. Two backslashes in
1096 sequence will result in the evaluation of the backslash as a
1097 character with no special meaning.
1098
1099 =over 4
1100
1101 =item Examples
1102
1103 =item C<[^]-]>
1104
1105 matches any single character other than a close square
1106 bracket or a minus sign/dash.
1107
1108 =item C<*bdc>
1109
1110 matches any string that ends with the string "bdc"
1111 including the string "bdc" (without quotes).
1112
1113 =item C<[0-9a-zA-Z]>
1114
1115 matches any single printable alphanumeric ASCII character.
1116
1117 =item C<a??d>
1118
1119 matches any four character string which begins
1120 with a and ends with d.
1121
1122 =back
1123
1124 =back
1125
1126 =head1 SEE ALSO
1127
1128 L<Net::Cmd>
1129
1130 =head1 AUTHOR
1131
1132 Graham Barr <gbarr@pobox.com>
1133
1134 =head1 COPYRIGHT
1135
1136 Copyright (c) 1995-1997 Graham Barr. All rights reserved.
1137 This program is free software; you can redistribute it and/or modify
1138 it under the same terms as Perl itself.
1139
1140 =cut