#!/usr/bin/perl -w

# This expects input of the form:
# type    filenameprefix    commonname    emailaddress    password
# user    path/joeuser    Joe User    joeuser@org.com    8gfBl@#9
# host    path/twizzle    twizzle.org.com    joeuser@org.com
#
# Fiields are separated by tabs.  Users have passwords but
# hosts do not.

use strict;

my($ca_pass_file) = "capassfile.txt";

# This is necessary because if you do this with large input files,
# your machine will run out of randomness.
print STDERR "Running find to get randomness.\n";
my(@files) = grep(!/\'/, (`find /Users/ian/src`));
chomp @files;

print STDERR "Validating.\n";
my(@lines);

# Input validation pass
while(<>) {
    chomp;
    my($type,$filename,$commonname,$email,$password) = split /\t/;
    if(not ($type eq 'user' or $type eq 'host')) {
        die "Error in input: first field should be type, either 'user' or 'host'.";
    }

    my $input_file = "$filename.txt";
    my $key_file = "$filename.key";
    my $req_file = "$filename.req";
    my $cert_file = "$filename.crt";
    if(-e $key_file) {
        die "Key file exists: '$key_file'";
    }
    if(-e $req_file) {
        die "Request file exists: '$req_file'";
    }
    if(-e $cert_file) {
        die "Certificate file exists: '$cert_file'";
    }

    # How to validate common name?

    # Stronger validation of email addresses?
    # See: http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html
    # see also perlfaq9
    if(not $email =~ /^[\w.-]+\@(?:[\w-]+\.)+\w+$/) {
        die "Invalid email address: '$email'";
    }
    if($type eq 'user' and $password eq '') {
        die "User can't have null password.";
    }
    if($type eq 'host' and $password ne '') {
        die "Host must have null password.";
    }
    push @lines, $_;
}
print STDERR "Running.\n";
foreach (@lines) {
    chomp;
    my($type,$filename,$commonname,$email,$password) = split /\t/;

    my $input_file = "$filename.txt";
    my $key_file = "$filename.key";
    my $req_file = "$filename.req";
    my $cert_file = "$filename.crt";
    my $pass_file = "$filename.pass";

    # create input file
    open FF, ">$input_file" or die "Can't open '$input_file': $!";
    print FF "\n\n\n\n\n\n$commonname\n$email\n";
    close FF;

    # make the reqs and keys
    my $files = "'".$files[int(rand($#files))]."':'".$files[int(rand($#files))]."':'".$files[int(rand($#files))]."'";
    if($type eq 'user') {
        # create password file
        open FF, ">$pass_file" or die "Can't open '$pass_file': $!";
        print FF "$password\n";
        close FF;

        &docmd("openssl req -newkey rsa:1024 -keyout $key_file -passout file:$pass_file -config openssl.cnf -out $req_file -rand $files < $input_file");
        unlink($pass_file) or die "Can't delete '$pass_file': $!";
    } else {
        &docmd("openssl req -newkey rsa:1024 -keyout $key_file -nodes -config openssl.cnf -out $req_file -rand $files < $input_file");
    }
    &docmd("openssl ca -config openssl.cnf -passin file:$ca_pass_file -batch -out $cert_file -infiles $req_file");
}

sub docmd {
    my($cmd) = @_;
    print "$cmd\n";
    print `$cmd`;
}