From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 0F4351FA01 for ; Sat, 20 Mar 2021 10:04:08 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 4/5] lei_to_mail: match mutt order of status headers Date: Sat, 20 Mar 2021 19:04:06 +0900 Message-Id: <20210320100407.15713-5-e@80x24.org> In-Reply-To: <20210320100407.15713-1-e@80x24.org> References: <20210320100407.15713-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: These changes may make it easier to do byte-for-byte comparisons with mail copied out of mutt, a popular MUA for our target audience. mutt currently outputs the 'R' (seen) flag before the 'O' character in the Status: header. We'll assume that stays the case (it has been for a while). Status now comes before X-Status, also matching mutt behavior. --- lib/PublicInbox/LeiToMail.pm | 14 ++++++++------ t/lei-q-kw.t | 14 +++++++------- t/lei-q-remote-import.t | 4 ++-- t/lei_to_mail.t | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/PublicInbox/LeiToMail.pm b/lib/PublicInbox/LeiToMail.pm index 3e6cf00c..e9ab939c 100644 --- a/lib/PublicInbox/LeiToMail.pm +++ b/lib/PublicInbox/LeiToMail.pm @@ -42,10 +42,7 @@ sub _mbox_hdr_buf ($$$) { my ($eml, $type, $smsg) = @_; $eml->header_set($_) for (qw(Lines Bytes Content-Length)); - # Messages are always 'O' (non-\Recent in IMAP), it saves - # MUAs the trouble of rewriting the mbox if no other - # changes are made - my %hdr = (Status => [ 'O' ]); # set Status, X-Status + my %hdr = (Status => []); # set Status, X-Status for my $k (@{$smsg->{kw} // []}) { if (my $ent = $kw2status{$k}) { push @{$hdr{$ent->[0]}}, $ent->[1]; @@ -53,8 +50,13 @@ sub _mbox_hdr_buf ($$$) { warn "TODO: keyword `$k' not supported for mbox\n"; } } - while (my ($name, $chars) = each %hdr) { - $eml->header_set($name, join('', sort @$chars)); + # Messages are always 'O' (non-\Recent in IMAP), it saves + # MUAs the trouble of rewriting the mbox if no other + # changes are made. We put 'O' at the end (e.g. "Status: RO") + # to match mutt(1) output. + $eml->header_set('Status', join('', sort(@{$hdr{Status}})). 'O'); + if (my $chars = delete $hdr{'X-Status'}) { + $eml->header_set('X-Status', join('', sort(@$chars))); } my $buf = delete $eml->{hdr}; diff --git a/t/lei-q-kw.t b/t/lei-q-kw.t index de2c775a..b5e22e9b 100644 --- a/t/lei-q-kw.t +++ b/t/lei-q-kw.t @@ -13,7 +13,7 @@ my $exp = { '' => eml_load('t/plack-qp.eml'), '' => eml_load('t/utf8.eml'), }; -$exp->{''}->header_set('Status', 'OR'); +$exp->{''}->header_set('Status', 'RO'); $exp->{''}->header_set('Status', 'O'); test_lei(sub { @@ -57,7 +57,7 @@ SKIP: { open my $fh, '<', \$buf or BAIL_OUT $!; PublicInbox::MboxReader->mboxrd($fh, sub { my ($eml) = @_; - $eml->header_set('Status', 'OR'); + $eml->header_set('Status', 'RO'); is_deeply($eml, $exp->{''}, 'FIFO output works as expected'); }); @@ -96,7 +96,7 @@ for my $sfx ('', '.gz') { is($buf, '', 'emptied'); lei_ok(qw(q -o), "mboxrd:$o", qw(m:qp@example.com)); $buf = $read_file->($o); - $buf =~ s/\nStatus: O\n\n/\nStatus: OR\n\n/s or + $buf =~ s/\nStatus: O\n\n/\nStatus: RO\n\n/s or BAIL_OUT "no Status in $buf"; $write_file->($o, $buf); lei_ok(qw(q -a -o), "mboxrd:$o", qw(m:testmessage@example.com)); @@ -111,7 +111,7 @@ for my $sfx ('', '.gz') { lei_ok(qw(q -o), "mboxrd:/dev/stdout", qw(m:qp@example.com)) or diag $lei_err; - like($lei_out, qr/^Status: OR\n/sm, 'Status set by previous augment'); + like($lei_out, qr/^Status: RO\n/sm, 'Status set by previous augment'); } # /mbox + mbox.gz tests my ($ro_home, $cfg_path) = setup_public_inboxes; @@ -144,7 +144,7 @@ lei_ok(qw(q -o), "mboxrd:$o", "m:$m", @inc); # emulate MUA marking mboxrd message as unread open my $fh, '<', $o or BAIL_OUT; my $s = do { local $/; <$fh> }; -$s =~ s/^Status: OR\n/Status: O\nX-Status: AF\n/sm or +$s =~ s/^Status: RO\n/Status: O\nX-Status: AF\n/sm or fail "failed to clear R flag in $s"; open $fh, '>', $o or BAIL_OUT; print $fh $s or BAIL_OUT; @@ -155,8 +155,8 @@ lei_ok(qw(q -o), "mboxrd:$o", 'm:bogus', @inc, lei_ok(qw(q -o), "mboxrd:$o", "m:$m", @inc); open $fh, '<', $o or BAIL_OUT; $s = do { local $/; <$fh> }; -like($s, qr/^Status: O\n/ms, 'seen keyword gone in mbox'); -like($s, qr/^X-Status: AF\n/ms, 'answered + flagged set'); +like($s, qr/^Status: O\nX-Status: AF\n/ms, + 'seen keyword gone in mbox, answered + flagged set'); lei_ok(qw(q --pretty), "m:$m", @inc); like($lei_out, qr/^ "kw": \["answered", "flagged"\],\n/sm, diff --git a/t/lei-q-remote-import.t b/t/lei-q-remote-import.t index 2293489a..25e461ac 100644 --- a/t/lei-q-remote-import.t +++ b/t/lei-q-remote-import.t @@ -80,7 +80,7 @@ From a@z Mon Sep 17 00:00:00 2001 From: nobody@localhost Date: Sat, 13 Mar 2021 18:23:01 +0600 Message-ID: -Status: RO +Status: OR whatever EOF @@ -89,7 +89,7 @@ EOF is_deeply($slurp_emls->($o), [$exp], 'got expected result after clobber') or diag $lei_err; lei_ok(qw(q -o mboxrd:/dev/stdout m:never-before-seen@example.com)); - like($lei_out, qr/seen\@example\.com>\nStatus: OR\n\nwhatever/sm, + like($lei_out, qr/seen\@example\.com>\nStatus: RO\n\nwhatever/sm, '--import-before imported totally unseen message'); }); done_testing; diff --git a/t/lei_to_mail.t b/t/lei_to_mail.t index 585db689..626bdab3 100644 --- a/t/lei_to_mail.t +++ b/t/lei_to_mail.t @@ -28,7 +28,7 @@ for my $mbox (@MBOX) { my $s = $cb->(PublicInbox::Eml->new($from), $smsg); is(substr($$s, -1, 1), "\n", "trailing LF in normal $mbox"); my $eml = PublicInbox::Eml->new($s); - is($eml->header('Status'), 'OR', "Status: set by $m"); + is($eml->header('Status'), 'RO', "Status: set by $m"); is($eml->header('X-Status'), 'AF', "X-Status: set by $m"); if ($mbox eq 'mboxcl2') { like($eml->body_raw, qr/^From /, "From not escaped $m");