--- dkimproxy.out.OLD	2007-05-01 14:31:02.000000000 +0200
+++ dkimproxy.out.NEW	2007-05-02 11:48:33.000000000 +0200
@@ -18,6 +18,8 @@
 #   GNU General Public License for more details.
 #
 # Written by Bennett Todd <bet@rahul.net>
+# Support for multiple keys and configuration file added by
+#         Xavier Perseguers <xavier.perseguers@skutale.ch>
 
 use warnings;
 use strict;
@@ -30,21 +32,31 @@
 use Mail::DKIM 0.20;
 use Mail::DKIM::Signer;
 use MySmtpServer;
+use AppConfig::File;
 
 my $reject_error = 0;
 my $signature = "dkim";
-my $keyfile;
-my $selector;
-my $domain_arg;
-my $method;
+my $configfile;
+my %signatures;
+my %methods;
+my %selectors;
+my %keys;
 my $debugtrace = undef;
-my @domains;
+my $domains;
 
 use base "MySmtpProxyServer";
 main->run(
 		server_type => "PreFork",
 	);
 
+sub check_private_key
+{
+	my $var   = shift;
+	my $val   = shift;
+
+	return (-r $val) ? 1 : 0;
+}
+
 sub configure
 {
 	my $self = shift;
@@ -54,10 +66,7 @@
 	GetOptions(
 			"reject-error" => \$reject_error,
 			"signature=s" => \$signature,
-			"keyfile=s" => \$keyfile,
-			"selector=s" => \$selector,
-			"domain=s" => \$domain_arg,
-			"method=s" => \$method,
+			"configfile=s" => \$configfile,
 			"debugtrace=s" => \$debugtrace,
 			"daemonize" => \$self->{server}->{setsid},
 			"pidfile=s" => \$self->{server}->{pid_file},
@@ -84,39 +93,87 @@
 	{
 		pod2usage("Error: invalid signature type specified");
 	}
-	unless (defined $keyfile)
+	unless (defined $configfile)
 	{
-		pod2usage("Error: no keyfile specified");
+		pod2usage("Error: no configfile specified");
 	}
-	unless (-r $keyfile)
+	unless (-r $configfile)
 	{
-		pod2usage("Error: cannot read keyfile $keyfile");
+		pod2usage("Error: cannot read configfile $configfile");
 	}
-	unless (defined $selector)
+
+	# Parse the configuration file
+
+	my $state = AppConfig::State->new();
+
+	$state->define(
+		'domain_name', {
+			ARGCOUNT => AppConfig::ARGCOUNT_LIST
+		},
+		'domain_signature', {
+			ARGCOUNT => AppConfig::ARGCOUNT_LIST
+		},
+		'domain_selector', {
+			ARGCOUNT => AppConfig::ARGCOUNT_LIST
+		},
+		'domain_method', {
+			ARGCOUNT => AppConfig::ARGCOUNT_LIST
+		},
+		'domain_private_key', {
+			ARGCOUNT => AppConfig::ARGCOUNT_LIST,
+			VALIDATE => \&check_private_key
+		}
+	);
+
+	my $config = AppConfig::File->new($state);
+	$config->parse($configfile);
+
+	$domains     = $state->get('domain_name');
+	my $_signatures = $state->get('domain_signature');
+	my $_methods    = $state->get('domain_method');
+	my $_selectors  = $state->get('domain_selector');
+	my $_keys       = $state->get('domain_private_key');
+
+	unless ( scalar @$domains == scalar @$_signatures )
 	{
-		pod2usage("Error: selector not specified");
+		pod2usage("Error: number of signatures is different than number of domains");
 	}
-	unless (defined $domain_arg)
+	unless ( scalar @$domains == scalar @$_methods )
 	{
-		pod2usage("Error: domain not specified");
+		pod2usage("Error: number of methods is different than number of domains");
 	}
-	@domains = split(/,\s*/, $domain_arg);
-	# check method argument
-	if ($signature eq "dkim")
+	unless ( scalar @$domains == scalar @$_selectors )
 	{
-		$method ||= "relaxed";
-		unless (grep { $method eq $_ } qw(simple relaxed nowsp))
-		{
-			die "Error: invalid method $method\n";
-		}
+		pod2usage("Error: number of selectors is different than number of domains");
 	}
-	elsif ($signature eq "domainkeys")
+	unless ( scalar @$domains == scalar @$_keys )
 	{
-		$method ||= "nofws";
-		unless (grep { $method eq $_ } qw(simple nofws))
+		pod2usage("Error: number of private keys is different than number of domains");
+	}
+
+	for ( my $i = 0; $i < scalar @$domains; $i++ )
+	{
+		# check method argument
+		if (@$_signatures[$i] eq "dkim")
+		{
+			$methods{ @$domains[$i] } ||= "relaxed";
+			unless (grep { $methods{ @$domains[$i] } eq $_ } qw(simple relaxed nowsp))
+			{
+				die "Error: invalid method ".$methods{ @$domains[$i] }."\n";
+			}
+		}
+		elsif (@$_signatures[$i] eq "domainkeys")
 		{
-			die "Error: invalid method $method\n";
+			$methods{ @$domains[$i] } ||= "nofws";
+			unless (grep { $methods{ @$domains[$i] } eq $_ } qw(simple nofws))
+			{
+				die "Error: invalid method ".$methods{ @$domains[$i] }."\n";
+			}
 		}
+
+		$signatures{ @$domains[$i] } = @$_signatures[$i];
+		$selectors{ @$domains[$i] } = @$_selectors[$i];
+		$keys{ @$domains[$i] } = @$_keys[$i];
 	}
 }
 
@@ -126,9 +183,9 @@
 
 	# create an object for sending the outgoing SMTP commands
 	#  (and the signed message)
-    my $client = MSDW::SMTP::Client->new(
-			interface => $self->{server}->{dest_host},
-			port => $self->{server}->{dest_port});
+	my $client = MSDW::SMTP::Client->new(
+		interface => $self->{server}->{dest_host},
+		port => $self->{server}->{dest_port});
 	return $client;
 }
 
