about summary refs log tree commit homepage
path: root/lib/PublicInbox/NNTP.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox/NNTP.pm')
-rw-r--r--lib/PublicInbox/NNTP.pm192
1 files changed, 93 insertions, 99 deletions
diff --git a/lib/PublicInbox/NNTP.pm b/lib/PublicInbox/NNTP.pm
index c574c9e6..cdbd8e98 100644
--- a/lib/PublicInbox/NNTP.pm
+++ b/lib/PublicInbox/NNTP.pm
@@ -34,7 +34,6 @@ my $LIST_HEADERS = join("\r\n", @OVERVIEW,
                         qw(:bytes :lines Xref To Cc)) . "\r\n";
 
 # disable commands with easy DoS potential:
-# LISTGROUP could get pretty bad, too...
 my %DISABLED; # = map { $_ => 1 } qw(xover list_overview_fmt newnews xhdr);
 
 my $EXPMAP; # fd -> [ idle_time, $self ]
@@ -226,15 +225,12 @@ sub cmd_listgroup ($;$) {
         }
 
         $self->{ng} or return '412 no newsgroup selected';
-        long_response($self, 0, long_response_limit, sub {
-                my ($i) = @_;
-                my $nr = $self->{ng}->mm->id_batch($$i, sub {
-                        my ($ary) = @_;
-                        more($self, join("\r\n", @$ary));
-                });
-
-                # -1 to adjust for implicit increment in long_response
-                $$i = $nr ? $$i + $nr - 1 : long_response_limit;
+        my $n = 0;
+        long_response($self, sub {
+                my $ary = $self->{ng}->mm->ids_after(\$n);
+                scalar @$ary or return;
+                more($self, join("\r\n", @$ary));
+                1;
         });
 }
 
@@ -332,24 +328,22 @@ sub cmd_newnews ($$$$;$$) {
         };
         return '.' unless @srch;
 
-        $ts .= '..';
-        my $opts = { asc => 1, limit => 1000, offset => 0 };
-        long_response($self, 0, long_response_limit, sub {
-                my ($i) = @_;
+        my $prev = 0;
+        long_response($self, sub {
                 my $srch = $srch[0];
-                my $res = $srch->query_ts($ts, $opts);
-                my $msgs = $res->{msgs};
-                if (my $nr = scalar @$msgs) {
+                my $msgs = $srch->query_ts($ts, $prev);
+                if (scalar @$msgs) {
                         more($self, '<' .
                                 join(">\r\n<", map { $_->mid } @$msgs ).
                                 '>');
-                        $opts->{offset} += $nr;
+                        $prev = $msgs->[-1]->{num};
                 } else {
                         shift @srch;
                         if (@srch) { # continue onto next newsgroup
-                                $opts->{offset} = 0;
+                                $prev = 0;
+                                return 1;
                         } else { # break out of the long response.
-                                $$i = long_response_limit;
+                                return;
                         }
                 }
         });
@@ -414,12 +408,30 @@ sub header_append ($$$) {
         $hdr->header_set($k, @v, $v);
 }
 
-sub set_nntp_headers {
-        my ($hdr, $ng, $n, $mid) = @_;
+sub xref ($$$$) {
+        my ($self, $ng, $n, $mid) = @_;
+        my $ret = "$ng->{domain} $ng->{newsgroup}:$n";
+
+        # num_for is pretty cheap and sometimes we'll lookup the existence
+        # of an article without getting even the OVER info.  In other words,
+        # I'm not sure if its worth optimizing by scanning To:/Cc: and
+        # PublicInbox::ExtMsg on the PSGI end is just as expensive
+        foreach my $other (@{$self->{nntpd}->{grouplist}}) {
+                next if $ng eq $other;
+                my $num = eval { $other->mm->num_for($mid) } or next;
+                $ret .= " $other->{newsgroup}:$num";
+        }
+        $ret;
+}
+
+sub set_nntp_headers ($$$$$) {
+        my ($self, $hdr, $ng, $n, $mid) = @_;
 
         # clobber some
-        $hdr->header_set('Newsgroups', $ng->{newsgroup});
-        $hdr->header_set('Xref', xref($ng, $n));
+        my $xref = xref($self, $ng, $n, $mid);
+        $hdr->header_set('Xref', $xref);
+        $xref =~ s/:\d+//g;
+        $hdr->header_set('Newsgroups', (split(/ /, $xref, 2))[1]);
         header_append($hdr, 'List-Post', "<mailto:$ng->{-primary_address}>");
         if (my $url = $ng->base_url) {
                 $mid = mid_escape($mid);
@@ -464,18 +476,16 @@ find_mid:
                 defined $mid or return $err;
         }
 found:
-        my $bytes;
-        my $s = eval { $ng->msg_by_mid($mid, \$bytes) } or return $err;
-        $s = Email::Simple->new($s);
-        my $lines;
+        my $smsg = $ng->search->{over_ro}->get_art($n) or return $err;
+        my $msg = $ng->msg_by_smsg($smsg) or return $err;
+        my $s = Email::Simple->new($msg);
         if ($set_headers) {
-                set_nntp_headers($s->header_obj, $ng, $n, $mid);
-                $lines = $s->body =~ tr!\n!\n!;
+                set_nntp_headers($self, $s->header_obj, $ng, $n, $mid);
 
                 # must be last
                 $s->body_set('') if ($set_headers == 2);
         }
-        [ $n, $mid, $s, $bytes, $lines, $ng ];
+        [ $n, $mid, $s, $smsg->bytes, $smsg->lines, $ng ];
 }
 
 sub simple_body_write ($$) {
@@ -573,8 +583,8 @@ sub get_range ($$) {
         [ $beg, $end ];
 }
 
-sub long_response ($$$$) {
-        my ($self, $beg, $end, $cb) = @_;
+sub long_response ($$) {
+        my ($self, $cb) = @_;
         die "BUG: nested long response" if $self->{long_res};
 
         my $fd = $self->{fd};
@@ -585,24 +595,14 @@ sub long_response ($$$$) {
         $self->watch_read(0);
         my $t0 = now();
         $self->{long_res} = sub {
-                # limit our own running time for fairness with other
-                # clients and to avoid buffering too much:
-                my $lim = 100;
-
-                my $err;
-                do {
-                        eval { $cb->(\$beg, \$lim) };
-                } until (($err = $@) || $self->{closed} ||
-                         ++$beg > $end || --$lim < 0 ||
-                         $self->{write_buf_size});
-
-                if ($err || $self->{closed}) {
+                my $more = eval { $cb->() };
+                if ($@ || $self->{closed}) {
                         $self->{long_res} = undef;
 
-                        if ($err) {
+                        if ($@) {
                                 err($self,
                                     "%s during long response[$fd] - %0.6f",
-                                    $err, now() - $t0);
+                                    $@, now() - $t0);
                         }
                         if ($self->{closed}) {
                                 out($self, " deferred[$fd] aborted - %0.6f",
@@ -611,7 +611,7 @@ sub long_response ($$$$) {
                                 update_idle_time($self);
                                 $self->watch_read(1);
                         }
-                } elsif ($lim < 0 || $self->{write_buf_size}) {
+                } elsif ($more) { # $self->{write_buf_size}:
                         # no recursion, schedule another call ASAP
                         # but only after all pending writes are done
                         update_idle_time($self);
@@ -643,19 +643,17 @@ sub hdr_message_id ($$$) { # optimize XHDR Message-ID [range] for slrnpull.
                 my $mm = $self->{ng}->mm;
                 my ($beg, $end) = @$r;
                 more($self, $xhdr ? r221 : r225);
-                long_response($self, $beg, $end, sub {
-                        my ($i) = @_;
-                        my $mid = $mm->mid_for($$i);
-                        more($self, "$$i <$mid>") if defined $mid;
+                long_response($self, sub {
+                        my $r = $mm->msg_range(\$beg, $end);
+                        @$r or return;
+                        more($self, join("\r\n", map {
+                                "$_->[0] <$_->[1]>"
+                        } @$r));
+                        1;
                 });
         }
 }
 
-sub xref ($$) {
-        my ($ng, $n) = @_;
-        "$ng->{domain} $ng->{newsgroup}:$n"
-}
-
 sub mid_lookup ($$) {
         my ($self, $mid) = @_;
         my $self_ng = $self->{ng};
@@ -675,9 +673,11 @@ sub hdr_xref ($$$) { # optimize XHDR Xref [range] for rtin
         my ($self, $xhdr, $range) = @_;
 
         if (defined $range && $range =~ /\A<(.+)>\z/) { # Message-ID
-                my ($ng, $n) = mid_lookup($self, $1);
+                my $mid = $1;
+                my ($ng, $n) = mid_lookup($self, $mid);
                 return r430 unless $n;
-                hdr_mid_response($self, $xhdr, $ng, $n, $range, xref($ng, $n));
+                hdr_mid_response($self, $xhdr, $ng, $n, $range,
+                                xref($self, $ng, $n, $mid));
         } else { # numeric range
                 $range = $self->{article} unless defined $range;
                 my $r = get_range($self, $range);
@@ -686,26 +686,31 @@ sub hdr_xref ($$$) { # optimize XHDR Xref [range] for rtin
                 my $mm = $ng->mm;
                 my ($beg, $end) = @$r;
                 more($self, $xhdr ? r221 : r225);
-                long_response($self, $beg, $end, sub {
-                        my ($i) = @_;
-                        my $mid = $mm->mid_for($$i);
-                        more($self, "$$i ".xref($ng, $$i)) if defined $mid;
+                long_response($self, sub {
+                        my $r = $mm->msg_range(\$beg, $end);
+                        @$r or return;
+                        more($self, join("\r\n", map {
+                                my $num = $_->[0];
+                                "$num ".xref($self, $ng, $num, $_->[1]);
+                        } @$r));
+                        1;
                 });
         }
 }
 
 sub search_header_for {
-        my ($srch, $mid, $field) = @_;
-        my $smsg = $srch->lookup_mail($mid) or return;
-        $smsg->$field;
+        my ($srch, $num, $field) = @_;
+        my $smsg = $srch->{over_ro}->get_art($num) or return;
+        return PublicInbox::SearchMsg::date($smsg) if $field eq 'date';
+        $smsg->{$field};
 }
 
 sub hdr_searchmsg ($$$$) {
         my ($self, $xhdr, $field, $range) = @_;
         if (defined $range && $range =~ /\A<(.+)>\z/) { # Message-ID
                 my ($ng, $n) = mid_lookup($self, $1);
-                return r430 unless $n;
-                my $v = search_header_for($ng->search, $range, $field);
+                return r430 unless defined $n;
+                my $v = search_header_for($ng->search, $n, $field);
                 hdr_mid_response($self, $xhdr, $ng, $n, $range, $v);
         } else { # numeric range
                 $range = $self->{article} unless defined $range;
@@ -715,22 +720,17 @@ sub hdr_searchmsg ($$$$) {
                 return $r unless ref $r;
                 my ($beg, $end) = @$r;
                 more($self, $xhdr ? r221 : r225);
-                my $off = 0;
-                long_response($self, $beg, $end, sub {
-                        my ($i, $lim) = @_;
-                        my $res = $srch->query_xover($beg, $end, $off);
-                        my $msgs = $res->{msgs};
+                my $cur = $beg;
+                long_response($self, sub {
+                        my $msgs = $srch->query_xover($cur, $end);
                         my $nr = scalar @$msgs or return;
-                        $off += $nr;
-                        $$lim -= $nr;
                         my $tmp = '';
                         foreach my $s (@$msgs) {
-                                $tmp .= $s->num . ' ' . $s->$field . "\r\n";
+                                $tmp .= $s->{num} . ' ' . $s->$field . "\r\n";
                         }
                         utf8::encode($tmp);
                         do_more($self, $tmp);
-                        # -1 to adjust for implicit increment in long_response
-                        $$i = $nr ? $$i + $nr - 1 : long_response_limit;
+                        $cur = $msgs->[-1]->{num} + 1;
                 });
         }
 }
@@ -804,11 +804,11 @@ sub cmd_xrover ($;$) {
         my $mm = $ng->mm;
         my $srch = $ng->search;
         more($self, '224 Overview information follows');
-        long_response($self, $beg, $end, sub {
-                my ($i) = @_;
-                my $mid = $mm->mid_for($$i) or return;
-                my $h = search_header_for($srch, $mid, 'references');
-                more($self, "$$i $h");
+
+        long_response($self, sub {
+                my $h = search_header_for($srch, $beg, 'references');
+                more($self, "$beg $h") if defined($h);
+                $beg++ < $end;
         });
 }
 
@@ -820,10 +820,10 @@ sub over_line ($$) {
                 $smsg->{subject},
                 $smsg->{from},
                 PublicInbox::SearchMsg::date($smsg),
-                '<'.PublicInbox::SearchMsg::mid($smsg).'>',
+                "<$smsg->{mid}>",
                 $smsg->{references},
-                PublicInbox::SearchMsg::bytes($smsg),
-                PublicInbox::SearchMsg::lines($smsg));
+                $smsg->{bytes},
+                $smsg->{lines});
         utf8::encode($s);
         $s
 }
@@ -832,8 +832,8 @@ sub cmd_over ($;$) {
         my ($self, $range) = @_;
         if ($range && $range =~ /\A<(.+)>\z/) {
                 my ($ng, $n) = mid_lookup($self, $1);
-                my $smsg = $ng->search->lookup_mail($range) or
-                        return '430 No article with that message-id';
+                defined $n or return r430;
+                my $smsg = $ng->search->{over_ro}->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
@@ -854,22 +854,16 @@ sub cmd_xover ($;$) {
         my ($beg, $end) = @$r;
         more($self, "224 Overview information follows for $beg to $end");
         my $srch = $self->{ng}->search;
-        my $off = 0;
-        long_response($self, $beg, $end, sub {
-                my ($i, $lim) = @_;
-                my $res = $srch->query_xover($beg, $end, $off);
-                my $msgs = $res->{msgs};
+        my $cur = $beg;
+        long_response($self, sub {
+                my $msgs = $srch->query_xover($cur, $end);
                 my $nr = scalar @$msgs or return;
-                $off += $nr;
-                $$lim -= $nr;
 
                 # OVERVIEW.FMT
                 more($self, join("\r\n", map {
-                        over_line(PublicInbox::SearchMsg::num($_), $_);
+                        over_line($_->{num}, $_);
                         } @$msgs));
-
-                # -1 to adjust for implicit increment in long_response
-                $$i = $nr ? $$i + $nr - 1 : long_response_limit;
+                $cur = $msgs->[-1]->{num} + 1;
         });
 }