+sub do_hdr ($$$;$) {
+ my ($self, $xhdr, $header, $range) = @_;
+ my $sub = lc $header;
+ if ($sub eq 'message-id') {
+ hdr_message_id($self, $xhdr, $range);
+ } elsif ($sub eq 'xref') {
+ hdr_xref($self, $xhdr, $range);
+ } elsif ($sub =~ /\A(?:subject|references|date|from|to|cc|
+ bytes|lines)\z/x) {
+ hdr_searchmsg($self, $xhdr, $sub, $range);
+ } elsif ($sub =~ /\A:(bytes|lines)\z/) {
+ hdr_searchmsg($self, $xhdr, $1, $range);
+ } else {
+ $xhdr ? (r221 . "\r\n.") : "503 HDR not permitted on $header";
+ }
+}
+
+# RFC 3977
+sub cmd_hdr ($$;$) {
+ my ($self, $header, $range) = @_;
+ do_hdr($self, 0, $header, $range);
+}
+
+# RFC 2980
+sub cmd_xhdr ($$;$) {
+ my ($self, $header, $range) = @_;
+ do_hdr($self, 1, $header, $range);
+}
+
+sub hdr_mid_prefix ($$$$$) {
+ my ($self, $xhdr, $ng, $n, $mid) = @_;
+ return $mid if $xhdr;
+
+ # HDR for RFC 3977 users
+ if (my $self_ng = $self->{ng}) {
+ ($self_ng eq $ng) ? $n : '0';
+ } else {
+ '0';
+ }
+}
+
+sub hdr_mid_response ($$$$$$) {
+ my ($self, $xhdr, $ng, $n, $mid, $v) = @_; # r: art_lookup result
+ 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, $ng, $n, $mid);
+ $res .= "$pfx $v\r\n";
+ }
+ res($self, $res .= '.');
+ undef;
+}
+
+sub cmd_xrover ($;$) {
+ my ($self, $range) = @_;
+ my $ng = $self->{ng} or return '412 no newsgroup selected';
+ (defined $range && $range =~ /[<>]/) and
+ return '420 No article(s) selected'; # no message IDs
+
+ $range = $self->{article} unless defined $range;
+ my $r = get_range($self, $range);
+ return $r unless ref $r;
+ my ($beg, $end) = @$r;
+ my $mm = $ng->mm;
+ my $srch = $ng->search;
+ more($self, '224 Overview information follows');
+ long_response($self, $beg, $end, sub {
+ my ($i) = @_;
+ my $num = $$i;
+ my $h = search_header_for($srch, $num, 'references');
+ defined $h or return;
+ more($self, "$num $h");
+ });
+}
+
+sub over_line ($$) {
+ my ($num, $smsg) = @_;
+ # n.b. field access and procedural calls can be
+ # 10%-15% faster than OO method calls:
+ my $s = join("\t", $num,
+ $smsg->{subject},
+ $smsg->{from},
+ PublicInbox::SearchMsg::date($smsg),
+ "<$smsg->{mid}>",
+ $smsg->{references},
+ $smsg->{bytes},
+ $smsg->{lines});
+ utf8::encode($s);
+ $s
+}
+
+sub cmd_over ($;$) {
+ my ($self, $range) = @_;
+ if ($range && $range =~ /\A<(.+)>\z/) {
+ my ($ng, $n) = mid_lookup($self, $1);
+ 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
+ my $self_ng = $self->{ng};
+ $n = 0 if (!$self_ng || $self_ng ne $ng);
+ more($self, over_line($n, $smsg));
+ '.';
+ } else {
+ cmd_xover($self, $range);
+ }
+}
+