]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/View.pm
replyto parameter support
[public-inbox.git] / lib / PublicInbox / View.pm
index a50cb642373ad1f9eea336e119180128d674e15e..2d816408cee5ca7437cbe0b11fde8084f363f67e 100644 (file)
@@ -6,7 +6,6 @@
 package PublicInbox::View;
 use strict;
 use warnings;
-use URI::Escape qw/uri_escape_utf8/;
 use Date::Parse qw/str2time/;
 use PublicInbox::Hval qw/ascii_html/;
 use PublicInbox::Linkify;
@@ -14,6 +13,7 @@ use PublicInbox::MID qw/mid_clean id_compress mid_mime mid_escape/;
 use PublicInbox::MsgIter;
 use PublicInbox::Address;
 use PublicInbox::WwwStream;
+use PublicInbox::Reply;
 require POSIX;
 
 use constant INDENT => '  ';
@@ -52,12 +52,13 @@ sub msg_reply {
         'https://en.wikipedia.org/wiki/Posting_style#Interleaved_style';
 
        my $info = '';
-       if (my $url = $ctx->{-inbox}->{infourl}) {
+       my $ibx = $ctx->{-inbox};
+       if (my $url = $ibx->{infourl}) {
                $url = PublicInbox::Hval::prurl($ctx->{env}, $url);
                $info = qq(\n  List information: <a\nhref="$url">$url</a>\n);
        }
 
-       my ($arg, $link) = mailto_arg_link($hdr);
+       my ($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
        push @$arg, '/path/to/YOUR_REPLY';
        $arg = ascii_html(join(" \\\n    ", '', @$arg));
        <<EOF
@@ -92,13 +93,13 @@ EOF
 
 sub in_reply_to {
        my ($hdr) = @_;
-       my $irt = $hdr->header_raw('In-Reply-To');
-
-       return mid_clean($irt) if defined $irt && $irt ne '';
-
-       my $refs = $hdr->header_raw('References');
-       if ($refs && $refs =~ /<([^>]+)>\s*\z/s) {
-               return $1;
+       my %mid = map { $_ => 1 } $hdr->header_raw('Message-ID');
+       my @refs = ($hdr->header_raw('References'),
+                       $hdr->header_raw('In-Reply-To'));
+       @refs = ((join(' ', @refs)) =~ /<([^>]+)>/g);
+       while (defined(my $irt = pop @refs)) {
+               next if $mid{"<$irt>"};
+               return $irt;
        }
        undef;
 }
@@ -201,7 +202,10 @@ sub _th_index_lite {
        my $rv = '';
        my $mapping = $ctx->{mapping} or return $rv;
        my $pad = '  ';
-       my ($attr, $node, $idx, $level) = @{$mapping->{$mid_raw}};
+       my $mid_map = $mapping->{$mid_raw};
+       defined $mid_map or
+               return 'public-inbox BUG: '.ascii_html($mid_raw).' not mapped';
+       my ($attr, $node, $idx, $level) = @$mid_map;
        my $children = $node->{children};
        my $nr_c = scalar @$children;
        my $nr_s = 0;
@@ -260,8 +264,8 @@ sub _th_index_lite {
 }
 
 sub walk_thread {
-       my ($th, $ctx, $cb) = @_;
-       my @q = map { (0, $_, -1) } @{$th->{rootset}};
+       my ($rootset, $ctx, $cb) = @_;
+       my @q = map { (0, $_, -1) } @$rootset;
        while (@q) {
                my ($level, $node, $i) = splice(@q, 0, 3);
                defined $node or next;
@@ -285,10 +289,10 @@ sub thread_index_entry {
 }
 
 sub stream_thread ($$) {
-       my ($th, $ctx) = @_;
+       my ($rootset, $ctx) = @_;
        my $inbox = $ctx->{-inbox};
        my $mime;
-       my @q = map { (0, $_) } @{$th->{rootset}};
+       my @q = map { (0, $_) } @$rootset;
        my $level;
        while (@q) {
                $level = shift @q;
@@ -299,7 +303,7 @@ sub stream_thread ($$) {
        }
        return missing_thread($ctx) unless $mime;
 
-       $mime = Email::MIME->new($mime);
+       $mime = PublicInbox::MIME->new($mime);
        $ctx->{-title_html} = ascii_html($mime->header('Subject'));
        $ctx->{-html_tip} = thread_index_entry($ctx, $level, $mime);
        PublicInbox::WwwStream->response($ctx, 200, sub {
@@ -311,7 +315,7 @@ sub stream_thread ($$) {
                        unshift @q, map { ($cl, $_) } @{$node->{children}};
                        my $mid = $node->{id};
                        if ($mime = $inbox->msg_by_smsg($node->{smsg})) {
-                               $mime = Email::MIME->new($mime);
+                               $mime = PublicInbox::MIME->new($mime);
                                return thread_index_entry($ctx, $level, $mime);
                        } else {
                                return ghost_index_entry($ctx, $level, $node);
@@ -350,10 +354,10 @@ sub thread_html {
        $ctx->{mapping} = {};
        $ctx->{s_nr} = "$nr+ messages in thread";
 
-       my $th = thread_results($msgs);
-       walk_thread($th, $ctx, *pre_thread);
+       my $rootset = thread_results($msgs);
+       walk_thread($rootset, $ctx, *pre_thread);
        $skel .= '</pre>';
-       return stream_thread($th, $ctx) unless $ctx->{flat};
+       return stream_thread($rootset, $ctx) unless $ctx->{flat};
 
        # flat display: lazy load the full message from smsg
        my $inbox = $ctx->{-inbox};
@@ -362,7 +366,7 @@ sub thread_html {
                $mime = $inbox->msg_by_smsg($mime) and last;
        }
        return missing_thread($ctx) unless $mime;
-       $mime = Email::MIME->new($mime);
+       $mime = PublicInbox::MIME->new($mime);
        $ctx->{-title_html} = ascii_html($mime->header('Subject'));
        $ctx->{-html_tip} = '<pre>'.index_entry($mime, $ctx, scalar @$msgs);
        $mime = undef;
@@ -372,7 +376,7 @@ sub thread_html {
                        $mime = $inbox->msg_by_smsg($mime) and last;
                }
                if ($mime) {
-                       $mime = Email::MIME->new($mime);
+                       $mime = PublicInbox::MIME->new($mime);
                        return index_entry($mime, $ctx, scalar @$msgs);
                }
                $msgs = undef;
@@ -435,13 +439,14 @@ sub attach_link ($$$$;$) {
        }
        $ret .= "[-- Attachment #$idx: ";
        my $ts = "Type: $ct, Size: $size bytes";
+       $desc = ascii_html($desc);
        $ret .= ($desc eq '') ? "$ts --]" : "$desc --]\n[-- $ts --]";
        $ret .= "</a>\n";
 }
 
 sub add_text_body {
        my ($upfx, $p) = @_; # from msg_iter: [ Email::MIME, depth, @idx ]
-       my ($part, $depth, @idx) = @$p;
+       my ($part, $depth) = @$p; # attachment @idx is unused
        my $ct = $part->content_type || 'text/plain';
        my $fn = $part->filename;
 
@@ -476,29 +481,26 @@ sub add_text_body {
        }
        my @quot;
        my $l = PublicInbox::Linkify->new;
-       while (defined(my $cur = shift @lines)) {
+       foreach my $cur (@lines) {
                if ($cur !~ /^>/) {
                        # show the previously buffered quote inline
                        flush_quote(\$s, $l, \@quot) if @quot;
 
                        # regular line, OK
-                       $cur = $l->linkify_1($cur);
-                       $cur = ascii_html($cur);
-                       $s .= $l->linkify_2($cur);
+                       $l->linkify_1($cur);
+                       $s .= $l->linkify_2(ascii_html($cur));
                } else {
                        push @quot, $cur;
                }
        }
 
-       my $end = "\n";
-       if (@quot) {
-               $end = '';
+       if (@quot) { # ugh, top posted
                flush_quote(\$s, $l, \@quot);
+       } elsif ($s =~ /\n\z/s) { # common, last line ends with a newline
+               $s;
+       } else { # some editors don't do newlines...
+               $s .= "\n";
        }
-       $s =~ s/[ \t]+$//sgm; # kill per-line trailing whitespace
-       $s =~ s/\A\n+//s; # kill leading blank lines
-       $s =~ s/\s+\z//s; # kill all trailing spaces
-       $s .= $end;
 }
 
 sub _msg_html_prepare {
@@ -617,49 +619,6 @@ sub _parent_headers {
        $rv;
 }
 
-sub squote_maybe ($) {
-       my ($val) = @_;
-       if ($val =~ m{([^\w@\./,\%\+\-])}) {
-               $val =~ s/(['!])/'\\$1'/g; # '!' for csh
-               return "'$val'";
-       }
-       $val;
-}
-
-sub mailto_arg_link {
-       my ($hdr) = @_;
-       my %cc; # everyone else
-       my $to; # this is the From address
-
-       foreach my $h (qw(From To Cc)) {
-               my $v = $hdr->header($h);
-               defined($v) && ($v ne '') or next;
-               my @addrs = PublicInbox::Address::emails($v);
-               foreach my $address (@addrs) {
-                       my $dst = lc($address);
-                       $cc{$dst} ||= $address;
-                       $to ||= $dst;
-               }
-       }
-       my @arg;
-
-       my $subj = $hdr->header('Subject') || '';
-       $subj = "Re: $subj" unless $subj =~ /\bRe:/i;
-       my $mid = $hdr->header_raw('Message-ID');
-       push @arg, '--in-reply-to='.squote_maybe(mid_clean($mid));
-       my $irt = mid_escape($mid);
-       delete $cc{$to};
-       push @arg, "--to=$to";
-       $to = uri_escape_utf8($to);
-       $subj = uri_escape_utf8($subj);
-       my @cc = sort values %cc;
-       push(@arg, map { "--cc=$_" } @cc);
-       my $cc = uri_escape_utf8(join(',', @cc));
-       my $href = "mailto:$to?In-Reply-To=$irt&Cc=${cc}&Subject=$subj";
-
-       (\@arg, ascii_html($href));
-}
-
 sub html_footer {
        my ($hdr, $standalone, $ctx, $rhref) = @_;
 
@@ -749,10 +708,7 @@ sub msg_timestamp {
 sub thread_results {
        my ($msgs) = @_;
        require PublicInbox::SearchThread;
-       my $th = PublicInbox::SearchThread->new($msgs);
-       $th->thread;
-       $th->order(*sort_ts);
-       $th
+       PublicInbox::SearchThread::thread($msgs, *sort_ts);
 }
 
 sub missing_thread {