#!/usr/bin/perl -w

# (c) 2006-2007 by CAcert.org

# Server (running on the certificate machine)

use strict;
use Device::SerialPort qw( :PARAM :STAT 0.07 );
use POSIX;
use IO::Select;
use File::CounterFile;
use Time::HiRes q(usleep);
use IPC::Open3;
use File::Copy;
use Digest::SHA1 qw(sha1_hex);

#Protocol version:
my $ver=1;

my $debug=0;

my $paranoid=1;

my $serialport="/dev/ttyUSB0";
#my $serialport="/dev/ttyS0";

my $CPSUrl="http://www.cacert.org/cps.php";

my $OCSPUrl="http://ocsp.cacert.org/";

my $gpgbin="/usr/bin/gpg";

my $opensslbin="/usr/bin/openssl";

my $work="./work";

#my $gpgID='gpgtest@cacert.at';
my $gpgID='gpg@cacert.org';


my %PkiSystems=(
"1"=>"X.509",
"2"=>"OpenPGP");
my %rootkeys=(
"1"=>5, #X.509
"2"=>1);#OpenPGP
my %hashes=(
"0"=>"",
"1"=>"-md md5",
"2"=>"-md sha1",
"3"=>"-md rmd160",
"8"=>"-md sha256",
"9"=>"-md sha384",
"10"=>"-md sha512");
my %templates=(
       "0"=>"client.cnf",
       "1"=>"client-org.cnf",
       "2"=>"client-codesign.cnf",
       "3"=>"client-machine.cnf",
       "4"=>"client-ads.cnf",
       "5"=>"server.cnf",
       "6"=>"server-org.cnf",
       "7"=>"server-jabber.cnf",
       "8"=>"ocsp.cnf",
       "9"=>"timestamp.cnf",
       "10"=>"proxy.cnf",
       "11"=>"subca.cnf"
);

my $starttime=5*60;  # 5 minutes

my %currenthash=();


#End of configurations

########################################################

mkdir "$work",0700;
mkdir "currentcrls";

$ENV{'PATH'}='/usr/bin/:/bin';
$ENV{'IFS'}="\n";
$ENV{'LD_PRELOAD'}='';
$ENV{'LD_LIBRARY_PATH'}='';
$ENV{'LANG'}='';

#Logging functions:
sub SysLog($)
{
  my $date=strftime("%Y-%m-%d",localtime);
  open LOG,">>logfile$date.txt";
  return if(not defined($_[0]));
  my $timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime);
  #$syslog->write($_[0]."\x00");
  print LOG "$timestamp $_[0]";
#  print "$timestamp $_[0]";
  flush LOG;
  close LOG;
}

sub Error($)
{
  SysLog($_[0]);
  if($paranoid)
  {
    die $_[0];
  }
}

sub readfile($)
{
  my $olds=$/;
  open READIN,"<$_[0]";
  undef $/;
  my $content=<READIN>;
  close READIN;
  $/=$olds;
  return $content;
}


#Hexdump function: Returns the hexdump representation of a string
sub hexdump($)
{
  return "" if(not defined($_[0]));
  my $content="";
  $content.=sprintf("%02X ",unpack("C",substr($_[0],$_,1))) foreach (0 .. length($_[0])-1);
  return $content;
}

#pack3 packs together the length of the data in 3 bytes and the data itself, size limited to 16MB. In case the data is more than 16 MB, it is ignored, and a 0 Byte block is transferred
sub pack3
{
  return "\x00\x00\x00" if(!defined($_[0]));
  my $data=(length($_[0]) >= 2**24)? "":$_[0];
  my $len=pack("N",length($data));
  #print "len: ".length($data)."\n";
  return substr($len,1,3).$data;
}


