File Coverage

blib/lib/NNML/Connection.pm
Criterion Covered Total %
statement 40 468 8.5
branch 0 158 0.0
condition 0 36 0.0
subroutine 13 54 24.0
pod 0 41 0.0
total 53 757 7.0


line stmt bran cond sub pod time code
1             # -*- Mode: Perl -*-
2             # Connection.pm --
3             # ITIID : $ITI$ $Header $__Header$
4             # Author : Ulrich Pfeifer
5             # Created On : Sat Sep 28 15:24:53 1996
6             # Last Modified By: Ulrich Pfeifer
7             # Last Modified On: Mon Mar 31 09:19:23 1997
8             # Language : CPerl
9             # Update Count : 331
10             # Status : Unknown, Use with caution!
11             #
12             # (C) Copyright 1996, Universität Dortmund, all rights reserved.
13             #
14              
15             package NNML::Connection;
16 1     1   606 use NNML::Active qw($ACTIVE);
  1         3  
  1         148  
17 1     1   5 use NNML::Config qw($Config);
  1         2  
  1         76  
18 1     1   1336 use Text::Abbrev;
  1         220  
  1         65  
19 1     1   1169 use Time::Local;
  1         2012  
  1         77  
20 1     1   1817 use Socket;
  1         4924  
  1         746  
21 1     1   13 use strict;
  1         2  
  1         40  
22 1     1   926 use Sys::Hostname;
  1         1234  
  1         54  
23 1     1   832 use IO::Select;
  1         1711  
  1         61  
24              
25             require NNML::Auth;
26              
27 1     1   7 use vars qw(%ACMD %CMD %MSG %HELP);
  1         1  
  1         83  
28              
29             my $HOST = hostname;
30             {
31 1     1   6 no strict;
  1         2  
  1         505  
32             local *stab = *NNML::Connection::;
33             my ($key,$val);
34             while (($key,$val) = each(%stab)) {
35             next unless $key =~ /^cmd_(.*)/;
36             local(*ENTRY) = $val;
37             if (defined &ENTRY) {
38             $CMD{$1} = \&ENTRY;
39             }
40             }
41             }
42              
43             abbrev(*ACMD, keys %CMD);
44              
45             sub new {
46 0     0 0   my $type = shift;
47 0           my $fh = shift;
48 0           my $msg = shift;
49 0           my $self = {_fh => $fh};
50            
51 0           my $hersockaddr = $fh->peername();
52 0           my ($port, $iaddr) = unpack_sockaddr_in($hersockaddr);
53 0           my $peer = gethostbyaddr($iaddr, AF_INET);
54 0           $self->{_peer} = $peer;
55 0           $self->{_user} = 'nobody';
56 0           $self->{_passwd} = '*';
57 0           print "Connection from $peer\n";
58 0           bless $self, $type;
59 0           $self->msg(200, $msg);
60 0           $self;
61             }
62              
63             sub close {
64 0     0 0   my $self = shift;
65              
66 0           $self->{_fh}->close;
67             }
68              
69             sub dispatch {
70 0     0 0   my $self = shift;
71 0           my $cmd = shift;
72              
73 0           print "$cmd @_\n";
74 0 0         unless (exists $ACMD{$cmd}) {
75 0           $self->msg(500);
76             } else {
77 0 0         if (NNML::Auth::perm($self, $ACMD{$cmd})) {
78 0           &{$CMD{$ACMD{$cmd}}}($self, @_);
  0            
79             } else {
80 0           $self->msg(480);
81             }
82             }
83 0           return $ACMD{$cmd};
84             }
85              
86             sub msg {
87 0     0 0   my $self = shift;
88 0           my $code = shift;
89 0   0       my $msg = $MSG{$code} || '';
90 0           printf("%03d $msg\r\n", $code, @_);
91 0           $self->{_fh}->datasend(sprintf "%03d $msg\r\n", $code, @_);
92             }
93              
94             sub end {
95 0     0 0   my $self = shift;
96 0           $self->{_fh}->dataend;
97             }
98              
99 1     1   946 use IO::Pipe;
  1         1319  
  1         35  
