6 our $VERSION = "1.05 - 2018-10-08";
7 our $cmd = $0 =~ s{.*/}{}r;
10 my $err = shift and select STDERR
;
11 say "usage: $cmd file ...";
18 "help|?" => sub { usage
(0); },
19 "V|version" => sub { say "$cmd [$VERSION]"; exit 0; },
24 foreach my $fn (@ARGV) {
26 open my $fh, "<", $fn or die "$fn: $!\n";
27 my ($hdr, $body) = split m/(?<=\n)(?=\r?\n)/ => do { local $/; <$fh> }, 2;
30 $hdr && $hdr =~ m/\b(?:Date|Received)\b/ or next;
32 my ($mid) = $hdr =~ m{^Message-Id: (?:[\x20\t]*\n)?[\x20\t]+ (\S.*)}xmi;
33 my ($dte) = $hdr =~ m{^Date: (?:[\x20\t]*\n)?[\x20\t]+ (\S.*)}xmi;
34 my ($rcv) = $hdr =~ m{\nReceived: (?:[\x20\t]*\n)?[\x20\t]+ (\S.*(?:\n\s+.*)*+)}xi;
35 my ($irt) = $hdr =~ m{^In-Reply-To: (?:[\x20\t]*\n)?[\x20\t]+ (\S.*)}xmi;
36 my ($ref) = $hdr =~ m{^References: (?:[\x20\t]*\n)?[\x20\t]+ (\S.*)}xmi;
39 $rcv =~ s/[\s\r\n]+/ /g;
42 $rcv =~ s/.* id \S+\s+//i;
43 my $stamp = str2time
($rcv) or die $rcv;
44 my $date = $stamp ?
do {
45 my @d = localtime $stamp;
46 sprintf "%4d-%02d-%02d %02d:%02d:%02d", $d[5] + 1900, ++$d[4], @d[3,2,1,0];
48 #printf "%12s %-20s %s\n", $stamp // "-", $date, $rcv;
64 $stamp < $f{$p}{stamp
} and $p = $fn;
67 # All but the oldest will refer to the oldest as parent
70 my $pid = $f{$p}{msg_id
} or die "Parent file $p has no Message-Id\n";
72 foreach my $fn (sort keys %f) {
80 unless ($f->{refs
} eq $pid) {
82 $f->{hdr
} =~ s{^(?=References:)}{References: $pid\nX-}mi;
87 $f->{hdr
} =~ s{^(?=Message-Id:)}{References: $pid\n}mi;
90 unless ($f->{irt
} eq $pid) {
92 $f->{hdr
} =~ s{^(?=In-Reply-To:)}{In-Reply-To: $pid\nX-}mi;
97 $f->{hdr
} =~ s{^(?=Message-Id:)}{In-Reply-To: $pid\n}mi;
100 $c or next; # No changes required
102 unless ($f->{msg_id
}) {
103 warn "Child message $fn has no Message-Id, skipped\n";
107 say "$f->{msg_id} => $pid";
110 open my $fh, ">", $fn or die "$fn: $!\n";
111 print $fh $f->{hdr
}, $f->{body
};
112 close $fh or die "$fn: $!\n";
113 utime $t[8], $t[9], $fn;
120 cm-reparent.pl - fix mail threading
124 cm-reparent.pl ~/Mail/inbox/23 ~/Mail/inbox/45 ...
128 This script should be called from within Claws-Mail as an action
132 Menu name: Reparent (fix threading)
133 Command: cm-reparent.pl %F
135 Then select from the message list all files that should be re-parented
137 Then invoke the action
139 All but the oldest of those mails will be modified (if needed) to
140 reflect that the oldest mail is the parent of all other mails by
141 adding or altering the header lines C<In-Reply-To:> and C<References:>
143 Given 4 files A, B, C, and D like
146 A 123AC_12 2016-06-01 12:13:14
147 B aFFde2993 2016-06-01 13:14:15
148 C 0000_1234 2016-06-02 10:18:04
149 D foo_bar_12 2016-06-03 04:00:00
151 The new tree will be like
153 A 123AC_12 2016-06-01 12:13:14
154 +- B aFFde2993 2016-06-01 13:14:15
155 +- C 0000_1234 2016-06-02 10:18:04
156 +- D foo_bar_12 2016-06-03 04:00:00
160 A 123AC_12 2016-06-01 12:13:14
161 +- B aFFde2993 2016-06-01 13:14:15
162 +- C 0000_1234 2016-06-02 10:18:04
163 +- D foo_bar_12 2016-06-03 04:00:00
165 Existing entries of C<References:> and C<In-Reply-To:> in the header
166 of any of B, C, or D will be preserved as C<X-References:> or
167 C<X-In-Reply-To:> respectively.
171 L<Date::Parse>, L<Claws Mail|http://www.claws-mail.org>
176 H.Merijn Brand <h.m.brand@xs4all.nl>
178 =head1 COPYRIGHT AND LICENSE
180 Copyright (C) 2016-2018 H.Merijn Brand. All rights reserved.
182 This library is free software; you can redistribute and/or modify it under
183 the same terms as Perl itself.
184 See the L<Artistic license|http://dev.perl.org/licenses/artistic.html>.