#unpack3 unpacks packed data.
sub unpack3($)
{
  return undef if((not defined($_[0])) or length($_[0])<3);
  #print "hexdump: ".hexdump("\x00".substr($_[0],0,3))."\n";
  my $len=unpack("N","\x00".substr($_[0],0,3));
  #print "len3: $len length(): ".length($_[0])." length()-3: ".(length($_[0])-3)."\n";
  return undef if(length($_[0])-3 != $len);
  return substr($_[0],3);
}


#unpack3array extracts a whole array of concatented packed data.
sub unpack3array($)
{
  my @retarr=();
  if((not defined($_[0])) or length($_[0])<3)
  {
    SysLog "Datenanfang kaputt\n";
    return ();
  }
  my $dataleft=$_[0];
  while(length($dataleft)>=3)
  {
    #print "hexdump: ".hexdump("\x00".substr($dataleft,0,3))."\n";
    my $len=unpack("N","\x00".substr($dataleft,0,3));
    #print "len3: $len length(): ".length($dataleft)." length()-3: ".(length($dataleft)-3)."\n";
    if(length($dataleft)-3 < $len)
    {
      SysLog "Datensatz abgeschnitten\n";
      return ();
    }
    push @retarr, substr($dataleft,3,$len);
    $dataleft=substr($dataleft,3+$len);
  }
  if(length($dataleft)!=0)
  {
    SysLog "Ende abgeschnitten\n";
    return ();
  }
  return @retarr;
}




my $timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime);

SysLog("Starting Server at $timestamp\n");

SysLog("Opening Serial interface:\n");
#if(1)
#{

sub SerialSettings
{
  my $PortObj=$_[0];
  Error "Could not open Serial Port!\n" if(!defined($PortObj));
  $PortObj->baudrate(115200);
  $PortObj->parity("none");
  $PortObj->databits(8);
  $PortObj->stopbits(1);        
}

#We have to open the SerialPort and close it again, so that we can bind it to a Handle
my $PortObj = new Device::SerialPort($serialport);
SerialSettings($PortObj);
$PortObj->save("serialserver.conf");
#}
undef $PortObj;

$PortObj = tie (*SER, 'Device::SerialPort', "serialserver.conf") || Error "Can't tie using Configuration_File_Name: $!\n";

Error "Could not open Serial Interface!\n" if(not defined($PortObj));
SerialSettings($PortObj);
#open SER,">$serialport";

SysLog("Serial interface opened: $PortObj\n");


#Creating select() selector for improved reading:
my $sel = new IO::Select( \*SER );

#Raw send function over the Serial Interface  (+debugging)
sub SendIt($)
{
  return unless defined($_[0]);
  SysLog "Sending ".length($_[0])."\n"; #hexdump($_[0])."\n";
  my $data=$_[0];
  my $runcount=0;
  my $total=0;
  my $mtu=30;
  while(length($data))
  {
    my $iwrote=scalar($PortObj->write(substr($data,0,$mtu)))||0;
    usleep(270*$iwrote+9000); # On Linux, we have to wait to make sure it is being sent, and we dont loose any data.
    $total+=$iwrote;
    $data=substr($data,$iwrote);
    print "i wrote: $iwrote total: $total left: ".length($data)."\n" if(!($runcount++ %10));
  }

#  print "Sending ".length($_[0])."\n"; #hexdump($_[0])."\n";
#  foreach(0 .. length($_[0]))
#  {
#    $PortObj->write(substr($_[0],$_,1));
#  }

}


