#!/usr/bin/perl -w

# $Id: ca-recv 197 2005-05-04 12:56:21Z lfousse $

# Copyright (c) 1998 Ian Jackson
#           (c) 2001, 2003, 2004 Peter Palfrader
#           (c) 2005 Laurent Fousse
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Privacy Guard; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.


use strict;
use File::Path;
use Cabot qw(%CONFIG getkeydir ask initialize_cabot);

umask(0007);

&initialize_cabot();

die "sendmail not configured, please fix your configuration"
    unless defined($CONFIG{'sendmail'});

die "`tosign' directory not configured, please fix your configuration."
    unless defined($CONFIG{'tosign'});

#
# is this chdir really necesarry? if not, we can get rid of $CONFIG{'cabothome'}.
#
chdir($CONFIG{'cabothome'});

sub send_mail($$) {
	my ($subject, $body) = @_;
	
	my $msg = << "EOF";
Subject: $subject
To: $CONFIG{'name'}
Bcc: $CONFIG{'BCCmail'}
From: $CONFIG{'bot'}

$body
EOF
	open(MAIL, $CONFIG{'sendmail'}) || die ("Cannot execute sendmail: $!\n");
	print MAIL $msg;
	close(MAIL);
};

my @in;

sub process_one_reply ($$$) {
	my ($keyid, $magic, $upload) = @_;

	unless ( -d getkeydir('sentdir', $keyid) ) {
		send_mail("CABOT: key does not exist in sentdir", "Key $keyid does not exist in sentdir:\n\n@in\n");
		return;
	};

	unless (opendir(DIR, getkeydir('sentdir', $keyid))) {
		send_mail("CABOT: error opening sentdir", "Error opening ".getkeydir('sentdir', $keyid).": $!\nOriginal message:\n\n@in\n");
		return;
	};

	my @uids= grep { ! /^\./ } readdir(DIR);
	close(DIR);

	my $ok=0;
	for my $uid (@uids) {
		unless (open(MAGIC, getkeydir('sentdir', $keyid, $uid).'/MAGIC')) {
			send_mail("CABOT: error opening MAGIC", 
				  "Error opening ".getkeydir('sentdir', $keyid, $uid)."/MAGIC: $!\nOriginal message:\n\n@in\n");
			return;
		};
		my $storedmagic = <MAGIC>;
		close(MAGIC);
		if ($storedmagic eq $magic) {
			$ok = $uid;
			last;
		};
	};

	unless ($ok) {
		send_mail("CABOT: could not find a matching magic", "could not find a matching magic\nOriginal message:\n\n@in\n");
		return;
	};

	my $uid = $ok;

	unless ( -d getkeydir('tosign', $keyid) ) {
		unless (-d $CONFIG{'tosign'}) {
	    		unless (mkpath($CONFIG{'tosign'}, 0, 0711)) {
		    		send_mail("CABOT: Cannot create dir",
		          	"Cannot create dir ".$CONFIG{'tosign'}.": $!\nOriginal message:\n\n@in\n");
		    		return;
            		}
        	}
		unless (mkpath(getkeydir('tosign', $keyid), 0, 0771)) {
			send_mail("CABOT: Cannot create dir in tosign", 
			  "Cannot create dir ".getkeydir('tosign', $keyid).": $!\nOriginal message:\n\n@in\n");
		return;
		};
	};

	unless (rename(getkeydir('sentdir', $keyid, $uid), getkeydir('tosign', $keyid, $uid)) ) {
		send_mail("CABOT: Cannot move to tosign", "Cannot move ".getkeydir('sentdir', $keyid, $uid)." to ".
	          getkeydir('tosign', $keyid, $uid).": $!\nOriginal message:\n\n@in\n");
		return;
	};

	rmdir( getkeydir('sentdir', $keyid)); # may fail


	unless (open(LOG, '>'. getkeydir('tosign', $keyid, $uid).'/LOG')) {
		send_mail("CABOT: Cannot create file in tosign", 
	          "Cannot write to ".getkeydir('tosign', $keyid, $uid)."/LOG $!\nOriginal message:\n\n@in\n");
		return;
	};
	print LOG @in;
	close(LOG);

	if ($upload) {
		unless (open(UPLOAD, '>'. getkeydir('tosign', $keyid, $uid).'/upload')) {
			send_mail("CABOT: Cannot create file Upload", 
		          "Cannot write to ".getkeydir('tosign', $keyid, $uid)."/upload $!\nOriginal message:\n\n@in\n");
			return;
		};
		close(UPLOAD);
	};

	send_mail("CABOT: new key in tosign", "Keyid: $keyid\nUid: $uid\n\nOriginal message:\n\n@in\n") unless (defined($CONFIG{'want_recv_notice'}) && ($CONFIG{'want_recv_notice'} eq "no"));

}

