## OpenCA - Public Web-Gateway Command
## (c) 1999-2009 by Massimiliano Pala and OpenCA Group
##
##   File Name: startAutoCRL
##       Brief: start Auto (On-Line) CRL Daemon
##     Version: $Revision: 1.10 $
## Description: this script starts the On-Line CRL Daemon
##  Parameters: 

use strict;

sub cmdStartAutoCRL {

	my $pid = undef;
	my $status = undef;
	my $pidfile = undef;
	my $params = undef;

	## Parameters used by the issuing certificate process

        our ($query, $errno, $errval, $cryptoShell, $ca_token, $DEBUG);
	our (%AUTOCONF);

	my $db = undef;

	$pidfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autocrl.pid";

	$status = libGetPidProcessStatus ( $pidfile );

	if( $status gt 0 ) {
		generalError( "ERROR, the On-Line Daemon is already active!");
	} elsif ( not defined $status ) {
		generalError("Can not open $pidfile!");
	}

	if( not defined $ca_token ) {
		$ca_token = crypto_get_ca_token();
		if( not $ca_token ) {
			print STDERR "ERROR::Can not get the CA token!\n";
			generalError( "ERROR, Can not get the CA_TOKEN." .
				" Please check the token password.\n", 99 );
		}
	}

	$pid = fork();
	if( $pid < 0 ) {
		generalError( "ERROR, can not spawn new processes!\n" );
	} elsif ( $pid > 0 ) {
		if( not open( FD, ">$pidfile")) {
			generalError("Can not write to $pidfile!");
		} else {
			print FD "$pid";
			close( FD );
		}

		$query->param ('cmd', 'statusAutoCRL' );
        	libExecuteCommand ();

		# success("Success",
		# 	"On-Line CRL Daemon started correctly ($pid)");
	} else {
		# This is the main loop for the auto-ca options
		# while( 1 ) {
		close (STDOUT);

		$SIG{HUP} = \&closeAutoCRL;

		# Load the Parameters
		$params = startAutoCRL_loadParams();

		if( $params->{'period_type'} =~ /seconds/i ) {
			$params->{'SLEEP'} = $params->{'period'};
		} elsif ( $params->{'period_type'} =~ /(minutes|hours)/i ) {
			$params->{'SLEEP'} = 60;
		} elsif ( $params->{'period_type'} =~/days/i ) {
			$params->{'SLEEP'} = 3600;
		} else {
			$params->{'SLEEP'} = 30;
		};

		if( not ( $db = newConnectDB()) ) {
			print STDERR "ERROR::Can not create new DB connect!\n";
			exit(1);
		} else {
			print STDERR "newConnectDB()->Ok.\n" if ( $DEBUG );
		}

		while( 1 ) {
			my $retVal = 0;

			if( $DEBUG ) {
			   print STDERR "On-Line CRL::Cycle Start!\n";
			   print STDERR "On-Line CRL::DB=>$db\n";
			   print STDERR "On-Line CRL::CRYPTO=>$ca_token\n";
			};

			if( not $db->connect()) {
				print STDERR "On-Line CRL::DB CONNECT ERROR::" . 
					$db->{errno} . " - " .
					$db->{errval} . "\n";
					next;
			} else {
				print STDERR "On-Line CRL::DB CONNECT Ok\n"
					if( $DEBUG );
			}

			$params->{CA_TOKEN} = $ca_token;
			$params->{DB} = $db;
			$retVal = autoCRLProcess( %{ $params } );

			$db->commit();

			if( not $db->disconnect()){
				print STDERR "On-Line CRL::DB DISCONNECT ERR::". 
					$db->{errno} . " - " .
					$db->{errval} . "\n";
			} else {
				print STDERR "On-Line CRL::DB DISCONNECT Ok\n"
					if( $DEBUG );
			} 

			# if( $retVal eq -666 ) {
			# 	closeAutoCRL("Unrecoverable Error!");
			# 	exit(1);
			# }

			sleep( $params->{SLEEP} );
		}
	}
}

