]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/Reply.pm
treewide: run update-copyrights from gnulib for 2019
[public-inbox.git] / lib / PublicInbox / Reply.pm
1 # Copyright (C) 2014-2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # For reply instructions and address generation in WWW UI
5 package PublicInbox::Reply;
6 use strict;
7 use warnings;
8 use URI::Escape qw/uri_escape_utf8/;
9 use PublicInbox::Hval qw/ascii_html obfuscate_addrs/;
10 use PublicInbox::Address;
11 use PublicInbox::MID qw/mid_clean mid_escape/;
12
13 sub squote_maybe ($) {
14         my ($val) = @_;
15         if ($val =~ m{([^\w@\./,\%\+\-])}) {
16                 $val =~ s/(['!])/'\\$1'/g; # '!' for csh
17                 return "'$val'";
18         }
19         $val;
20 }
21
22 sub add_addrs {
23         my ($to, $cc, @addrs) = @_;
24         foreach my $address (@addrs) {
25                 my $dst = lc($address);
26                 $cc->{$dst} ||= $address;
27                 $$to ||= $dst;
28         }
29 }
30
31 my @reply_headers = qw(From To Cc Reply-To);
32 my $reply_headers = join('|', @reply_headers);
33
34 sub mailto_arg_link {
35         my ($ibx, $hdr) = @_;
36         my $cc = {}; # everyone else
37         my $to; # this is the From address by defaultq
38         my $reply_to_all = 'reply-to-all'; # the only good default :P
39         my $reply_to_cfg = $ibx->{replyto};
40
41         $reply_to_cfg ||= ':all';
42         if ($reply_to_cfg =~ /\A:none=(.*)/) {
43                 my $msg = $1;
44                 $msg = 'replies disabled' if $msg eq '';
45                 return \$msg;
46         }
47
48         foreach my $rt (split(/\s*,\s*/, $reply_to_cfg)) {
49                 if ($rt eq ':all') {
50                         foreach my $h (@reply_headers) {
51                                 my $v = $hdr->header($h);
52                                 defined($v) && ($v ne '') or next;
53                                 my @addrs = PublicInbox::Address::emails($v);
54                                 add_addrs(\$to, $cc, @addrs);
55                         }
56                 } elsif ($rt eq ':list') {
57                         $reply_to_all = 'reply-to-list';
58                         add_addrs(\$to, $cc, $ibx->{-primary_address});
59                 } elsif ($rt =~ /\A(?:$reply_headers)\z/io) {
60                         # ugh, this is weird...
61                         my $v = $hdr->header($rt);
62                         if (defined($v) && ($v ne '')) {
63                                 my @addrs = PublicInbox::Address::emails($v);
64                                 add_addrs(\$to, $cc, @addrs);
65                         }
66                 } elsif ($rt =~ /@/) {
67                         add_addrs(\$to, $cc, $rt);
68                 } else {
69                         warn "Unrecognized replyto = '$rt' in config\n";
70                 }
71         }
72
73         my @arg;
74         my $obfs = $ibx->{obfuscate};
75         my $subj = $hdr->header('Subject') || '';
76         $subj = "Re: $subj" unless $subj =~ /\bRe:/i;
77         my $mid = $hdr->header_raw('Message-ID');
78         push @arg, '--in-reply-to='.squote_maybe(mid_clean($mid));
79         my $irt = mid_escape($mid);
80         delete $cc->{$to};
81         if ($obfs) {
82                 my $arg_to = $to;
83                 obfuscate_addrs($ibx, $arg_to, '$(echo .)');
84                 push @arg, "--to=$arg_to";
85                 # no $subj for $href below
86         } else {
87                 push @arg, "--to=$to";
88                 $to = uri_escape_utf8($to);
89                 $subj = uri_escape_utf8($subj);
90         }
91         my @cc = sort values %$cc;
92         $cc = '';
93         if (@cc) {
94                 if ($obfs) {
95                         push(@arg, map {
96                                 my $addr = $_;
97                                 obfuscate_addrs($ibx, $addr, '$(echo .)');
98                                 "--cc=$addr";
99                         } @cc);
100                 } else {
101                         $cc = '&Cc=' . uri_escape_utf8(join(',', @cc));
102                         push(@arg, map { "--cc=$_" } @cc);
103                 }
104         }
105
106         # I'm not sure if address obfuscation and mailto: links can
107         # be made compatible; and address obfuscation is misguided,
108         # anyways.
109         return (\@arg, '', $reply_to_all) if $obfs;
110
111         # order matters, Subject is the least important header,
112         # so it is last in case it's lost/truncated in a copy+paste
113         my $href = "mailto:$to?In-Reply-To=$irt${cc}&Subject=$subj";
114
115         (\@arg, ascii_html($href), $reply_to_all);
116 }
117
118 1;