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