NAME
Mail::DKIM2::BodyPlan - every Message-Instance's body hash in one pass
SYNOPSIS
use Mail::DKIM2::BodyPlan;
# The parsed Message-Instance fields, highest m= first.
my $plan = Mail::DKIM2::BodyPlan->new(@instances);
$plan->add($chunk) while defined($chunk = next_body_chunk());
$plan->finish;
for my $mi (@instances) {
my $m = $mi->get_tag('m');
die $plan->recipe_error($m) if $plan->recipe_error($m);
my $bh = $plan->body_hash($m, 'sha256'); # undef: no body to hash
}
DESCRIPTION
The undo walk checks a chain by rebuilding each previous body from the one above it and hashing it, so a chain of N instances copies and hashes the body N times. A BodyPlan reads the body Recipes first and works out, for every instance, which lines of the current body and which Recipe literals make up its body, in order. Copy ranges ascend (-07 section 5.2), so those lists are all in body order, and the current body is read once, front to back, in pieces of any size, each line going to the digests of the instances that hold it. No previous body is built, and the current one is never held whole. Instances with the same body (no body Recipe) share one set of digests.
Below a null body Recipe, or a body Recipe that does not undo, no instance has a body hash. The verdicts and their wording are those of "undo" in Mail::DKIM2::MessageInstance: Mail::DKIM2::Verifier's StreamBody option uses a BodyPlan in place of the undo walk's bodies.
METHODS
new(@instances)
The Mail::DKIM2::MessageInstance objects of the chain, highest m= first, each the parent of the next. Hashes are computed for each instance's h= algorithms that are implemented.
compose($segments, $recipe, \@literals)
A function, not a method: the segment list for what $recipe (copy ranges and literals, as "parse" in Mail::DKIM2::MessageInstance gives them) rebuilds from $segments, appending its literals to @literals. A segment is [0, $from, $to], a run of the original lines (or values), or [1, $from, $to], a run of @literals. Copy ranges must already be known to ascend; one past the end is cut short. Mail::DKIM2::HeaderPlan uses it too.
add($bytes)
The next piece of the current body, cut anywhere. A CRLF or a bare LF ends a line.
finish()
The body has all been added. Checks each body Recipe's copy ranges against the body it applies to.
body_hash($m, $alg)
The base64 body hash of instance $m by $alg, after finish; undef if that instance's body cannot be rebuilt.
recipe_error($m)
After finish, why instance $m's body Recipe does not undo, in "undo" in Mail::DKIM2::MessageInstance's words; undef if it does.
AUTHOR
Bron Gondwana <brong@fastmailteam.com>
COPYRIGHT AND LICENSE
Copyright (c) 2025-2026 Fastmail Pty Ltd. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.