sub autoCRLProcess {

    ## get the parameters
    ## Get the parameters
    my $params	 = { @_ };

    our ($query, $errno, $errval, $cryptoShell, $ca_token, $DEBUG);

    my ($request, $operator_cert, $operator_serial, $signature,
        $role_sig, $cert);

    ## Get required parameters from the configuration file
    my $cacert    = getRequired( 'CACertificate' );
    my $crlDir    = getRequired( 'CRLDir' );

    my $db = $params->{DB};

    ### unique DN?
    my $nouniqueDN = 0;
    if (getRequired ('UNIQUE_DN') =~ /NO|N|OFF/i) {
    	$nouniqueDN = 1;
    }

    if( $DEBUG ) {
      print STDERR "On-Line CRL::autoCRLProcess() started\n";
      print STDERR "On-Line CRL::Params::CA_TOKEN=>" . 
						$params->{CA_TOKEN} ."\n";
    };

    my $chainDir = getRequired('ChainDir');

    if( not $params ) {
    	print STDERR "On-Line CRL::autoCRLProcess()::Unrecoverable Error\n";
	return( 666 );
    }

    if( $DEBUG ) {
      print STDERR "On-LineCA::params::CA_TOKEY=>".$params->{CA_TOKEN}."\n";
    }
    
    my $tempDir = getRequired ('TempDir');

    ## loop
    ## there can never be a request 0
    my $key = 0;
    my $dataType = "VALID_CRL";
    my $issueCRL = 1;

    print STDERR "On-Line CRL::autoCRLProcess()::Start Request Listing\n"
	if ( $DEBUG);

    ## Get the list of CRLs (VALID)
    my @list = $db->searchItems( DATATYPE => $dataType,
				 MODE => "KEYLIST" );

    ## We need only the last valid issued CRL
    my $lastCRLSerial = $list[$#list];

    @list = undef;
    ## Refresh the autoCRL Parameters from the configuration file

    if ( ($params = startAutoCRL_loadParams()) == undef ) {
	print STDERR "On-Line CRL::autoCRLProcess()::Can not get params\n!";
	return undef;
    };

    ## Convert the issuing period in seconds and put it into a
    ## new value - sec_period 
    
    if ( $params->{'period_type'} =~ /minutes/i ) {
		$params->{'sec_period'} = $params->{'period'} * 60;
    } elsif ( $params->{'period_type'} =~ /hours/i ) {
	$params->{'sec_period'} = $params->{'period'} * 3600;
    } elsif ( $params->{'period_type'} =~ /days/i ) {
	$params->{'sec_period'} = $params->{'sec_period'} * 86400;
    };

    ## Convert the validitiy period in seconds and put it into a
    ## new value - sec_validity. Unfortunately the smallest unit
    ## we can use is hours (openssl shell limitation)
    
    $params->{'hours_validity'} = 1;

    if ( $params->{'validity_type'} =~ /hours/i ) {
	$params->{'hours_validity'} = $params->{'validity'};
    } elsif ( $params->{'validity_type'} =~ /days/i ) {
	$params->{'hours_validity'} = $params->{'validity'} * 24;
    };

    ## If no VALID CRL is found, we definitely have to issue a new one,
    ## therefore the default value of $issueCRL is 1
    
    if( $lastCRLSerial ) {

	## If lastCRL is available then let's see if it is time to issue
	## a new CRL by looking at the last_update field
	
	my $today = gmtime();
	my $numLastUpdate = 0;
	my $numToday = 0;

	my $lastCRL = $db->getItem( DATATYPE => "VALID_CRL", 
				    KEY => $lastCRLSerial );

	if( not defined ( $lastCRL )) {
		print STDERR "On-Line CRL::autoCRLProcess()::ERROR, ca not " .
			"get $lastCRLSerial CRL from DB!\n";
		return 1;
	}

    	my $lastCRL_lastUpdate = $lastCRL->getParsed()->{LAST_UPDATE};

	$numLastUpdate = $cryptoShell->getNumericDate ( $lastCRL_lastUpdate );
	$numToday = $cryptoShell->getNumericDate ( $today );

	my $diff = $numToday - $numLastUpdate;

	if ( $DEBUG ) {
		print STDERR "On-Line CRL::autoCRLProcess()::CRL last " .
			"update = " .  $numLastUpdate . " ( " . 
				$lastCRL_lastUpdate . " ) - CRL Serial [" . 
				"$lastCRLSerial]\n";

		print STDERR "On-Line CRL::autoCRLProcess()::Today = " .
			$numToday . " ($today)\n";
	}

	if ( $diff >= $params->{'sec_period'} ) {
		if ( $DEBUG ) {
			print STDERR "On-Line CRL::autoCRLProcess()::Time " .
				"to issue a new CRL ( " .  ($diff) . " > " .
				$params->{'sec_period'} . ")\n";
		};
		$issueCRL = 1;
	} else {
		if( $DEBUG ) {
			print STDERR "On-Line CRL::autoCRLProcess()::No CRL ".
				"to issue ($diff < " . 
					$params->{'sec_period'} . ")\n";
		};

		$issueCRL = 0;
	}
    }

    if ( $issueCRL ne 1 ) {
	return 1;
    };

    my $exts = $params->{'crl_ext'};

    if ( $exts =~ /^(none|off)$/i ){
	if ( $DEBUG ) {
		print STDERR "autoCRL::No extension selected for CRL ($exts)\n";
	};
	$exts = "";
    } else {
	if ( $DEBUG ) {
		print STDERR "autoCRL::Extensions selected for CRL " .
							"are ($exts)\n";
	}
    }

    ## Now let's generate the CRL
    my $CRL = new OpenCA::CRL (
                           SHELL      => $ca_token,
                           HOURS      => $params->{'hours_validity'},
                           EXTS       => $exts,
                           GETTEXT    => \&i18nGettext,
                           NOUNIQUEDN => $nouniqueDN );

    ## We need to save the default PEM format

    if ( open( FD, ">$crlDir/${$}_cacrl.pem" ) ) {
	print FD $CRL->getPEM();
	close(FD);
    } else {
	print STDERR "autoCRLProcess()::CRL saving Error " .
					"($crlDir/${$}_cacrl.pem)!\n";
	return undef;
    }

    ## Let's store the new CRL in the DB

    if( not $db->storeItem( DATATYPE=>"VALID_CRL",
                                        OBJECT=>$CRL, MODE=>"INSERT" )) {

	print STDERR "autoCRLProcess()::ERROR::Can not store CRL in DB!\n";
	return undef;
    }

    ## Now save all the default formats PEM, DER, CRL, TXT
    if( not ($tools->saveFile (FILENAME => "$crlDir/cacrl.pem",
                        			DATA => $CRL->getPEM()))) {
	print STDERR "autoCRLProcess()::ERROR::Can not save CRL to " .
				"$crlDir/cacrl.pem\n";
    }

    if(not ($tools->saveFile (FILENAME => "$crlDir/cacrl.der",
                        			DATA => $CRL->getDER()))) {
	print STDERR "autoCRLProcess()::ERROR::Can not save CRL to " .
				"$crlDir/cacrl.der\n";
    }

    if(not ($tools->saveFile (FILENAME => "$crlDir/cacrl.crl",
                        			DATA => $CRL->getDER()))) {
	print STDERR "autoCRLProcess()::ERROR::Can not save CRL to " .
				"$crlDir/cacrl.crl\n";
    }

    if (not $tools->saveFile (FILENAME => "$crlDir/cacrl.txt",
                        DATA       => $CRL->getTXT())) {

	print STDERR "autoCRLProcess()::ERROR::Can not save CRL to " .
				"$crlDir/cacrl.txt\n";
    }

    return 1;

}

sub getParamsStartAutoCRL {

    our ($query, $DEBUG);

    my $result = "";

    my $pidfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autocrl.pid";
    my $status = libGetPidProcessStatus ( $pidfile );

    if( $status gt 0 ) {
	return undef;
    };

    if (not $_[0]) {

	my %labels = undef;

	my $params = startAutoCRL_loadParams();

	my $crlPeriod = $query->newInput (
				-intype => 'textfield',
				-name   => 'period',
				-regx   => 'numeric',
				-class  => 'small',
				-default => $params->{'period'},
				 );

	%labels = ( 'Days'  => gettext ('Days'),
		    'Hours' => gettext ('Hours'),
		    'Minutes'  => gettext ('Minutes'),
		    'Seconds'  => gettext ('Seconds') );

	my $crlPeriodType = $query->newInput (
				-intype  => 'popup_menu',
				-name    => 'period_type',
				-regx    => '*',
				-default => $params->{'period_type'},
				-class  => 'small',
				-style   => 'min-width: 13em; width: 13em;',
				-values  => [ 'Days','Hours','Minutes', 
					      'Seconds' ],
				-labels  => \%labels );

	%labels = ( 'Days'  => gettext ('Days'),
		    'Hours' => gettext ('Hours') );

	my $crlValidityType = $query->newInput (
				-intype  => 'popup_menu',
				-name    => 'validity_type',
				-regx    => '*',
				-default => $params->{'validity_type'},
				-class  => 'small',
				-style   => 'min-width: 13em; width: 13em;',
				-values  => [ 'Days','Hours' ],
				-labels  => \%labels );

	%labels = ('crl_ext'=> gettext('Default'), 'None' => gettext('None') );
        my $crlExtensions = $query->newInput (
                		-regx=>'LETTERS',
                		-intype=>'popup_menu',
                		-name=>'crl_ext',
                		-default=> $params->{'crl_ext'},
                		-values=>[ 'crl_ext', 'None'],
                		-labels=>\%labels );

	my $crlValidity = $query->newInput (
				-regx=>'NUMERIC',
				-intype=>'textfield',
				-name=>'validity',
				-class=>'small',
				-default=>$params->{'validity'} );

        $result = "<table class=\"getParams\">\n";
        $result .= "<tr><td colspan=\"2\">\n";
	$result .= "<center><div style='font-size: 120%;'><h3>" . 
		       gettext("Auto CRL Issuing System")."</h3>" .
		   "</div></center>";

	$result .=  "<div class=\"description\" style='margin: 10px;'>" .
		    gettext (
		    "The following information will be used by the " .
		    "automatic CRL issuing system in order to issue CRLs " .
		    "according to your needs. " .
		    "Remeber that although the configuration options are ".
		    "stored on your system, if the OpenCA server is " .
		    "rebooted you will need to activate the system again."
		    ) .
		    "</div>";
        $result .= "</td></tr>\n";

        $result .= "<tr><td colspan=\"2\">\n";
	$result .= "<center><h3>" . 
			gettext("CRL Issuing Details") . "</h3>" .
		   "</center>";
        $result .= "</td></tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("Issue CRL Every")."</td>\n".
                   "    <td>".$crlPeriod . " " . $crlPeriodType ."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("CRL Validity")."</td>\n".
                   "    <td>". $crlValidity . " " . $crlValidityType."</td>\n".
                   "  </tr>\n";

        $result .= "  <tr>\n".
                   "    <td class='desclabel'>".gettext ("CRL Extensions")."</td>\n".
                   "    <td>" . $crlExtensions . "</td>\n".
                   "  </tr>\n";

        $result .= "</table>\n";

    } else {
	# We do have the parameters, let's save them!
	startAutoCRL_saveParams ();
    };


    return $result;

};


sub closeAutoCRL {

	my $reason = shift;
	my $statusfile = undef;

	our ($db, %AUTOCONF);

	$statusfile = $AUTOCONF{"var_prefix"}."/tmp/openca_autocrl_status.txt";

	if( ($reason eq "HUP") or ($reason eq "") ) {
		$reason = gettext ( "Process Stopped from CA Interface" );
	}

	if( $db ) {
		$db->disconnect();
	}

	# Unrecoverable Error
	if( not open( FD, ">$statusfile")) {
		exit(1);
	} else {
		print FD "$reason";
		close( FD );
	}
	exit(1);
};

sub startAutoCRL_saveParams {

	my $ret = undef;

	our ( $query, %AUTOCONF );

	my $conf = $AUTOCONF{"var_prefix"}."/db/openca_autocrl.cnf";

	return libSaveCgiParams ( $conf );

	return ( $ret );
}

sub startAutoCRL_loadParams {

	my $ret = undef
	my $savedQuery = undef;
	my $defaults = undef;

	$defaults->{'period'} = '1';
	$defaults->{'period_type'} = gettext('Days');
	$defaults->{'validity'} = '1';
	$defaults->{'validity_type'} = gettext ('Days');
	$defaults->{'crl_ext'} = 'crl_ext';

	our ( $query, %AUTOCONF );

	my $conf = $AUTOCONF{"var_prefix"}."/db/openca_autocrl.cnf";

	return libLoadCgiParams ( $conf, $defaults );
}

sub autoCRL_isIn {
	my $val = shift;
	my ( $list ) = @_ ;

	foreach my $i ( @{ $list } ) {
		if( $val =~ /^$i$/i ) {
			return 1;
		}
	}

	return undef;
}

1;