100 1     1   8 use IO::File;
  1         3  
  1         1342  
101              
102             sub output {
103 0     0 0   my $self = shift;
104              
105 0           $self->{_fh}->datasend(@_);
106             }
107              
108            
109             sub cmd_help {
110 0     0 0   my $self = shift;
111              
112 0           $self->msg(100);
113 0           for (sort keys %CMD) {
114 0   0       $self->output(sprintf("%-15s %s\r\n", $_, $HELP{$_}||''));
115             }
116 0           $self->end;
117             }
118              
119             sub cmd_authinfo {
120 0     0 0   my ($self, $cmd, $arg) = @_;
121              
122 0 0         if (uc($cmd) eq 'USER') {
    0          
123 0           $self->{_user} = $arg;
124 0 0 0       unless (exists $self->{_passwd} and $self->{_passwd} ne '*') {
125 0           $self->msg(381);
126 0           return;
127             }
128             } elsif (uc($cmd) eq 'PASS') {
129 0           $self->{_passwd} = $arg;
130 0 0 0       unless (exists $self->{_user} and $self->{_user} ne 'nobody') {
131 0           $self->msg(382);
132 0           return;
133             }
134             } else {
135 0           $self->msg(501);
136 0           return;
137             }
138            
139 0 0         if (NNML::Auth::check($self->{_user}, $self->{_passwd})) {
140 0           $self->msg(281)
141             } else {
142 0           $self->msg(482);
143 0           delete $self->{_passwd};
144             }
145             }
146              
147             sub cmd_group {
148 0     0 0   my ($self, $groupname) = @_;
149 0           my $group = $ACTIVE->group($groupname);
150              
151 0 0         unless ($group) {
152 0           $self->msg(411);
153 0           return;
154             }
155 0           my $max = $group->max;
156 0           my $min = $group->min;
157              
158 0           $self->{_group} = $group;
159 0           $self->{_article} = $min;
160 0           $self->msg(211, $max-$min+1, $min, $max, $groupname);
161             }
162              
163             sub cmd_mode {
164 0     0 0   my $self = shift;
165 0           my $mode = uc shift;
166              
167 0           $self->msg(280, $mode);
168             }
169              
170             sub cmd_quit {
171 0     0 0   my $self = shift;
172 0           $self->msg(205);
173             }
174              
175             sub cmd_list {
176 0     0 0   my $self = shift;
177              
178 0 0         if (@_) {
179 0           my $cmd = shift;
180 0           my $match = shift;
181            
182 0 0         if ($cmd !~ /NEWSGROUPS/) {
183 0           $self->msg(500);
184 0           return;
185             }
186 0           $self->msg(215);
187 0           for ($ACTIVE->list_match($match)) {
188 0           $self->output($_->name, "\r\n");
189             }
190 0           $self->end;
191             } else {
192 0           $self->msg(215);
193 0           for ($ACTIVE->groups) {
194 0           $self->output(sprintf "%s %d %d %s\r\n",
195             $_->name, $_->max, $_->min, $_->post)
196             }
197 0           $self->end;
198             }
199             }
200              
201             sub cmd_newgroups {
202 0     0 0   my $self = shift;
203 0           my $ltime = to_time(@_);
204            
205 0 0         unless (defined $ltime) {
206 0           $self->msg(501);
207 0           return;
208             }
209              
210 0           $self->msg(231);
211 0           for ($ACTIVE->newgroups($ltime)) {
212 0           $self->output($_, "\r\n");
213             }
214 0           $self->end;
215             }
216              
217             sub cmd_newnews {
218 0     0 0   my $self = shift;
219 0           my $match = shift;
220 0           my $ltime = to_time(@_);
221 0           my %msgid;
222            
223 0           $self->msg(230);
224 0           for ($ACTIVE->list_match($match)) {
225 0           my %new = $_->newnews($ltime);
226 0           for (keys %new) {
227 0   0       $msgid{$_} ||= $new{$_};
228             }
229             }
230 0           for (sort {$msgid{$a} <=> $msgid{$b}} keys %msgid) {
  0            
231 0           $self->output($_, "\r\n");
232             }
233 0           $self->end;
234             }
235              
236             sub cmd_xover {
237 0     0 0   my $self = shift;
238 0           my $parm = shift;
239 0           my @range = ($parm =~ m/(\d+)-(\d+)/);
240 0 0         unless ($self->{_group}) {
241 0           $self->msg(412);
242 0           return;
243             }
244 0           my $xover = $self->{_group}->xover(@range);
245 0           $self->msg(224);
246 0           $self->output("$xover");
247 0           $self->end;
248             }
249              
250              
251             my %FLD;
252              
253             BEGIN {
254 1     1   2 my $i;
255              
256 1         12 my @FLD = qw(ano subject from date message-id references size lines xref);
257              
258 1         6 for ($i=0;$i<@FLD;$i++) {
259 9         4112 $FLD{$FLD[$i]} = $i;
260             }
261             }
262              
263             sub cmd_xhdr {
264 0     0 0   my $self = shift;
265 0           my $fld = shift;
266 0           my $fno = $FLD{lc $fld};
267 0           my $parm = shift;
268 0   0       my @range = ($parm =~ m/(\d+)-(\d+)/ || ($parm, $parm));
269 0 0         unless ($self->{_group}) {
270 0           $self->msg(412);
271 0           return;
272             }
273 0           my $xover = $self->{_group}->xover(@range);
274 0           $self->msg(221, $fld);
275 0           for (split /\n/, $xover) {
276 0           my ($ano, $val) = (split /\t/, $_)[0,$fno];
277 0 0         $val = "(none)" unless $val;
278              
279 0           $self->output("$ano $val\r\n");
280             }
281 0           $self->end;
282             }
283              
284             sub cmd_next {
285 0     0 0   my $self = shift;
286 0 0         unless ($self->{_group}) {
287 0           $self->msg(412);
288 0           return;
289             }
290 0 0         unless ($self->{_article}) {
291 0           $self->msg(420);
292 0           return;
293             }
294 0 0         if ($self->{_article} < $self->{_group}->max) {
295 0           $self->{_article}++;
296             } else {
297 0           $self->msg(421);
298 0           return;
299             }
300 0           $self->msg(223, $self->{_article},
301             $self->{_group}->article_by_no($self->{_article}))
302             }
303              
304             sub cmd_last {
305 0     0 0   my $self = shift;
306 0 0         unless ($self->{_group}) {
307 0           $self->msg(412);
308 0           return;
309             }
310 0 0         unless ($self->{_article}) {
311 0           $self->msg(420);
312 0           return;
313             }
314 0 0         if ($self->{_article} > $self->{_group}->min) {
315 0           $self->{_article}--;
316             } else {
317 0           $self->msg(422);
318 0           return;
319             }
320 0           $self->msg(223, $self->{_article},
321             $self->{_group}->article_by_no($self->{_article}))
322             }
323              
324             sub cmd_slave {
325 0     0 0   my $self = shift;
326 0           $self->{timeout} = $Config->mirror_timeout;
327 0           $self->{slave} = 1;
328 0           $self->msg(202);
329             }
330              
331             # only article number for is supported
332             sub cmd_stat {
333 0     0 0   my $self = shift;
334 0           my $ano = shift;
335              
336 0 0         unless (defined $ano) {
337 0           $self->msg(501);
338 0           return;
339             }
340 0 0         unless ($self->{_group}) {
341 0           $self->msg(412);
342 0           return;
343             }
344 0 0 0       if ($ano >= $self->{_group}->min and $ano <= $self->{_group}->max) {
345 0           $self->{_article} = $ano;
346             } else {
347 0           $self->msg(423, $self->{_group}->name);
348 0           return;
349             }
350 0           $self->msg(223, $self->{_article},
351             $self->{_group}->article_by_no($self->{_article}))
352             }
353              
354             sub cmd_xdelete {
355 0     0 0   my $self = shift;
356 0   0       my $ano = shift || $self->{_article};
357              
358 0 0         unless (defined $ano) {
359 0           $self->msg(501);
360 0           return;
361             }
362 0 0         unless ($self->{_group}) {
363 0           $self->msg(412);
364 0           return;
365             }
366 0 0         if ($self->{_group}->delete($ano)) {
367 0           $self->msg(285);
368             } else {
369 0           $self->msg(485);
370             }
371             }
372              
373             sub cmd_xdeletegroup {
374 0     0 0   my $self = shift;
375              
376 0 0         unless ($self->{_group}) {
377 0           $self->msg(412);
378 0           return;
379             }
380 0 0         if ($ACTIVE->delete_group($self->{_group}->name)) {
381 0           $self->msg(286);
382             } else {
383 0           $self->msg(486);
384             }
385             }
386              
387             sub cmd_xmovefrom {
388 0     0 0   my $self = shift;
389 0   0       my $ano = shift || $self->{_article};
390              
391 0 0         unless ($self->{_group}) {
392 0           $self->msg(412);
393 0           return;
394             }
395              
396 0 0         unless ($ano) {
397 0           $self->msg(420);
398 0           return;
399             }
400              
401 0           my ($head, $body) = $self->{_group}->get($ano);
402 0 0         unless ($head) {
403 0           $self->msg(423, $self->{_group}->name);
404 0           return;
405             }
406 0 0         unless ($self->{_group}->delete($ano)) {
407 0           $self->msg(285);
408 0           return;
409             }
410 0           my ($msgid) = ($head =~ /^Message-Id:\s*(<\S+>)/m);
411 0           $self->msg(220,$ano, $msgid);
412 0           $self->output($head, "\n", $body);
413             }
414              
415             sub cmd_xaccept {
416 0     0 0   my $self = shift;
417              
418 0 0         unless ($self->{_group}) {
419 0           $self->msg(412);
420 0           return;
421             }
422            
423 0 0         unless ($self->post) {
424 0           $self->msg(440);
425 0           return;
426             }
427 0           $self->msg(340);
428 0           $self->accept_article(undef,$self->{_group}->name);
429             }
430              
431 0     0 0   sub cmd_article { my $self = shift; $self->article('article', join ' ', @_)};
  0            
