#!/usr/bin/env perl

use 5.02;
use utf8;
use warnings;

our $VERSION = 1.01;

use Getopt::Long qw(GetOptions);
use Mail::Address;
use Mail::DKIM::Signer;
use Mail::Header;
use Mail::DKIM::TextWrap;
use Path::Tiny qw(path);

my $mh = Mail::Header->new( \*STDIN );

sub abort {
    warn "@_\n";
    $mh->print and say '' if $mh;
    local $/ = \4096;
    print while <STDIN>;
    exit;
}

GetOptions
  'key-basedir=s' => \( my $KeyBasedir = '/var/qmail/control/domainkeys' ),
  or abort 'options error';

my $from = $mh->get('From') or abort 'no RFC5322.From found';
($from) = Mail::Address->parse($from) or abort 'cannot parse RFC5322.From';
my $domain = $from->host;

my $keydir = path( $KeyBasedir, lc $domain );
abort qq(Key directory "$keydir" does not exist.) unless $keydir->exists;

my $selector_file = path( $keydir, 'selector' );
my $selector      = 'default';
chomp( $selector = $selector_file->slurp ) if $selector_file->exists;

my $keyfile = path( $keydir, $selector );
abort qq(Keyfile "$keyfile" does not exist.) unless $keyfile->exists;

my $signer = Mail::DKIM::Signer->new(
    Algorithm => 'rsa-sha256',
    Method    => 'relaxed/relaxed',
    Domain    => $domain,
    Selector  => $selector,
    KeyFile   => $keyfile,
);

$signer->PRINT("$_\cM\cJ") for split( /\cM?\cJ/, $mh->as_string ), '';
my @body;
while (<STDIN>) {
    push @body, $_;
    my $line = $_ =~ s/[\cM\cJ]+\z//r;
    $signer->PRINT("$line\cM\cJ");
}
$signer->CLOSE;

say for split( /\cM\cJ/, $signer->signature->as_string ), $mh->as_string;
print @body;

__END__

=encoding UTF-8

=head1 NAME

dkim-sign - filter to DKIM sign an e-mail

=head1 SYNOPSIS

In C</var/qmail/supervise/qmail-send/run>:

    exec env - PATH="/var/qmail/bin:$PATH" \
    QMAILLOCAL=/var/qmail/bin/spawn-filter \
    QMAILREMOTE=/var/qmail/bin/spawn-filter \
    FILTERARGS=dkim-sign \
    qmail-start "`cat /var/qmail/control/defaultdelivery`"
    /var/qmail/supervise/qmail-send/run

=head1 DESCRIPTION

This is meant as a drop-in replacement for dk-filter
to be used in conjunction with spawn-filter
to sign e-mails in a qmail-send context.
