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