* [PATCH 1/3] view: split out reply logic into its own module
2017-06-14 0:10 [PATCH 0/3] support per-inbox "replyto" parameter Eric Wong
@ 2017-06-14 0:10 ` Eric Wong
2017-06-14 0:10 ` [PATCH 2/3] replyto parameter support Eric Wong
2017-06-14 0:10 ` [PATCH 3/3] reply: support Reply-To Eric Wong
2 siblings, 0 replies; 4+ messages in thread
From: Eric Wong @ 2017-06-14 0:10 UTC (permalink / raw)
To: meta
We'll be adding more reply options for centralized mailing
lists. So split out the logic so it's easy-to-find.
Organizing code is hard :<
---
MANIFEST | 1 +
lib/PublicInbox/Reply.pm | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/PublicInbox/View.pm | 47 ++---------------------------------------
t/view.t | 2 +-
4 files changed, 58 insertions(+), 46 deletions(-)
create mode 100644 lib/PublicInbox/Reply.pm
diff --git a/MANIFEST b/MANIFEST
index 3bfd9a4..0475cdd 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -76,6 +76,7 @@ lib/PublicInbox/NewsWWW.pm
lib/PublicInbox/ParentPipe.pm
lib/PublicInbox/ProcessPipe.pm
lib/PublicInbox/Qspawn.pm
+lib/PublicInbox/Reply.pm
lib/PublicInbox/SaPlugin/ListMirror.pm
lib/PublicInbox/Search.pm
lib/PublicInbox/SearchIdx.pm
diff --git a/lib/PublicInbox/Reply.pm b/lib/PublicInbox/Reply.pm
new file mode 100644
index 0000000..73a4df1
--- /dev/null
+++ b/lib/PublicInbox/Reply.pm
@@ -0,0 +1,54 @@
+# Copyright (C) 2014-2017 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+package PublicInbox::Reply;
+use strict;
+use warnings;
+use URI::Escape qw/uri_escape_utf8/;
+use PublicInbox::Hval qw/ascii_html/;
+use PublicInbox::Address;
+use PublicInbox::MID qw/mid_clean mid_escape/;
+
+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));
+}
+
+1;
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 9ef4712..0d85581 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -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 => ' ';
@@ -57,7 +57,7 @@ sub msg_reply {
$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($hdr);
push @$arg, '/path/to/YOUR_REPLY';
$arg = ascii_html(join(" \\\n ", '', @$arg));
<<EOF
@@ -618,49 +618,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) = @_;
diff --git a/t/view.t b/t/view.t
index 2181b5e..abd0001 100644
--- a/t/view.t
+++ b/t/view.t
@@ -15,7 +15,7 @@ my @q = (
while (@q) {
my $input = shift @q;
my $expect = shift @q;
- my $res = PublicInbox::View::squote_maybe($input);
+ my $res = PublicInbox::Reply::squote_maybe($input);
is($res, $expect, "quote $input => $res");
}
--
EW
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] replyto parameter support
2017-06-14 0:10 [PATCH 0/3] support per-inbox "replyto" parameter Eric Wong
2017-06-14 0:10 ` [PATCH 1/3] view: split out reply logic into its own module Eric Wong
@ 2017-06-14 0:10 ` Eric Wong
2017-06-14 0:10 ` [PATCH 3/3] reply: support Reply-To Eric Wong
2 siblings, 0 replies; 4+ messages in thread
From: Eric Wong @ 2017-06-14 0:10 UTC (permalink / raw)
To: meta
This allows us to support centralized mailing lists (which suck,
but better than no mailing list at all).
---
MANIFEST | 1 +
lib/PublicInbox/Config.pm | 2 +-
lib/PublicInbox/Reply.pm | 66 +++++++++++++++++++++++++++++++++-------------
lib/PublicInbox/View.pm | 5 ++--
t/reply.t | 67 +++++++++++++++++++++++++++++++++++++++++++++++
t/view.t | 12 ---------
6 files changed, 120 insertions(+), 33 deletions(-)
create mode 100644 t/reply.t
diff --git a/MANIFEST b/MANIFEST
index 0475cdd..d0b7f2b 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -159,6 +159,7 @@ t/psgi_mount.t
t/psgi_search.t
t/psgi_text.t
t/qspawn.t
+t/reply.t
t/search-thr-index.t
t/search.t
t/spamcheck_spamc.t
diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm
index f6275cd..323f8a1 100644
--- a/lib/PublicInbox/Config.pm
+++ b/lib/PublicInbox/Config.pm
@@ -136,7 +136,7 @@ sub _fill {
foreach my $k (qw(mainrepo address filter url newsgroup
infourl watch watchheader httpbackendmax
- feedmax nntpserver)) {
+ replyto feedmax nntpserver)) {
my $v = $self->{"$pfx.$k"};
$rv->{$k} = $v if defined $v;
}
diff --git a/lib/PublicInbox/Reply.pm b/lib/PublicInbox/Reply.pm
index 73a4df1..5bbe8f4 100644
--- a/lib/PublicInbox/Reply.pm
+++ b/lib/PublicInbox/Reply.pm
@@ -17,36 +17,66 @@ sub squote_maybe ($) {
$val;
}
+sub add_addrs {
+ my ($to, $cc, @addrs) = @_;
+ foreach my $address (@addrs) {
+ my $dst = lc($address);
+ $cc->{$dst} ||= $address;
+ $$to ||= $dst;
+ }
+}
+
+my @reply_headers = qw(From To Cc);
+my $reply_headers = join('|', @reply_headers);
+
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 ($ibx, $hdr) = @_;
+ my $cc = {}; # everyone else
+ my $to; # this is the From address by default
+
+ foreach my $rt (split(/\s*,\s*/, $ibx->{replyto} || ':all')) {
+ if ($rt eq ':all') {
+ foreach my $h (@reply_headers) {
+ my $v = $hdr->header($h);
+ defined($v) && ($v ne '') or next;
+ my @addrs = PublicInbox::Address::emails($v);
+ add_addrs(\$to, $cc, @addrs);
+ }
+ } elsif ($rt eq ':list') {
+ add_addrs(\$to, $cc, $ibx->{-primary_address});
+ } elsif ($rt =~ /\A(?:$reply_headers)\z/io) {
+ my $v = $hdr->header($rt);
+ if (defined($v) && ($v ne '')) {
+ my @addrs = PublicInbox::Address::emails($v);
+ add_addrs(\$to, $cc, @addrs);
+ }
+ } elsif ($rt =~ /@/) {
+ add_addrs(\$to, $cc, $rt);
+ } else {
+ warn "Unrecognized replyto = '$rt' in config\n";
}
}
- my @arg;
+ 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};
+ 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";
+ my @cc = sort values %$cc;
+ $cc = '';
+ if (@cc) {
+ push(@arg, map { "--cc=$_" } @cc);
+ $cc = '&Cc=' . uri_escape_utf8(join(',', @cc));
+ }
+
+ # order matters, Subject is the least important header,
+ # so it is last in case it's lost/truncated in a copy+paste
+ my $href = "mailto:$to?In-Reply-To=$irt${cc}&Subject=$subj";
(\@arg, ascii_html($href));
}
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 0d85581..2d81640 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -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) = PublicInbox::Reply::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
diff --git a/t/reply.t b/t/reply.t
new file mode 100644
index 0000000..640069c
--- /dev/null
+++ b/t/reply.t
@@ -0,0 +1,67 @@
+# Copyright (C) 2017 all contributors <meta@public-inbox.org>
+# License: AGPL-3+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+use strict;
+use warnings;
+use Test::More;
+use Email::MIME;
+use_ok 'PublicInbox::Reply';
+
+my @q = (
+ 'foo@bar', 'foo@bar',
+ 'a b', "'a b'",
+ "a'b", "'a'\\''b'",
+);
+
+while (@q) {
+ my $input = shift @q;
+ my $expect = shift @q;
+ my $res = PublicInbox::Reply::squote_maybe($input);
+ is($res, $expect, "quote $input => $res");
+}
+
+my $mime = Email::MIME->new(<<'EOF');
+From: from <from@example.com>
+To: to <to@example.com>
+Cc: cc@example.com
+Message-Id: <blah@example.com>
+Subject: hihi
+
+EOF
+
+my $hdr = $mime->header_obj;
+my $ibx = { -primary_address => 'primary@example.com' };
+
+my ($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
+my $exp = [
+ '--in-reply-to=blah@example.com',
+ '--to=from@example.com',
+ '--cc=cc@example.com',
+ '--cc=to@example.com'
+];
+
+is_deeply($arg, $exp, 'default reply is to :all');
+$ibx->{replyto} = ':all';
+($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
+is_deeply($arg, $exp, '":all" also works');
+
+$exp = [ '--in-reply-to=blah@example.com', '--to=primary@example.com' ];
+$ibx->{replyto} = ':list';
+($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
+is_deeply($arg, $exp, '":list" works for centralized lists');
+
+$exp = [
+ '--in-reply-to=blah@example.com',
+ '--to=primary@example.com',
+ '--cc=cc@example.com',
+ '--cc=to@example.com'
+];
+$ibx->{replyto} = ':list,Cc,To';
+($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
+is_deeply($arg, $exp, '":list,Cc,To" works for kinda centralized lists');
+
+$ibx->{replyto} = 'new@example.com';
+($arg, $link) = PublicInbox::Reply::mailto_arg_link($ibx, $hdr);
+$exp = [ '--in-reply-to=blah@example.com', '--to=new@example.com' ];
+is_deeply($arg, $exp, 'explicit address works, too');
+
+done_testing();
diff --git a/t/view.t b/t/view.t
index abd0001..1f4ed93 100644
--- a/t/view.t
+++ b/t/view.t
@@ -7,18 +7,6 @@ use Email::MIME;
use Plack::Util;
use_ok 'PublicInbox::View';
-my @q = (
- 'foo@bar', 'foo@bar',
- 'a b', "'a b'",
- "a'b", "'a'\\''b'",
-);
-while (@q) {
- my $input = shift @q;
- my $expect = shift @q;
- my $res = PublicInbox::Reply::squote_maybe($input);
- is($res, $expect, "quote $input => $res");
-}
-
# FIXME: make this test less fragile
my $ctx = {
env => { HTTP_HOST => 'example.com', 'psgi.url_scheme' => 'http' },
--
EW
^ permalink raw reply related [flat|nested] 4+ messages in thread