The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

# Copyright 2005-2007 Messiah College. All rights reserved.
# Jason Long <jlong@messiah.edu>

# Copyright (c) 2004 Anthony D. Urso. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.

use strict;
use warnings;

package Mail::DKIM::Canonicalization::DkimCommon;
use base 'Mail::DKIM::Canonicalization::Base';
use Carp;

sub init {
    my $self = shift;
    $self->SUPER::init;

    $self->{body_count}     = 0;
    $self->{body_truncated} = 0;

    # these canonicalization methods require signature to use
    $self->{Signature}
      or croak 'no signature specified';
}

# similar to code in DkCommon.pm
sub add_header {

    #Note: canonicalization of headers is performed
    #in finish_header()
}

sub finish_header {
    my $self = shift;
    my %args = @_;

    # Headers are canonicalized in the order specified by the h= tag.
    # However, in the case of multiple instances of the same header name,
    # the headers will be canonicalized in reverse order (i.e. "from
    # the bottom of the header field block to the top").
    #
    # This is described in 5.4 of RFC4871.

    # Since the bottom-most headers are to get precedence, we reverse
    # the headers here... (now the first header matching a particular
    # name is the header to insert)
    my @mess_headers = reverse @{ $args{Headers} };

    # presence of a h= tag is mandatory...
    unless ( defined $self->{Signature}->headerlist ) {
        die "Error: h= tag is required for this canonicalization\n";
    }

    # iterate through the header field names specified in the signature
    my @sig_headers = $self->{Signature}->headerlist;
    foreach my $hdr_name (@sig_headers) {
        $hdr_name = lc $hdr_name;

        # find the specified header in the message
      inner_loop:
        for ( my $i = 0 ; $i < @mess_headers ; $i++ ) {
            my $hdr = $mess_headers[$i];

            if ( $hdr =~ /^([^\s:]+)\s*:/ ) {
                my $key = lc $1;
                if ( $key eq $hdr_name ) {

                    # found it

                    # remove it from our list, so if it occurs more than
                    # once, we'll get the next header in line
                    splice @mess_headers, $i, 1;

                    $hdr =~ s/\015\012\z//s;
                    $self->output(
                        $self->canonicalize_header($hdr) . "\015\012" );
                    last inner_loop;
                }
            }
        }
    }
}

sub add_body {
    my $self = shift;
    my ($multiline) = @_;

    $multiline = $self->canonicalize_body($multiline);
    if ( $self->{Signature} ) {
        if ( my $limit = $self->{Signature}->body_count ) {
            my $remaining = $limit - $self->{body_count};
            if ( length($multiline) > $remaining ) {
                $self->{body_truncated} += length($multiline) - $remaining;
                $multiline = substr( $multiline, 0, $remaining );
            }
        }
    }

    $self->{body_count} += length($multiline);
    $self->output($multiline);
}

sub finish_body {
}

sub finish_message {
    my $self = shift;

    if ( $self->{Signature} ) {
        $self->output("\015\012");

        # append the DKIM-Signature (without data)
        my $line = $self->{Signature}->as_string_without_data;

        # signature is subject to same canonicalization as headers
        $self->output( $self->canonicalize_header($line) );
    }
}

sub body_count {
    my $self = shift;
    return $self->{body_count};
}

sub body_truncated {
    my $self = shift;
    return $self->{body_truncated};
}

1;

__END__

=head1 NAME

Mail::DKIM::Canonicalization::DkimCommon - common canonicalization methods

=head1 DESCRIPTION

This class implements functionality that is common to all the
currently-defined DKIM canonicalization methods, but not necessarily
common with future canonicalization methods.

For functionality that is common to all canonicalization methods
(including future methods), see Mail::DKIM::Canonicalization::Base.

=head1 SEE ALSO

Mail::DKIM::Canonicalization::Base

=cut