#Send data over the Serial Interface with handshaking:
#Warning: This function is implemented paranoid. It exits the program in case something goes wrong.
sub SendHandshakedParanoid($)
{
  #print "Shaking hands ...\n";
  SendIt("\x02");

  Error "Handshake uncompleted. Connection lost!" if(!scalar($sel->can_read(2)));
  my $data="";
  usleep(1000000);
  my $length=read SER,$data,1;
  if($length && $data eq "\x10")
  {
    print "OK ...\n";
    my $xor=0;
    foreach(0 .. length($_[0])-1)
    {
      #print "xor mit ".unpack("C",substr($_[0],$_,1))."\n";
      $xor ^= unpack("C",substr($_[0],$_,1));
    }
    #print "XOR: $xor\n";

    my $tryagain=1;
    while($tryagain)
    {
      SendIt($_[0].pack("C",$xor)."rie4Ech7");

      Error "Packet receipt was not confirmed in 5 seconds. Connection lost!" if(!scalar($sel->can_read(5)));

      $data="";
      $length=read SER,$data,1;
      
      if($length && $data eq "\x10")
      {
        SysLog "Sent successfully!...\n";
        $tryagain=0;
      }
      elsif($length && $data eq "\x11")
      {
        $tryagain=1;
      }
      else
      {
        Error "I cannot send! $length ".unpack("C",$data)."\n"; 
      }
    }

  }
  else
  {
    print "!Cannot send! $length $data\n"; 
    Error "!Stopped sending.\n";
  }
}

sub Receive
{
  my $data="";
  my @ready = $sel->can_read(20);

  my $length=read SER,$data,1,0;

  #SysLog "Data: ".hexdump($data)."\n";

  if($data eq "\x02")
  {
    my $modus=1;
    SysLog "Start received, sending OK\n";
    SendIt("\x10");

    my $block="";
    my $blockfinished=0;
    my $tries=10000;

    while(!$blockfinished)
    {
      Error("Tried reading too often\n") if(($tries--)<=0);

      $data="";
      if(!scalar($sel->can_read(2)))
      {
        SysLog("Timeout!\n");
        return;
      }
      $length=read SER,$data,100,0;
      if($length)
      {
        $block.=$data;
      }
      #SysLog("Received: $length ".length($block)."\n");
      $blockfinished=defined(unpack3(substr($block,0,-9)))?1:0;

      if(!$blockfinished and substr($block,-8,8) eq "rie4Ech7")
      {
        SysLog "BROKEN Block detected!";
        SendIt("\x11");
        $block="";
        $blockfinished=0;
        $tries=10000;
      }

    }
    SysLog "Block done: \n";#.hexdump($block)."\n";
    SendIt("\x10");
    SysLog "Returning block\n";
    return($block);
  }
  else
  {
    Error("Error: No Answer received, Timeout.\n") if(length($data)==0);
    Error("Error: Wrong Startbyte: ".hexdump($data)." !\n");
  }

  SysLog "Waiting on next request ...\n";

}


#Checks the CRC of a received block for validity
#Returns 1 upon successful check and 0 for a failure
sub CheckCRC($)
{
  my $block=$_[0];
  return 0 if(length($_[0])<1);
  return 1 if($_[0] eq "\x00");
  my $xor=0;
  foreach(0 .. length($block)-2)
  {
    #print "xor mit ".unpack("C",substr($block,$_,1))."\n";
    $xor ^= unpack("C",substr($block,$_,1));
  }
  #print "XOR: $xor BCC: ".unpack("C",substr($block,-1,1))."\n";
  if($xor eq unpack("C",substr($block,-1,1)))
  {
    #print "Checksum correct\n";
    return 1;
  }
  else
  {
    #print "Checksum on received packet wrong!\n";
    return 0;
  }

}

#Formatting and sending a Response packet
sub Response($$$$$$$)
{
  SendHandshakedParanoid(pack3(pack3(pack("C*",$_[0],$_[1],$_[2],$_[3])).pack3($_[4]).pack3($_[5]).pack3($_[6])));
}


#Checks the parameters, whether the certificate system (OpenPGP, X.509, ...) is available,
#whether the specified root key is available, whether the config file is available, ...
#Returns 1 upon success, and dies upon error!
sub CheckSystem($$$$)
{
  my ($system,$root,$template,$hash)=@_;
  if(not defined($templates{$template}))
  {
    Error "Template unknown!\n";
  }
  if(not defined($hashes{$hash}))
  {
    Error "Hash algorithm unknown!\n";
  }
  if(defined($rootkeys{$system}))
  {
    if($root<$rootkeys{$system})
    {
      return 1;
    }
    else
    {
      Error "Identity System $system has only $rootkeys{$system} root keys, key $root does not exist.\n";
    }
  }
  else
  {
    Error "Identity System $system not supported";
  }
  
  return 0;
}


