]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/ContentId.pm
279eec0c1e06f742c2e49a7b91f9f5750b76c0a9
[public-inbox.git] / lib / PublicInbox / ContentId.pm
1 # Copyright (C) 2018 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 package PublicInbox::ContentId;
5 use strict;
6 use warnings;
7 use base qw/Exporter/;
8 our @EXPORT_OK = qw/content_id content_digest/;
9 use PublicInbox::MID qw(mids references);
10
11 # not sure if less-widely supported hash families are worth bothering with
12 use Digest::SHA;
13
14 sub content_digest ($) {
15         my ($mime) = @_;
16         my $dig = Digest::SHA->new(256);
17         my $hdr = $mime->header_obj;
18
19         # References: and In-Reply-To: get used interchangeably
20         # in some "duplicates" in LKML.  We treat them the same
21         # in SearchIdx, so treat them the same for this:
22         my %seen;
23         foreach my $mid (@{mids($hdr)}) {
24                 # do NOT consider the Message-ID as part of the content_id
25                 # if we got here, we've already got Message-ID reuse
26                 $seen{$mid} = 1;
27         }
28         foreach my $mid (@{references($hdr)}) {
29                 next if $seen{$mid};
30                 $dig->add('ref: '.$mid);
31         }
32
33         # Only use Sender: if From is not present
34         foreach my $h (qw(From Sender)) {
35                 my @v = $hdr->header_raw($h);
36                 if (@v) {
37                         $dig->add("$h: $_") foreach @v;
38                         last;
39                 }
40         }
41
42         # Content-* headers are often no-ops, so maybe we don't need them
43         foreach my $h (qw(Subject Date To Cc)) {
44                 my @v = $hdr->header_raw($h);
45                 $dig->add("$h: $_") foreach @v;
46         }
47         $dig->add($mime->body_raw);
48         $dig;
49 }
50
51 sub content_id ($) {
52         content_digest($_[0])->digest;
53 }
54
55 1;