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