#Selects the specified config file for OpenSSL and makes sure that the specified config file exists
#Returns the full path to the config file
sub X509ConfigFile($$)
{
  my ($root,$template)=@_;
  my $opensslcnf="";
  if($root==0)
  {
    $opensslcnf="/etc/ssl/openssl-$templates{$template}";
  }
  elsif($root==1)
  {
    $opensslcnf="/etc/ssl/class3-$templates{$template}";
  }
  elsif($root==2)
  {
    $opensslcnf="/etc/ssl/class3s-$templates{$template}";
  }
  else
  {
    $opensslcnf="/etc/ssl/root$root/$templates{$template}";
  }
  # Check that the config file exists
  Error "Config file does not exist: $opensslcnf!" unless (-f $opensslcnf);

  return $opensslcnf;
}

sub CreateWorkspace()
{
  mkdir "$work",0700;
  my $id = (new File::CounterFile "./$work/.counter", "0")->inc;
  mkdir "$work/".int($id/1000),0700;
  mkdir "$work/".int($id/1000)."/".($id%1000),0700;
  my $wid="$work/".int($id/1000)."/".($id%1000);
  SysLog "Creating Working directory: $wid\n";
  return $wid;
}


sub SignX509($$$$$$$$)
{
  my ($root,$template,$hash,$days,$spkac,$request,$san,$subject)=@_;

  my $wid=CreateWorkspace();

  my $opensslcnf=X509ConfigFile($root,$template);

  print "Subject: $subject\n";
  print "SAN: $san\n";


  $subject=~ s/\\x([A-F0-9]{2})/pack("C", hex($1))/egi;
  $san=~ s/\\x([A-F0-9]{2})/pack("C", hex($1))/egi;

  Error "Invalid characters in SubjectAltName!\n" if($san=~m/[ \n\r\t\x00"'\\]/);
  Error "Invalid characters in Subject: ".hexdump($subject)." - $subject\n" if($subject=~m/[\n\r\t\x00"'\\]/);

  print "Subject: $subject\n";
  print "SAN: $san\n";

  my $extfile="";
  if($templates{$template}=~m/server/)  #??? Should we really do that for all and only for server certs?
  {
    open OUT,">$wid/extfile";
    print OUT "basicConstraints = critical, CA:FALSE\n";
    print OUT "keyUsage = critical, digitalSignature, keyEncipherment, keyAgreement\n";
    print OUT "extendedKeyUsage = clientAuth, serverAuth, nsSGC, msSGC\n";
    print OUT "authorityInfoAccess = OCSP;URI:$OCSPUrl\n";
    
    my $CRLUrl="";
    if($root==0)
    {
        $CRLUrl="http://crl.cacert.org/revoke.crl";
    }
    elsif($root==1)
    {
        $CRLUrl="http://crl.cacert.org/class3-revoke.crl";
    }
    elsif($root==2)
    {
        $CRLUrl="http://crl.cacert.org/class3s-revoke.crl";
    }
    else
    {
        $CRLUrl="http://crl.cacert.org/root${root}.crl";
    }
    print OUT "crlDistributionPoints = URI:${CRLUrl}\n";
    print OUT "subjectAltName = $san\n" if(length($san));
    close OUT;
    $extfile=" -extfile $wid/extfile ";
  }

  my $cmd=($request=~m/SPKAC\s*=/)?"-spkac":"-subj '$subject' -in";

  #my $cmd=$spkac?"-spkac":"-subj '$subject' -in";


  if(open OUT,">$wid/request.csr")
  {
    print OUT $request;
    close OUT;

    my $do = `$opensslbin ca $hashes{$hash} -config $opensslcnf $cmd $wid/request.csr -out $wid/output.crt -days $days -key test -batch $extfile 2>&1`;

    SysLog $do;


    if(open IN,"<$wid/output.crt")
    {
      undef $/;
      my $content=<IN>;
      close IN;
      $/="\n";

      $content=~s/^.*-----BEGIN/-----BEGIN/s;
      SysLog "Antworte...\n";
      Response($ver,1,0,0,$content,"","");
      SysLog "Done.\n";
      if(!$debug)
      {
        unlink "$wid/output.crt";
        unlink "$wid/request.csr";
        unlink "$wid/extfile";
      }
    }
    else
    {
      Error("Could not read the resulting certificate.\n");
    }
  }
  else
  {
    Error("Could not save request.\n");
  }
  unlink "$wid";
}

sub SignOpenPGP
{
  my ($root,$template,$hash,$days,$spkac,$request,$san,$subject)=@_;

  my $wid=CreateWorkspace();

  if(! -f "secring$root.gpg")
  {
    Error "Root Key not found: secring$root.gpg !\n";
  }

  copy("secring$root.gpg","$wid/secring.gpg");
  copy("pubring$root.gpg","$wid/pubring.gpg");

  my $keyid=undef;

  Error "Invalid characters in SubjectAltName!\n" if($san=~m/[ \n\r\t\x00"'\\]/);
  Error "Invalid characters in Subject!\n" if($subject=~m/[ \n\r\t\x00"'\\;]/);


  if(open OUT,">$wid/request.key")
  {
    print OUT $request;
    close OUT;


#!!!!   ?!?
    #my $homedir=-w "/root/.gnupg" ? "/root/.gnupg":"$wid/";
    my $homedir="$wid/";

    {
      SysLog "Running GnuPG in $homedir...\n";
      my ($stdin,$stdout,$stderr) = (IO::Handle->new(),IO::Handle->new(),IO::Handle->new());


      SysLog "Importiere $gpgbin --no-tty --homedir $homedir --import $wid/request.key\n";

      my $pid = open3($stdin,$stdout,$stderr, "$gpgbin --no-tty --homedir $homedir --command-fd 0 --status-fd 1 --logger-fd 2 --with-colons --import $wid/request.key");

      if (!$pid) {
        Error "Cannot fork GnuPG.";
      }
      $/="\n";
      while(<$stdout>)
      {
        SysLog "Received from GnuPG: $_\n";
        if(m/^\[GNUPG:\] GOT_IT/)
        {
        }
        elsif(m/^\[GNUPG:\] GET_BOOL keyedit\.setpref\.okay/)
        {
          print $stdin "no\n";
        }
        elsif(m/^\[GNUPG:\] ALREADY_SIGNED/)
        {
        }
        elsif(m/^\[GNUPG:\] GOOD_PASSPHRASE/)
        {
        }
        elsif(m/^\[GNUPG:\] KEYEXPIRED/)
        {
        }
        elsif(m/^\[GNUPG:\] SIGEXPIRED/)
        {
        } 
        elsif(m/^\[GNUPG:\] IMPORT_OK/)
        {
        } 
        elsif(m/^\[GNUPG:\] IMPORT_RES/)
        {
        } 
        elsif(m/^\[GNUPG:\] IMPORTED ([0-9A-F]{16})/)
        {
          Error "More than one OpenPGP sent at once!" if(defined($keyid));
          $keyid=$1;
        } 
        elsif(m/^\[GNUPG:\] NODATA/)
        {
          # To crash or not to crash, thats the question.
        } 
        else
        {
          Error "ERROR: UNKNOWN $_\n";
        }

      }

      while(<$stderr>)
      {

        SysLog "Received from GnuPG on stderr: $_\n";

        if(m/^key ([0-9A-F]{8}): public key/)
        {
          #$keyid=$1;
        }
      }

      waitpid($pid,0);

    }

    Error "No KeyID found!" if(!defined($keyid));


    SysLog "Running GnuPG to Sign...\n";

    {
    my ($stdin,$stdout,$stderr) = (IO::Handle->new(),IO::Handle->new(),IO::Handle->new());



    $ENV{'LANG'}="";

    my $line="$gpgbin --no-tty --default-key $gpgID --homedir $homedir --default-cert-expire $days"."d --ask-cert-expire --cert-policy-url $CPSUrl --command-fd 0 --status-fd 1 --logger-fd 2 --sign-key $keyid ";
    SysLog($line."\n");

    my $pid = open3($stdin,$stdout,$stderr,$line);

    if (!$pid) {
      Error "Cannot fork GnuPG.";
    }
    SysLog "Got PID $pid\n";
    while(<$stdout>)
    {
      SysLog "Received from GnuPG: $_\n";
      if(m/^\[GNUPG:\] GET_BOOL keyedit\.sign_all\.okay/)
      {
        print $stdin "yes\n";
      }
      elsif(m/^\[GNUPG:\] GOT_IT/)
      {
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.okay/)
      {
        print $stdin "yes\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.expire_okay/)
      {
        print $stdin "yes\n";
      }
      elsif(m/^\[GNUPG:\] GET_LINE siggen\.valid\s?$/)
      {
        print $stdin "$days\n";
      }
      elsif(m/^\[GNUPG:\] GET_LINE sign_uid\.expire\s?$/)
      {
        print "DETECTED: Do you want your signature to expire at the same time? (Y/n) -> yes\n";
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.replace_expired_okay/)
      {
        print $stdin "yes\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.dupe_okay/)
      {
        print $stdin "yes\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL keyedit\.sign_revoked\.okay/)
      {
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.revoke_okay/)
      {
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.expired_okay/)
      {
        print "The key has already expired!!!\n";
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.nosig_okay/)
      {
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL sign_uid\.v4_on_v3_okay/)
      {
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] GET_BOOL keyedit\.setpref\.okay/)
      {
        print $stdin "no\n";
      }
      elsif(m/^\[GNUPG:\] ALREADY_SIGNED/)
      {
      }
      elsif(m/^\[GNUPG:\] GOOD_PASSPHRASE/)
      {
      }
      elsif(m/^\[GNUPG:\] KEYEXPIRED/)
      {
      }
      elsif(m/^\[GNUPG:\] SIGEXPIRED/)
      {
      } 
      elsif(m/^\[GNUPG:\] NODATA/)
      {
        # To crash or not to crash, thats the question.
      } 
      else
      {
        Error "ERROR: UNKNOWN $_\n";
      }
    }

      while(<$stderr>)
      {

        SysLog "Received from GnuPG on stderr: $_\n";

        if(m/^key ([0-9A-F]{8}): public key/)
        {
          #$keyid=$1;
        }
      }



    waitpid($pid,0);

    }

#$do = `( $extras echo "365"; echo "y"; echo "2"; echo "y")|$gpgbin --no-tty --default-key gpg@cacert.org --homedir $homedir --batch --command-fd 0 --status-fd 1 --cert-policy-url http://www.cacert.org/index.php?id=10 --ask-cert-expire --sign-key $row[email] 2>&1`;

    SysLog "Running GPG to export...\n";

    my $do = `$gpgbin --no-tty --homedir $homedir --export --armor $keyid > $wid/result.key`;
    SysLog $do;
    $do = `$gpgbin --no-tty --homedir $homedir --batch --yes --delete-key $keyid 2>&1`;
    SysLog $do;

    if(open IN,"<$wid/result.key")
    {
      undef $/;
      my $content=<IN>;
      close IN;
      $/="\n";

      $content=~s/^.*-----BEGIN/-----BEGIN/s;
      SysLog "Antworte...\n";
      Response($ver,2,0,0,$content,"","");
      SysLog "Done.\n";

      if(!$debug)
      {
        unlink "$wid/request.key";
        unlink "$wid/result.key";
      }

    } 
    else
    {
      SysLog "NO Resulting Key found!";
    }
  }
  else
  {
    Error "Kann Request nicht speichern!\n";
  }

  unlink("$wid/secring.gpg");
  unlink("$wid/pubring.gpg");
  unlink("$wid");
}

sub RevokeX509
{
  my ($root,$template,$hash,$days,$spkac,$request,$san,$subject)=@_;

  Error "Invalid characters in SubjectAltName!\n" if($san=~m/[ \n\r\t\x00"'\\]/);
  Error "Invalid characters in Hash!\n" if(! $subject=~m/^[0-9a-fA-F]+$/);

  SysLog "Widerrufe $PkiSystems{$_[0]}\n";
  SysLog "Aktueller Hash vom Webserver: $subject\n";

  my $iscurrent=0;

  $currenthash{$root}=sha1_hex(readfile("revoke-root$root.crl"));

  print "Aktueller Hash vom Signingserver: $currenthash{$root}\n";

  if($subject eq $currenthash{$root})
  {
    print "Hash matches current CRL.\n";
    print "Deleting old CRLs...\n";
    foreach (<currentcrls/$root/*>)
    {
      if($_ ne "currentcrls/$root/$subject.crl")
      {
        print "Deleting $_\n";
        unlink $_ ;
      }
    }
    print "Done with deleting old CRLs.\n";
    $iscurrent=1;
  }

  my $wid=CreateWorkspace();

  my $opensslcnf=X509ConfigFile($root,$template);

  if(open OUT,">$wid/request.crt")
  {
    print OUT $request;
    close OUT;

    my $do = `$opensslbin ca $hashes{$hash} -config $opensslcnf -key test -batch -revoke $wid/request.crt > /dev/null 2>&1`;
    $do = `$opensslbin ca $hashes{$hash} -config $opensslcnf -key test -batch -gencrl -crldays 7 -crlexts crl_ext -out $wid/cacert-revoke.crl > /dev/null 2>&1`;
    $do = `$opensslbin crl -inform PEM -in $wid/cacert-revoke.crl -outform DER -out $wid/revoke.crl > /dev/null 2>&1`;
    unlink "$wid/cacert-revoke.crl";

    if(open IN,"<$wid/revoke.crl")
    {
      undef $/;
      my $content=<IN>;
      close IN;
      $/="\n";
      unlink "$wid/revoke.crl";

      mkdir "currentcrls/$root";
      my $newcrlname="currentcrls/$root/".sha1_hex($content).".crl";
      open OUT,">$newcrlname";
      print OUT $content;
      close OUT;

      if($iscurrent)
      {
        SysLog "Schicke aktuelles Delta...\n";
        system "xdelta delta revoke-root$root.crl $newcrlname delta$root.diff";
        Response($ver,2,0,0,readfile("delta$root.diff"),"","");
        #Response($ver,2,0,0,$content,"","");
      }
      else
      {
        if(-f "currentcrls/$root/$subject.crl")
        {
          SysLog "Schicke altes Delta...\n";
          system "xdelta delta currentcrls/$root/$subject.crl $newcrlname delta$root.diff";

          Response($ver,2,0,0,readfile("delta$root.diff"),"","");
          #Response($ver,2,0,0,$content,"","");
        }
        else
        {
          SysLog "Out of Sync! Sending empty CRL...\n";
          Response($ver,2,0,0,"","","");  # CRL !!!!!!!!!
        }
      }
 
      open OUT,">revoke-root$root.crl";
      print OUT $content;
      close OUT;
    

      SysLog "Done.\n";
    }
  }
  unlink "$wid";
}


sub analyze($)
{
  SysLog "Analysiere ...\n";
  #SysLog hexdump($_[0])."\n";

  my @fields=unpack3array(substr($_[0],3,-9));
  Error "Wrong number of parameters: ".scalar(@fields)."\n" if(scalar(@fields)!=4);

  SysLog "Header: ".hexdump($fields[0])."\n";
  my @bytes=unpack("C*",$fields[0]);

  Error "Header too short!\n" if(length($fields[0])<3);

  Error "Version mismatch. Server does not support version $bytes[0], server only supports version $ver!\n" if($bytes[0]!=$ver);

  Error "Header has wrong length: ".length($fields[0])."!\n" if(length($fields[0])!=9);

  if($bytes[1] == 0) # NUL Request
  {
    SysLog "NUL Request detected.\n";
    if($fields[1] =~ /^\d+\.\d+$/)
    {
      open OUT,">timesync.sh";
      print OUT "date -u '$fields[1]'\n";
      print OUT "hwclock --systohc\n";
      close OUT;
    }
    Response($ver,0,0,0,"","","");
  }
  elsif($bytes[1]==1) # Sign Request
  {
     SysLog "SignRequest detected...\n";
     CheckSystem($bytes[2],$bytes[3],$bytes[4],$bytes[5]);
     if($bytes[2]==1)
     {
       SignX509($bytes[3],$bytes[4],$bytes[5],($bytes[6]<<8)+$bytes[7], $bytes[8],$fields[1],$fields[2],$fields[3]);
     }
     elsif($bytes[2]==2)
     {
       SignOpenPGP($bytes[3],$bytes[4],$bytes[5],($bytes[6]<<8)+$bytes[7], $bytes[8],$fields[1],$fields[2],$fields[3]);
     }
  }
  elsif($bytes[1]==2) # Revocation Request
  {
     SysLog "Revocation Request ...\n";
     CheckSystem($bytes[2],$bytes[3],$bytes[4],$bytes[5]);
     if($bytes[2]==1)
     {
       RevokeX509($bytes[3],$bytes[4],$bytes[5],($bytes[6]<<8)+$bytes[7], $bytes[8],$fields[1],$fields[2],$fields[3]);
     }
  }
  else
  {
    Error "Unknown command\n";
  }

}

SysLog "Server started. Waiting 5 minutes for contact from client ...\n";

#When started, we wait for 5 minutes for the client to connect:
my @ready=$sel->can_read($starttime);


my $count=0;

#As soon as the client connected successfully, the client has to send a request faster than every 10 seconds
while(@ready = $sel->can_read(15) && -f "./server.pl-active")
{
  my $data="";
  #my $length=read SER,$data,1;

  #SysLog "Data: ".hexdump($data)."\n";

  #Receive();

  $data=Receive();
  SysLog "Analysing ...\n";
  analyze($data);

#   if($data eq "\x02")
#   {
#     #SysLog "Start empfangen, sende OK\n";
#     SendIt("\x10");
# 
#     my $block="";
#     my $blockfinished=0;
#     my $tries=10000;
# 
#     while(!$blockfinished)
#     {
#       Error "Tried reading too often\n" if(($tries--)<=0);
# 
#       $data="";
#       @ready = $sel->can_read(2);
#       $length=read SER,$data,100;
#       if($length)
#       {
#         $block.=$data;
#       }
#       $blockfinished=defined(unpack3(substr($block,0,-1)))?1:0;
#     }
#     #SysLog "Block done: ".hexdump($block)."\n";
#     if(CheckCRC($block))
#     {
#       SendIt("\x10");
#       analyze($block);
#     }
#     else
#     {
#       Error "CRC Error\n";
#     }
#   }
#   else
#   {
#     Error "Error: Wrong Startbyte!\n";
#   }

  $count++;

  SysLog "$count requests processed. Waiting on next request ...\n";

}


Error "Timeout! No data from client anymore!\n";