432 0     0 0   sub cmd_head { my $self = shift; $self->article('head', join ' ', @_)};
  0            
433 0     0 0   sub cmd_body { my $self = shift; $self->article('body', join ' ', @_)};
  0            
434 0     0 0   sub cmd_xdate { my $self = shift; $self->article('date', join ' ', @_)};
  0            
435              
436             sub article {
437 0     0 0   my ($self, $cmd, $parm) = @_;
438 0 0 0       if (defined $parm and $parm =~ /^\s*<.*>\s*$/) {
439 0           my ($head, $body) = article_msgid($parm);
440 0 0         if ($head) {
441 0 0         if ($cmd eq 'article') {
    0          
442 0           $self->msg(220,0,$parm);
443 0           $self->output($head, "\n", $body);
444             } elsif ($cmd eq 'head') {
445 0           $self->msg(225,0,$parm);
446 0           $self->output($head);
447             } else {
448 0           $self->msg(222,0,$parm);
449 0           $self->output($body);
450             }
451 0           $self->end;
452             } else {
453 0           $self->msg(430);
454             }
455             } else {
456 0 0         unless ($self->{_group}) {
457 0           $self->msg(412);
458 0           return;
459             }
460 0   0       my $ano = $parm || $self->{_article};
461 0 0         unless ($ano =~ /^\d+$/) {
462 0           $self->msg(420);
463 0           return;
464             }
465              
466 0           my ($head, $body, $date) = $self->{_group}->get($ano);
467 0           my ($msgid) = ($head =~ /^Message-Id:\s*(<\S+>)/im);
468              
469             { # fake nnml header
470 0           my %ano = msgid_to_anos($msgid);
  0            
471 0           my @newsgroups = keys %ano;
472 0           $head =~ s/^X-nnml-groups:.*\n//mig;
473 0           my $newsgroups = sprintf("X-nnml-groups: %s\n", join(', ', @newsgroups));
474 0           $head .= $newsgroups;
475             }
476            
477 0 0         if ($body) {
478 0           $self->{_article} = $ano;
479 0 0         if ($cmd eq 'article') {
    0          
    0          
480 0           $self->msg(220,$ano, $msgid);
481 0           $self->output($head, "\n", $body);
482             } elsif ($cmd eq 'head') {
483 0           $self->msg(225,$ano, $msgid);
484 0           $self->output($head);
485             } elsif ($cmd eq 'date') {
486 0           $self->msg(288,$date >> 16, $date & 0xfffff, $ano, $msgid);
487 0           return;
488             } else {
489 0           $self->msg(222,$ano, $msgid);
490 0           $self->output($body);
491             }
492 0           $self->end;
493             } else {
494 0           $self->msg(423, $self->{_group}->name);
495             }
496             }
497             }
498              
499 0     0 0   sub post {1;} # tbs
500              
501             sub cmd_ihave {
502 0     0 0   my ($self, $msgid) = @_;
503              
504 0 0         unless ($self->post) {
505 0           $self->msg(437);
506 0           return;
507             }
508 0 0         if (article_msgid($msgid)) {
509 0           $self->msg(435);
510 0           return;
511             }
512 0           $self->msg(335);
513 0           $self->accept_article($msgid);
514             }
515              
516             sub cmd_post {
517 0     0 0   my $self = shift;
518              
519 0 0         unless ($self->post) {
520 0           $self->msg(440);
521 0           return;
522             }
523 0           $self->msg(340);
524 0           $self->accept_article();
525             }
526              
527            
528             sub accept_article { # $extra_group also allows overwriting
529 0     0 0   my ($self, $msgid, $extra_group) = @_;
530 0           my $art;
531              
532 0 0         if ($art = $self->{_fh}->read_until_dot()) {
533 0           $art = join '', @$art;
534             } else { # won't work?
535 0           print "accept_article() timed out\n";
536 0           $self->msg(441);
537 0           return;
538             }
539 0           my $create = NNML::Auth::perm($self,'create');
540              
541 0 0         if ($self->{slave}) {
542 0           $self->msg(spool_article($Config->spool, $art, $msgid,
543             $extra_group, $create));
544             } else {
545 0           my ($code, @msg) = inject_article($art, $msgid, $extra_group, $create);
546 0 0         unless ($code =~ /^2/) {
547 0           spool_article($Config->bad, $art, $msgid, $extra_group, $create);
548             }
549 0           $self->msg($code, @msg);
550             }
551             }
552              
553             sub spool_article {
554 0     0 0   my ($spool, $art, $msgid, $extra_group, $create) = @_;
555 0           my $sf = new IO::File ">> $spool";
556              
557 0 0         if ($sf) {
558 0           $sf->printf("$;$;$;$;\t%s\t%s\t%d\n", $msgid, $extra_group, $create);
559 0           $sf->print($art);
560 0           return(240);
561             } else {
562 0           return(441, "Could not spool article: $!")
563             }
564             }
565              
566             sub cmd_xunspool { # 289 %d/%d articles unspooled
567 0     0 0   my $self = shift;
568              
569 0 0         unless (NNML::Auth::perm($self,'create')) {
570 0           $self->msg(480, "'Need create power'");
571 0           return;
572             }
573 0           my ($no_art, $bad) = NNML::Server::unspool();
574 0           $self->msg(289, $no_art, $no_art-$bad);
575             }
576              
577             sub NNML::Server::unspool {
578 0     0 0   my ($no_art, $bad);
579 0           my $spool = $Config->spool;
580 0           my $sf = new IO::File "< $spool";
581              
582 0           NNML::Auth::_update(); # just for the message
583 0           NNML::Active::_update(); # just to make sure
584 0 0         if ($sf) {
585 0           local $/ = "$;$;$;$;\t";
586 0           my $ent;
587            
588 0           while (defined ($ent = <$sf>)) {
589 0           chomp($ent);
590 0 0         next unless $ent;
591 0           my($ctl, $art) = split /\n/, $ent, 2;
592 0           my ($msgid, $extra_group, $create) = split /\t/, $ctl;
593              
594 0           $no_art++;
595 0           my ($code, @msg) = inject_article($art, $msgid, $extra_group, $create);
596 0 0         unless ($code =~ /^2/) {
597 0           spool_article($Config->bad, $art, $msgid, $extra_group, $create);
598 0           $bad++;
599             }
600             }
601 0           $sf->close;
602 0 0         rename $spool, "$spool~"
603             or warn "Could not rename '$spool': $!\n";
604             }
605 0           return($no_art, $bad);
606             }
607              
608             sub inject_article {
609 0     0 0   my ($art, $msgid, $extra_group, $create) = @_;
610 0   0       my %head = (
611             subject => '',
612             from => '',
613             date => '',
614             'message-id' => $msgid || '',
615             references => '',
616             lines => 0,
617             xref => '',
618             'x-nnml-groups' => '',
619             newsgroups => '',
620             );
621 0           my $header;
622              
623             # done by Net::Cmd now
624             #$art =~ s/\.\r?\n$//;
625             #$art =~ s/\r//g;
626             #$art =~ s/^\.\././mg;
627              
628 0           my ($head, $body) = split /^$/m, $art, 2;
629              
630 0           my $headcopy = $head;
631 0           $headcopy =~ s{\s*\n\s+}{ }g; # fold continue lines
632 0           my ($fron, %thead) = split /^(\S+):/m, $headcopy;
633 0           for (keys %thead) {
634 0           my $val = $thead{$_};
635 0           $val =~ s/\s/ /;
636 0           $val =~ s/^\s+//;
637 0           $val =~ s/\s+$//;
638 0 0         $head{lc $_} = $val if exists $head{lc $_};
639             }
640 0 0         unless ($head{lines}) {
641 0           $head{lines} = ($body =~ m/(\n)/g);
642             }
643 0 0         unless ($head{'message-id'}) {
644 0           $head{'message-id'} = sprintf "<%d\@unknown%s>", time, $HOST;
645 0           $head .= "Message-Id: $head{'message-id'}\n";
646             } else {
647 0           $head{'message-id'} =~ s/^\s+//;
648 0           $head{'message-id'} =~ s/\s+$//;
649             }
650 0           for (keys %head) {
651 0 0         printf "%-15s %s\n", $_, $head{$_} if $head{$_};
652             }
653 0           my @newsgroups = split /,\s*/, $head{'x-nnml-groups'};
654 0 0         unless (@newsgroups) {
655 0           @newsgroups = split /,\s*/, $head{newsgroups};
656             }
657              
658 0           my $file;
659 0 0         if ($extra_group) {
660 0           my %all = msgid_to_anos($head{'message-id'});
661 0           @newsgroups = keys %all;
662 0           for (@newsgroups) {
663 0           my $any = $newsgroups[0];
664 0           my $group = $ACTIVE->group($any);
665 0           my $dir = $group->dir;
666 0 0         if (-f "$dir/$all{$any}") {
667 0           $file = "$dir/$all{$any}";
668 0           last;
669             }
670             }
671 0 0         push @newsgroups, $extra_group unless exists $all{$extra_group};
672             }
673 0 0         unless (@newsgroups) {
674 0           return(441, "No newsgroups specified");
675             }
676 0 0 0       if (!$extra_group and article_msgid($head{'message-id'})) {
677 0           print "POSTER lied about 'message-id'}\n";
678 0           return(441, "alreday have $head{'message-id'}");
679             }
680              
681 0 0         unless ($ACTIVE->accept_article(\%head, $head, $body, $create, $file,
682             $extra_group,
683             @newsgroups)) {
684 0           return(441, "Something went wrong");
685             }
686 0 0         if ($extra_group) {
687 0           my %all = msgid_to_anos($head{'message-id'});
688 0 0         if ($all{$extra_group}) {
689 0           return(287,$all{$extra_group},$extra_group);
690             } else {
691 0           return(441, "Article '$head{'message-id'}' not arrived in $extra_group");
692             }
693             } else {
694 0           return(240);
695             }
696             }
697              
698             sub article_msgid {
699 0     0 0   my $msgid = shift;
700 0           my ($groupname);
701 0           my %ano = msgid_to_anos($msgid);
702 0           my @newsgroups = keys %ano;
703 0           my ($head, $body);
704            
705 0           for $groupname (@newsgroups) {
706 0           my $group = $ACTIVE->group($groupname);
707              
708 0           ($head, $body) = $group->get($ano{$groupname});
709 0 0         last if defined $head;
710             }
711 0 0         return unless $head;
712 0           $head =~ s/^X-nnml-groups:.*\n//mig;
713 0           my $newsgroups = sprintf("X-nnml-groups: %s\n", join(', ', @newsgroups));
714 0           return $head . $newsgroups, $body;
715             }
716              
717             sub msgid_to_anos {
718 0     0 0   my $msgid = shift;
719 0           my $group;
720             my %ano;
721 0           for $group ($ACTIVE->groups) {
722 0           my $ano = $group->article_by_id($msgid);
723 0 0         if (defined $ano) {
724 0           $ano{$group->name} = $ano;
725             }
726             }
727 0           %ano;
728             }
729              
730             sub cmd_xtest {
731 0     0 0   my ($self,$msgid) = @_;
732 0           my %anos = msgid_to_anos(@_);
733 0           my ($grp, $ano);
734              
735 0           while (($grp, $ano) = each %anos) {
736 0           printf "%s %d\n", $grp, $anos{$grp};
737             }
738             }
739              
740             sub to_time {
741 0     0 0   my ($date, $time, $gmt) = @_;
742              
743 0 0         return unless defined $date;
744 0 0         if (length($date)<8) {
745 0           $date =~ m/^(\d\d)/;
746 0 0         if ($1 > 30) {
747 0           $date = "19$date"; # not strictly RCS 977
748             } else {
749 0           $date = "20$date"; # not strictly RCS 977
750             }
751             }
752 0 0         unless (defined $time) {
753 0           $time = "000000";
754             }
755              
756 0           $date .= $time;
757 0           my ($year,$mon,$mday,$hours,$min,$sec) =
758             ($date =~ m/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/);
759 0 0         return unless defined $sec;
760              
761 0           my $ltime;
762 0           $mon--;
763 0 0         if (defined $gmt) {
764 0           eval { $ltime = timegm($sec,$min,$hours,$mday,$mon,$year) };
  0            
765             } else {
766 0           eval { $ltime = timelocal($sec,$min,$hours,$mday,$mon,$year)};
  0            
767             }
768 0 0         return if $@ ne '';
769 0           return $ltime;
770             }
771              
772            
773             # read status messages
774             my $line;
775             while (defined ($line = )) {
776             chomp($line);
777             my ($cmd, $msg) = split ' ', $line, 2;
778             last unless $cmd;
779             $HELP{$cmd} = $msg;
780             }
781             while (defined ($line = )) {
782             chomp($line);
783             next unless $line =~ /^\d/;
784             my ($code, $msg) = split ' ', $line, 2;
785             $MSG{$code} = $msg;
786             }
787              
788              
789             1;
790              
791             __DATA__