@@ -167,13 +224,16 @@
 	my $dkim;
 	my $result;
 	my $result_detail;
+
 	eval
 	{
+		# Default values to first configured domain
+		my $default_domain = @$domains[0];
 		$dkim = Mail::DKIM::Signer->new_object(
 				Policy => "MySignerPolicy",
-				KeyFile => $keyfile,
-				Method => $method,
-				Selector => $selector,
+				KeyFile => $keys{ $default_domain },
+				Method => $methods{ $default_domain },
+				Selector => $selectors{ $default_domain }
 			);
 
 		$dkim->load($fh);
@@ -245,15 +305,20 @@
 
 	# determine what domain to use
 	my $domain;
+
 	if ($domain = lc $signer->message_sender->host)
 	{
 		while ($domain)
 		{
-			if (grep { lc($_) eq $domain } @domains)
+			if (grep { lc($_) eq $domain } @$domains)
 			{
 				$signer->domain($domain);
+				$signer->method( $methods{ $domain } );
+				$signer->selector( $selectors{ $domain } );
+				my $keyfile = $keys{ $domain };
+				$signer->{private} = Mail::DKIM::PrivateKey->load(File => $keyfile);
 
-				if ($signature eq "dkim")
+				if ($signatures{ $domain } eq "dkim")
 				{
 					# construct a DKIM-Signature
 					$signer->add_signature(
@@ -265,7 +330,7 @@
 							Selector => $signer->selector,
 							));
 				}
-				elsif ($signature eq "domainkeys")
+				elsif ($signatures{ $domain } eq "domainkeys")
 				{
 					# construct a DomainKey-Signature
 					$signer->add_signature(
@@ -295,11 +360,9 @@
 
 =head1 SYNOPSIS
 
-  dkimproxy.out [options] --keyfile=FILENAME --selector=SELECTOR \
-                 --domain=DOMAIN listen.addr:port talk.addr:port
+  dkimproxy.out [options] --configfile=FILENAME \
+                 listen.addr:port talk.addr:port
     options:
-      --signature=dkim|domainkeys
-      --method=simple|nowsp|relaxed|nofws
       --reject-error
 
     daemon options:
@@ -311,6 +374,26 @@
   dkimproxy.out --help
     to see a full description of the various options
 
+    Format of the configuration file:
+
+       # ------------------------------------------
+       # domain 1
+       [domain]
+       name        = domain.tld
+       signature   = SIGNATURE
+       method      = METHOD
+       selector    = SELECTOR
+       private_key = FILENAME
+
+       # domain 2
+       [domain]
+       name        = otherdomain.tld
+       signature   = SIGNATURE
+       method      = METHOD
+       selector    = SELECTOR
+       private_key = FILENAME
+       # ------------------------------------------
+
 =head1 OPTIONS
 
 =over
@@ -319,29 +402,29 @@
 
 If specified, the server will run in the background.
 
-=item B<--domain=DOMAIN>
-
-This is a required argument. Use it to specify what domain(s) emails
-are signed for. If you want to sign for multiple domains, specify the
-domains separated by commas. As messages are delivered through the proxy,
-the proxy will attempt to match the message to one of the domains
-specified in this argument. If it sees a match, it will sign the message
-using the matching domain.
-
 =item B<--group=GROUP>
 
 If specified, the daemonized process will setgid() to the specified
 GROUP.
 
-=item B<--keyfile=FILENAME>
+=item B<--configfile=FILENAME>
 
 This is a required argument. Use it to specify the filename containing
-the private key used in signing outgoing messages. For messages to
+the configuration options used in signing outgoing messages. For messages to
 verify, you will need to publish the corresponding public key in
-DNS, using the selector name specified by C<--selector>, under
-the domain(s) specified in C<--domain>.
+DNS, using the selector name specified by C<SELECTOR>, under
+the domain specified in C<domain>.
+
+=item B<DOMAIN>
+
+This is a required argument. Use it to specify what domain(s) emails
+are signed for. If you want to sign for multiple domains, specify the
+domains separated by commas. As messages are delivered through the proxy,
+the proxy will attempt to match the message to one of the domains
+specified in this argument. If it sees a match, it will sign the message
+using the matching domain.
 
-=item B<--method=simple|nowsp|relaxed|nofws>
+=item B<METHOD=simple|nowsp|relaxed|nofws>
 
 This option specifies the canonicalization algorithm to use for signing
 messages. For DKIM signatures, the options are C<simple>, C<nowsp>, or
@@ -363,12 +446,12 @@
 exact error code used). If this option is not specified, the message
 will be allowed to pass through without having a signature added.
 
-=item B<--selector=SELECTOR>
+=item B<SELECTOR>
 
 This is a required argument. Use it to specify the name of the key
 selector.
 
-=item B<--signature=dkim|domainkeys>
+=item B<dkim|domainkeys>
 
 This specifies what type of signature to add. Use C<dkim> to sign with
 IETF-standardized DKIM signatures. Use C<domainkeys> to sign with
@@ -395,8 +478,8 @@
 
 For example, if dkimproxy.out is started with:
 
-  dkimproxy.out --keyfile=private.key --selector=postfix \
-          --domain=example.org 127.0.0.1:10027 127.0.0.1:10028
+  dkimproxy.out --configfile=/etc/dkfilter.conf \
+          127.0.0.1:10027 127.0.0.1:10028
 
 the proxy will listen on port 10027 and send the signed messages to
 some other SMTP service on port 10028.

