X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FNNTP.pm;h=1f9058ca2b886604a99c923200bc36b974fa23b6;hb=0b3d2b151965aad70ca90f642edf0059c29e6855;hp=9df47133f0c99973544ba37957f31e5ce665e160;hpb=9c42ece6dfddf4156dc3016e2fa8835bf3d8aca1;p=public-inbox.git diff --git a/lib/PublicInbox/NNTP.pm b/lib/PublicInbox/NNTP.pm index 9df47133..1f9058ca 100644 --- a/lib/PublicInbox/NNTP.pm +++ b/lib/PublicInbox/NNTP.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2021 all contributors +# Copyright (C) all contributors # License: AGPL-3.0+ # # Each instance of this represents a NNTP client socket @@ -9,6 +9,7 @@ # long_cb: long_response private data package PublicInbox::NNTP; use strict; +use v5.10.1; use parent qw(PublicInbox::DS); use PublicInbox::MID qw(mid_escape $MID_EXTRACT); use PublicInbox::Eml; @@ -23,9 +24,9 @@ use constant { LINE_MAX => 512, # RFC 977 section 2.3 r501 => '501 command syntax error', r502 => '502 Command unavailable', - r221 => '221 Header follows', + r221 => "221 Header follows\r\n", r224 => '224 Overview information follows (multi-line)', - r225 => '225 Headers follow (multi-line)', + r225 => "225 Headers follow (multi-line)\r\n", r430 => '430 No article with that message-id', }; use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT); @@ -64,14 +65,13 @@ sub new ($$$) { } else { greet($self); } - $self->update_idle_time; $self; } sub args_ok ($$) { my ($cb, $argc) = @_; my $tot = prototype $cb; - my ($nreq, undef) = split(';', $tot); + my ($nreq, undef) = split(/;/, $tot); $nreq = ($nreq =~ tr/$//) - 1; $tot = ($tot =~ tr/$//) - 1; ($argc <= $tot && $argc >= $nreq); @@ -82,8 +82,8 @@ sub process_line ($$) { my ($self, $l) = @_; my ($req, @args) = split(/[ \t]+/, $l); return 1 unless defined($req); # skip blank line - $req = $self->can('cmd_'.lc($req)); - return res($self, '500 command not recognized') unless $req; + $req = $self->can('cmd_'.lc($req)) // + return $self->write(\"500 command not recognized\r\n"); return res($self, r501) unless args_ok($req, scalar @args); my $res = eval { $req->($self, @args) }; @@ -202,7 +202,7 @@ sub cmd_list ($;$$) { sub listgroup_range_i { my ($self, $beg, $end) = @_; - my $r = $self->{ibx}->mm->msg_range($beg, $end, 'num'); + my $r = $self->{ibx}->mm(1)->msg_range($beg, $end, 'num'); scalar(@$r) or return; $self->msg_more(join('', map { "$_->[0]\r\n" } @$r)); 1; @@ -210,7 +210,7 @@ sub listgroup_range_i { sub listgroup_all_i { my ($self, $num) = @_; - my $ary = $self->{ibx}->mm->ids_after($num); + my $ary = $self->{ibx}->over(1)->ids_after($num); scalar(@$ary) or return; more($self, join("\r\n", @$ary)); 1; @@ -241,7 +241,7 @@ sub parse_time ($$;$) { $gmt = 1; } my ($YYYY, $MM, $DD); - if (bytes::length($date) == 8) { # RFC 3977 allows YYYYMMDD + if (length($date) == 8) { # RFC 3977 allows YYYYMMDD ($YYYY, $MM, $DD) = unpack('A4A2A2', $date); } else { # legacy clients send YYMMDD my $YY; @@ -261,7 +261,7 @@ sub parse_time ($$;$) { sub group_line ($$) { my ($self, $ibx) = @_; - my ($min, $max) = $ibx->mm->minmax; + my ($min, $max) = $ibx->mm(1)->minmax; more($self, "$ibx->{newsgroup} $max $min n"); } @@ -349,7 +349,7 @@ sub cmd_newnews ($$$$;$$) { my $ts = eval { parse_time($date, $time, $gmt) }; return r501 if $@; more($self, '230 list of new articles by message-id follows'); - my ($keep, $skip) = split('!', $newsgroups, 2); + my ($keep, $skip) = split(/!/, $newsgroups, 2); ngpat2re($keep); ngpat2re($skip); my @names = grep(!/$skip/, grep(/$keep/, @@ -367,7 +367,7 @@ sub cmd_group ($$) { $nntpd->idler_start; $self->{ibx} = $ibx; - my ($min, $max) = $ibx->mm->minmax; + my ($min, $max) = $ibx->mm(1)->minmax; $self->{article} = $min; my $est_size = $max - $min; "211 $est_size $min $max $group"; @@ -381,11 +381,10 @@ sub article_adj ($$) { defined $n or return '420 no current article has been selected'; $n += $off; - my $mid = $ibx->mm->mid_for($n); - unless ($mid) { + my $mid = $ibx->mm(1)->mid_for($n) // do { $n = $off > 0 ? 'next' : 'previous'; return "421 no $n article in this group"; - } + }; $self->{article} = $n; "223 $n <$mid> article retrieved - request text separately"; } @@ -404,7 +403,7 @@ sub cmd_post ($) { sub cmd_quit ($) { my ($self) = @_; - res($self, '205 closing connection - goodbye!'); + $self->write(\"205 closing connection - goodbye!\r\n"); $self->shutdn; undef; } @@ -418,9 +417,8 @@ sub xref_by_tc ($$$) { $by_addr->{lc($_)} // () } (PublicInbox::Address::emails($smsg->{$f} // '')); for my $ibx (@ibxs) { - my $ngname = $ibx->{newsgroup} // next; - next if defined $xref->{$ngname}; - $xref->{$ngname} = eval { $ibx->mm->num_for($mid) }; + $xref->{$ibx->{newsgroup}} //= + $ibx->mm(1)->num_for($mid); } } } @@ -437,13 +435,15 @@ sub xref ($$$) { $xref = { $cur_ng => $smsg->{num} }; my $mid = $smsg->{mid}; for my $ibx (values %{$nntpd->{pi_cfg}->{-by_newsgroup}}) { - next if defined($xref->{$ibx->{newsgroup}}); - my $num = eval { $ibx->mm->num_for($mid) } // next; - $xref->{$ibx->{newsgroup}} = $num; + $xref->{$ibx->{newsgroup}} //= + $ibx->mm(1)->num_for($mid); } } my $ret = "$nntpd->{servername} $cur_ng:".delete($xref->{$cur_ng}); - $ret .= " $_:$xref->{$_}" for (sort keys %$xref); + for my $ng (sort keys %$xref) { + my $num = $xref->{$ng} // next; + $ret .= " $ng:$num"; + } $ret; } @@ -504,7 +504,7 @@ find_ibx: return '412 no newsgroup has been selected'; } found: - my $smsg = $ibx->over->get_art($n) or return $err; + my $smsg = $ibx->over(1)->get_art($n) or return $err; $smsg->{-ibx} = $ibx; if ($code == 223) { # STAT set_art($self, $n); @@ -618,7 +618,7 @@ sub get_range ($$) { my $ibx = $self->{ibx} or return '412 no news group has been selected'; defined $range or return '420 No article(s) selected'; my ($beg, $end); - my ($min, $max) = $ibx->mm->minmax; + my ($min, $max) = $ibx->mm(1)->minmax; if ($range =~ /\A([0-9]+)\z/) { $beg = $end = $1; } elsif ($range =~ /\A([0-9]+)-\z/) { @@ -650,8 +650,6 @@ sub long_step { out($self, " deferred[$fd] aborted - %0.6f", $elapsed); $self->close; } elsif ($more) { # $self->{wbuf}: - $self->update_idle_time; - # COMPRESS users all share the same DEFLATE context. # Flush it here to ensure clients don't see # each other's data @@ -665,7 +663,7 @@ sub long_step { $self->requeue if $new_size == 1; } else { # all done! delete $self->{long_cb}; - res($self, '.'); + $self->write(\".\r\n"); my $elapsed = now() - $t0; my $fd = fileno($self->{sock}); out($self, " deferred[$fd] done - %0.6f", $elapsed); @@ -688,7 +686,7 @@ sub long_response ($$;@) { sub hdr_msgid_range_i { my ($self, $beg, $end) = @_; - my $r = $self->{ibx}->mm->msg_range($beg, $end); + my $r = $self->{ibx}->mm(1)->msg_range($beg, $end); @$r or return; $self->msg_more(join('', map { "$_->[0] <$_->[1]>\r\n" } @$r)); 1; @@ -705,7 +703,7 @@ sub hdr_message_id ($$$) { # optimize XHDR Message-ID [range] for slrnpull. $range = $self->{article} unless defined $range; my $r = get_range($self, $range); return $r unless ref $r; - more($self, $xhdr ? r221 : r225); + $self->msg_more($xhdr ? r221 : r225); long_response($self, \&hdr_msgid_range_i, @$r); } } @@ -714,7 +712,7 @@ sub mid_lookup ($$) { my ($self, $mid) = @_; my $cur_ibx = $self->{ibx}; if ($cur_ibx) { - my $n = $cur_ibx->mm->num_for($mid); + my $n = $cur_ibx->mm(1)->num_for($mid); return ($cur_ibx, $n) if defined $n; } my $pi_cfg = $self->{nntpd}->{pi_cfg}; @@ -744,7 +742,7 @@ EOF } else { # slow path for non-ALL users for my $ibx (values %{$pi_cfg->{-by_newsgroup}}) { next if defined $cur_ibx && $ibx eq $cur_ibx; - my $n = $ibx->mm->num_for($mid); + my $n = $ibx->mm(1)->num_for($mid); return ($ibx, $n) if defined $n; } } @@ -754,7 +752,7 @@ EOF sub xref_range_i { my ($self, $beg, $end) = @_; my $ibx = $self->{ibx}; - my $msgs = $ibx->over->query_xover($$beg, $end); + my $msgs = $ibx->over(1)->query_xover($$beg, $end); scalar(@$msgs) or return; $$beg = $msgs->[-1]->{num} + 1; $self->msg_more(join('', map { @@ -770,29 +768,28 @@ sub hdr_xref ($$$) { # optimize XHDR Xref [range] for rtin my $mid = $1; my ($ibx, $n) = mid_lookup($self, $mid); return r430 unless $n; - my $smsg = $ibx->over->get_art($n) or return; + my $smsg = $ibx->over(1)->get_art($n) or return; hdr_mid_response($self, $xhdr, $ibx, $n, $range, xref($self, $ibx, $smsg)); } else { # numeric range $range = $self->{article} unless defined $range; my $r = get_range($self, $range); return $r unless ref $r; - more($self, $xhdr ? r221 : r225); + $self->msg_more($xhdr ? r221 : r225); long_response($self, \&xref_range_i, @$r); } } sub over_header_for { - my ($over, $num, $field) = @_; - my $smsg = $over->get_art($num) or return; + my ($ibx, $num, $field) = @_; + my $smsg = $ibx->over(1)->get_art($num) or return; return PublicInbox::Smsg::date($smsg) if $field eq 'date'; $smsg->{$field}; } sub smsg_range_i { my ($self, $beg, $end, $field) = @_; - my $over = $self->{ibx}->over; - my $msgs = $over->query_xover($$beg, $end); + my $msgs = $self->{ibx}->over(1)->query_xover($$beg, $end); scalar(@$msgs) or return; my $tmp = ''; @@ -816,13 +813,13 @@ sub hdr_smsg ($$$$) { if (defined $range && $range =~ $ONE_MSGID) { my ($ibx, $n) = mid_lookup($self, $1); return r430 unless defined $n; - my $v = over_header_for($ibx->over, $n, $field); + my $v = over_header_for($ibx, $n, $field); hdr_mid_response($self, $xhdr, $ibx, $n, $range, $v); } else { # numeric range $range = $self->{article} unless defined $range; my $r = get_range($self, $range); return $r unless ref $r; - more($self, $xhdr ? r221 : r225); + $self->msg_more($xhdr ? r221 : r225); long_response($self, \&smsg_range_i, @$r, $field); } } @@ -840,7 +837,7 @@ sub do_hdr ($$$;$) { } elsif ($sub =~ /\A:(bytes|lines)\z/) { hdr_smsg($self, $xhdr, $1, $range); } else { - $xhdr ? (r221 . "\r\n.") : "503 HDR not permitted on $header"; + $xhdr ? (r221 . '.') : "503 HDR not permitted on $header"; } } @@ -870,22 +867,15 @@ sub hdr_mid_prefix ($$$$$) { sub hdr_mid_response ($$$$$$) { my ($self, $xhdr, $ibx, $n, $mid, $v) = @_; - my $res = ''; - if ($xhdr) { - $res .= r221 . "\r\n"; - $res .= "$mid $v\r\n"; - } else { - $res .= r225 . "\r\n"; - my $pfx = hdr_mid_prefix($self, $xhdr, $ibx, $n, $mid); - $res .= "$pfx $v\r\n"; - } - res($self, $res .= '.'); + $self->write(($xhdr ? r221.$mid : + r225.hdr_mid_prefix($self, $xhdr, $ibx, $n, $mid)) . + " $v\r\n.\r\n"); undef; } sub xrover_i { my ($self, $beg, $end) = @_; - my $h = over_header_for($self->{ibx}->over, $$beg, 'references'); + my $h = over_header_for($self->{ibx}, $$beg, 'references'); more($self, "$$beg $h") if defined($h); $$beg++ < $end; } @@ -925,7 +915,7 @@ sub cmd_over ($;$) { if ($range && $range =~ $ONE_MSGID) { my ($ibx, $n) = mid_lookup($self, $1); defined $n or return r430; - my $smsg = $ibx->over->get_art($n) or return r430; + my $smsg = $ibx->over(1)->get_art($n) or return r430; more($self, '224 Overview information follows (multi-line)'); # Only set article number column if it's the current group @@ -946,7 +936,7 @@ sub cmd_over ($;$) { sub xover_i { my ($self, $beg, $end) = @_; my $ibx = $self->{ibx}; - my $msgs = $ibx->over->query_xover($$beg, $end); + my $msgs = $ibx->over(1)->query_xover($$beg, $end); my $nr = scalar @$msgs or return; # OVERVIEW.FMT @@ -975,7 +965,7 @@ sub cmd_starttls ($) { return r502 if ($sock->can('accept_SSL') || $self->compressed); my $opt = $self->{nntpd}->{accept_tls} or return '580 can not initiate TLS negotiation'; - res($self, '382 Continue with TLS negotiation'); + $self->write(\"382 Continue with TLS negotiation\r\n"); $self->{sock} = IO::Socket::SSL->start_SSL($sock, %$opt); $self->requeue if PublicInbox::DS::accept_tls_step($self); undef; @@ -1014,7 +1004,7 @@ sub cmd_xpath ($$) { } } else { # slow path, no point in using long_response for my $ibx (values %$groups) { - my $n = $ibx->mm->num_for($mid) // next; + my $n = $ibx->mm(1)->num_for($mid) // next; push @paths, "$ibx->{newsgroup}/$n"; } } @@ -1050,7 +1040,6 @@ sub event_step { return unless $self->flush_write && $self->{sock} && !$self->{long_cb}; - $self->update_idle_time; # only read more requests if we've drained the write buffer, # otherwise we can be buffering infinitely w/o backpressure @@ -1072,17 +1061,15 @@ sub event_step { out($self, "[$fd] %s - %0.6f$pending", $line, now() - $t0); return $self->close if $r < 0; $self->rbuf_idle($rbuf); - $self->update_idle_time; # maybe there's more pipelined data, or we'll have # to register it for socket-readiness notifications $self->requeue unless $pending; } -# for graceful shutdown in PublicInbox::Daemon: -sub busy { - my ($self, $now) = @_; - ($self->{rbuf} || $self->{wbuf} || $self->not_idle_long($now)); +sub busy { # for graceful shutdown in PublicInbox::Daemon: + my ($self) = @_; + defined($self->{rbuf}) || defined($self->{wbuf}) } 1;