my ($keyid, $magic, $upload);
my $found_one = 0;

@in = <STDIN>;

foreach (@in) {
	if (/Key: ([0-9A-F]{8})/) {
		if (defined($keyid)) {
			if (defined($magic)) {
				$found_one = 1;
				process_one_reply($keyid, $magic, $upload);
				$keyid = $1;
				undef $magic;
				undef $upload;
			}
			else {
				send_mail("CABOT: could not parse reply", "Could not parse this message successfully:\n\n@in\n");
				exit(0);
			}
		}
		else {
			$keyid = $1;
		}
	}
	if (/Magic: ([0-9A-F]*)/) {
		if (defined($magic)) {
			if (defined($keyid)) {
				$found_one = 1;
				process_one_reply($keyid, $magic, $upload);
				undef $keyid;
				$magic = $1;
				undef $upload;
			}
			else {
				send_mail("CABOT: could not parse reply", "Could not parse this message successfully:\n\n@in\n");
				exit(0);
			}
		}
		else {
			$magic = $1;
		}
	}
	if (/Upload to keyservers: (Yes)/) {
		if (defined($upload)) {
			if ((defined($keyid)) && (defined($magic))) {
				$found_one = 1;
				process_one_reply($keyid, $magic, $upload);
				undef $keyid;
				undef $magic;
				$upload = $1;
			}
			else {
				send_mail("CABOT: could not parse reply", "Could not parse this message successfully:\n\n@in\n");
				exit(0);
			}
		}
		else {
			$upload = $1;
		}
	}
}

if ((defined($keyid)) && (defined($magic))) {
	process_one_reply($keyid, $magic, $upload);
	$found_one = 1;
}

unless ($found_one) {
	send_mail("CABOT: no reply found", "Could not find a valid reply in:\n\n@in\n");
}

__END__

=pod

=head1 NAME

ca-recv - parse email message, holding a reply to a cabot GPG challenge

=head1 SYNOPSIS

B<ca-recv>

=head1 DESCRIPTION

B<ca-recv> expects an email message on stdin.  In this message, it looks
for lines containing

 Key:
 Magic:
 Upload to keyservers:

.

It verifies the Magic: against the cookie stored in sentdir/MAGIC, and exits if
this fails.

It creates tosign/LOG, and, if the mail contains a request for uploading,
tosign/upload.  It mails a report to C<name> as specified in ca-config(5).
If everything went fine, this report looks like:

 Subject: CABOT: new key in tosign
 To: John Doe <john@example.com>
 From: CA Bot running on behalf of John Doe <john-cabot@example.com>

 Keyid: ABC01234
 Uid: Foo Bar <foo@example.com>

 Original message:
 <the complete original email message>

Messages on errors, if any, are mailed to C<name> too.

B<ca-recv> is part of the ca-bot(7) process; typically, ca-dosign(1) is run
after B<ca-recv>.

=head1 SEE ALSO

ca-bot(7), ca-config(5)

=head1 VERSION

$Id: ca-recv 197 2005-05-04 12:56:21Z lfousse $

=head1 AUTHOR

Peter Palfrader, based upon work by Ian Jackson

=cut


