# processdata.pl 
#
# Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999 by RIPE NCC
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of the author not be
# used in advertising or publicity pertaining to distribution of the
# software without specific, written prior permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
# AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# $Id: processdata.pl,v 2.18 1999/10/06 16:41:17 marek Exp $
#
#	$RCSfile: processdata.pl,v $
#	$Revision: 2.18 $
#	$Author: marek $
#	$Date: 1999/10/06 16:41:17 $


@INC = ("LIBDIR", @INC);

require "flush.pl";

require "defines.pl";
require "adderror.pl";
require "dbadd.pl";
require "enparse.pl";
require "entype.pl";
require "enwrite.pl";
require "misc.pl";
require "rconf.pl";
require "rfc822.pl";
require "syslog.pl";
require "dpr.pl";


#
# printstat 
#
# prints verbose version of the update result.


sub printstat {
    local($output, *entry, $type, $options) = @_;

    if ($options & $DELETEOPTION) {
       print $output "Delete ";
    }
    elsif ($options & $NEWOPTION) {
       print $output "New ";
    }
    else {
       print $output "Update ";
    }
    
    if ($options & $OKOPTION) { 
       print $output "OK: ";
    }
    elsif ($options & $NOOPOPTION) { 
       print $output "NOOP: ";
    }
    else { 
       print $output "FAILED: ";
    }
    
    if ($entry{$type}) {
       
       print $output "[", $ATTL{$type}, "] ";
       
       if (($entry{"nh"}) && ($entry{"nh"}!~ /^$AUTONICPREFIXREGULAR/)) {
          print $output $entry{"nh"}, " (", $entry{$type},")\n";
       }
       else {
          print $output $entry{$type}, "\n";
       }
       
    }
    else {
       print $output "[unknown object]\n"; 
    }
 
}


#
# processdata($input, $output);
#
# does all the work.

sub processdata {
    local($input, $output)=@_;

    local($somethingfound)=0;
    local($haserror)=0;
    local($eof)=0;
    local(@queue)=();
    
    local($type,$key,$value,$reordered,$dbstat,$parsestat);
    local($options,$codes,$overrideattr,$deleteattr,@autonichandles);

    &dpr("\$FROM = $FROM, \$input = $input, \$output = $output\n");
    
    while(1) {

	($parsestat, $type, %entry)=&enparse($input) if (!$eof);
	
#	&dpr("enparse returned: \$parsestat = $parsestat, \$type = $type\n");
#	&dpr("+++++++++++++++++++++++++++\n");
#	&dpr("\%entry = " . join("*", %entry) . "\n");
#	&dpr("---------------------------\n");
	
	$eof=1 if ($parsestat==$EOF);
	
	# get objects from the queue if we reached EOF
	
	if ($eof) {
	   
	   if (@queue) {
	      
	      $parsestat=shift(@queue);
	      $type=shift(@queue);
	      @autonichandles=split(" ", shift(@queue));
	      	      
	      %entry=();
	      
	      while ($key=shift(@queue)) {
	         
	         $entry{$key}=shift(@queue);
	          
	      }
	      
	   }
	   else {
	      
	      last;
	      
	   }
	   
	}
	else {
	   @autonichandles=();
	}

        #
        # we found an object
        
        $somethingfound=1;
        
#	&dpr("read an object ($parsestat,$entry{$type})\n");

        if ($parsestat==$O_ERROR) {
           
           $dbstat=$O_ERROR;
           
           $options=0;
	
	   if (defined($entry{"ud"})) {
	
	      $options|=$DELETEOPTION;
	      $deleteattr=delete($entry{"ud"});
	                    
	   }
	   else {
	      $deleteattr=undef;
	   }
	
	   if (defined($entry{"uo"})) {
	       &dpr("override entry: ".$entry{"uo"}."\n");
	       $options |= $OVERRIDEOPTION;
	       $options |= $OVERRIDEINTEGRITY 
		   if ($entry{"uo"} =~ /integrity/i);
	       $overrideattr = delete($entry{"uo"});
	   }
	   else {
	      $overrideattr = undef;
	   }
           
        }
        else {
        
           #
           # very primitive check if we need to reorder the update 
           #
           # no time to make this intelligent right now
           # and it it probably not really needed for 99.9 % of
           # all updates
           
           if (!$eof) {
              
              $reordered=0;
              
              foreach (split(/ /, $OBJPOINTSTOATTR{$type})) {
                 
                 # print STDERR "$_\n";
                 
                 local($*)=1;
                 
                 if ($entry{$_}=~ /(^|\n)$AUTONICPREFIXREGULAR\d+[A-Z]*(\n|$)/o) {
                    
                    # print STDERR "pointsto: $_\n";
                       
                    push(@autonichandles, $_);
                       
                 }
              
              }
              
              # print STDERR "autofirst: ", join(" ", @autonichandles), "\n";
              
              if ($entry{"nh"}!~ /^$AUTONICPREFIXREGULAR\d+[A-Z]*$/o) {
                     
                 foreach (keys %entry) {
           
                    #
                    # it is very unlikely that a (new) maintainer
                    # will follow any object where it's immediately used
                 
                    next if (($type ne "mt") && (/^m[bl]$/));
                 
                    #
                    # reorder if an object points to another object
                 
                    if ((defined($POINTSTO{$_})) && ($POINTSTO{$_}!~ /^$_$/)) {
                    
                       $reordered++;
                    
                    } 
                 
                    local($*)=1;
                    
                    if ($entry{$_}=~ /(^|\n)$AUTONICPREFIXREGULAR\d+[A-Z]*(\n|$)/o) {
                 
                       $reordered++;
                       
                       # print STDERR "pointsto: $_\n";
                       
                    }
              
                 }
              
              }
              
              if ($reordered) {
                 
                 if (@autonichandles) {
                 
                    push(@queue, "") if (scalar(@queue));
                    push(@queue, $parsestat, $type, join(" ", @autonichandles));
	            while (($key,$value)=each(%entry)) {
	         
	               push(@queue, $key, $value);
	          
	            }
	            
                    # print STDERR "***nicqueue***:\n", join("\n", @queue), "-\n";
                 
                 }
                 else {
                    
                    unshift(@queue, "") if (scalar(@queue));
	            while (($key,$value)=each(%entry)) {
	         
	               unshift(@queue, $key, $value);
	          
	            }
	            unshift(@queue, $parsestat, $type, "");
                    
	            # print STDERR "***nonnicqueue***:\n", join("\n", @queue), "-\n";
                 
                 }
                 
                 next;
              
              }
           
           }
           
           #print STDERR "auto: ", join(" ", @autonichandles),"\n";
           #print STDERR "perform update: ", $entry{$type}, "\n"; next;
           
           #
           # perform the actual update
           
           $options=0;
	
	   if (defined($entry{"ud"})) {
	
	      $options|=$DELETEOPTION;
	      $deleteattr=delete($entry{"ud"});
	                    
	   }
	
	   if (defined($entry{"uo"})) {
	       &dpr("override entry: ".$entry{"uo"}."\n");
	       $options |= $OVERRIDEOPTION;
	       $options |= $OVERRIDEINTEGRITY 
		   if ($entry{"uo"} =~ /integrity/i);
	       $overrideattr = delete($entry{"uo"});
	   }
        
           #
	   # object parsed OK or has only warnings, now we will
	   # do the final checks when the databases are locked and opened
	
	   ($dbstat, $codes) =&finalchecksandupdate(*entry, $type, *autonichandles, *options);
	   
#	   &dpr("left ($FROM,$dbstat, $codes)\n");


	}
	
	# print STDERR %entry, "\n";
	
	if ($dbstat==$O_COULDNOTOPEN) {
	   
	   #
	   # Not too good, probably permission problem
	   # if this is given as output ...

	   &adderror(*entry, "Failed to open DB file ($ATTL{$codes}): $!");
	   &adderror(*entry, "Contact <$HUMAILBOX> for a problem report");

           $haserror++;

	}
	elsif (($dbstat!=$O_ERROR) && ($dbstat!=$O_OK) && ($dbstat!=$E_NOOP)) { 
	      
	    &adderror(*entry, eval $MESSAGE[$dbstat]);
	     
	    $haserror++;
	      
	}
	elsif ($dbstat!=$E_NOOP) {
	   
	   if ($options & $OVERRIDEOPTION) {
	      
	      &syslog("AUDITLOG", "override [$type] $overrideattr -> \"".$entry{$type}."\"");
	      
	   }
	   elsif ($options & $DELETEOPTION) {
	         
	      &syslog("AUDITLOG", "delete [$type] $deleteattr -> \"".$entry{$type}."\"");
	         
	   }
	   
	}
		
	# $opt_v set if long ack?

	if ($dbstat==$E_NOOP) {
#	  if ($opt_v) {
	    &printstat($output, *entry, $type, $options | $NOOPOPTION);
	    print $output "\n";
#	  }
	}
	elsif (&haserror(*entry)) {
	
#	   &dpr("object has error\n");
	   
	   $haserror++;
	   
	   &printstat($output, *entry, $type, $options | $FAILEDOPTION);
	   &enwrite($output,*entry,1,1,1);
	   print $output "\n";
	      
	}
	elsif (&haswarning(*entry)) {
	   
	   &printstat($output, *entry, $type, $options | $OKOPTION);
 	   &enwrite($output,*entry,1,1,1);
	   print $output "\n";
	   
	}
#	elsif (($opt_v) || ($options & $ASSIGNEDNICOPTION)) {
	      else {
	   &printstat($output, *entry, $type, $options | $OKOPTION);
	   print $output "\n";
	   
	}
	
	$entry{"uo"}=$overrideattr if ($overrideattr);
	$entry{"ud"}=$deleteattr if ($deleteattr);

    }

    return ($somethingfound, $haserror);
    
}

sub dbupdate {
    local($input, $output)=@_;
    
    # Set a global variable to mark this as an update

    $Main::is_update=1;

    # We want to make a local copy of the input, because we need to do
    # multiple things with it... also we want to separate PGP signed
    # parts from the original message to be fed to PGPV for checking.

    my($copyfile_unsigned) = $TMPDIR . "/dbupdcopy.$$";
    my(@copyfiles_signed);

    open(COPY, "+>$copyfile_unsigned")
	or fatalerror("Cannot create $copyfile_unsigned: $!");

    my(@input);

    if ($input) {
      @input = split(/\n/, $input);
    } 

    # log a copy of the message to UPDLOG before we process it

    # This may seem yucky, but is needed to untaint the filename ...
    {
      my($filename) = $LOGFILE{"UPDLOG"}."/".$DATE;
      $filename =~ /(.*)/;
      $realfile = $1;
    }

    if (open(LOG, ">>$realfile")) {
      &appendlock(LOG);
    
      if ($opt_M) {
        print LOG "\n>>> time: $TIME MAIL UPDATE <<<\n\n";
      } 
      elsif ($NETWORKUPDATE) {
        print LOG "\n>>> time: $TIME $NETWORKUPDATE NETWORK UPDATE <<<\n\n";
      }
      else {
        print LOG "\n>>> time: $TIME STDIN UPDATE <<<\n\n";
      }
    }
    else {
      &syslog("ERRLOG", "dbupdate cannot open $realfile"); 
      print STDERR "dbupdate cannot open $realfile";
      exit 75;
    }

    my($cpst) = 0; # 0 = unsigned, 1 = signed data, 2 = signature
		   # 3 = encoded message

    while (1) {
      if ($input) {
	last unless defined($_ = shift(@input));
	$_ .= "\n";	# split above eats new-lines
      } else {
	last unless defined($_ = <>);
      }
      print LOG;

      unless ($have_pgp) {
	print COPY;
	next;
      }
      if ($cpst == 0 && /^-----BEGIN PGP SIGNED MESSAGE-----$/) {
	$cpst = 1;

	my($copyfile_signed) = $TMPDIR . "/dbupdscopy.$$." . @copyfiles_signed;
	push(@copyfiles_signed, $copyfile_signed);

	open(PGPCOPY, ">$copyfile_signed")
	    or fatalerror("Cannot create $copyfile_signed: $!");

	# fall through
      }
      if ($cpst == 0 && /^-----BEGIN PGP MESSAGE-----$/) {
	$cpst = 3;

	my($copyfile_signed) = $TMPDIR . "/dbupdscopy.$$." . @copyfiles_signed;
	push(@copyfiles_signed, $copyfile_signed);

	open(PGPCOPY, ">$copyfile_signed")
	    or fatalerror("Cannot create $copyfile_signed: $!");

	# fall through
      }
      if ($cpst == 0) {
	print COPY;
	next;
      }
      if ($cpst == 1 && /^-----BEGIN PGP SIGNATURE-----$/) {
	$cpst = 2;
	# fall through
      }
      if ($cpst == 2 && /^-----END PGP SIGNATURE-----$/) {
	$cpst = 0;
	print PGPCOPY;
	close(PGPCOPY);
	next;
      }
      if ($cpst == 3 && /^-----END PGP MESSAGE-----$/) {
	$cpst = 0;
	print PGPCOPY;
	close(PGPCOPY);
	next;
      }
      if ($cpst == 1 || $cpst == 2 || $cpst == 3) {
	print PGPCOPY;
	next;
      }
      fatalerror("Unknown cpst state: $cpst (should not happen)");
      last; # not reached?
    }

    # discard (last) signed part if it is not properly finished
    if ($cpst != 0) {
      close(PGPCOPY);
      unlink(pop(@copyfiles_signed));
    }

    close(LOG);

    &flush(COPY);
    seek(COPY, 0, 0);

    # when building the acknowledgement text processdata will output
    # per object messages but in the output we want to flag sucess or
    # failure at the top of the message but won't know which it is until
    # we get to the bottom.
    # 
    # so create a file for processdata's text then slurp this into an 
    # array once it has finished.
    #
    # open the acknowledgement file
    
    local($ackfilename)=$TMPDIR."\/dbupdack.".$$;

    # file to put acknowledgement text in
    open(ACK, "+>".$ackfilename) || &fatalerror("Cannot create $ackfilename: $!");

    # array to put acknowledgement text in
    my(@ack)=();

    #
    # We have a mail, so let's first parse the headers ...

    local($stat)=1;
    undef @subj_ans;

    $stat=&parserfc822(COPY) if ((!$opt_m) && ($opt_M));

    &ReplaceGlobalVars(*FWHEADER);
    &ReplaceGlobalVars(*FWTXT);
    &ReplaceGlobalVars(*FWMAILTXT);
    &ReplaceGlobalVars(*MTFWHEADER);
    &ReplaceGlobalVars(*MTFWTXT);
    
    &ReplaceGlobalVars(*NHEADER);
    &ReplaceGlobalVars(*NOTITXT);
    &ReplaceGlobalVars(*NOTIMAILTXT);

    if ($opt_M) {
       
      &ReplaceGlobalVars(*MHEADER);
      &ReplaceGlobalVars(*FHEADER);
      &ReplaceGlobalVars(*MAILTXT);
      &ReplaceGlobalVars(*ACKERR);
      &ReplaceGlobalVars(*ACKOK);
      &ReplaceGlobalVars(*ACKSIG);
      &ReplaceGlobalVars(*SUCCESSTXT);
      &ReplaceGlobalVars(*FAILURETXT);
      &ReplaceGlobalVars(*HELPHEADER);
    }

    # if Help was requested just reply with the help
    # don't parse the body of the message

    # $HELPREQUESTED is set in parserfc822
    # so if it is set we are definately in mail mode
    if ($HELPREQUESTED) {
   
      push @ack, "To: $REPLYTO\n";

      push @ack, $HELPHEADER, "\n";
	 
      open(HELP, "<".$AUTODBMHELP);
      push @ack, "=" x 60, "\n\n", <HELP>, "\n", "=" x 60, "\n";
      close(HELP);
      
    } else {

      #
      # PGP signature state: undef if not signed, keyID if signed & ok
      #

      $PGP_signed = undef;

      #
      # Take all the stuff from file COPY in doaction. It will process the
      # whole stuff.
      #

      my($somethingfound, $haserror) = processdata(COPY, ACK);
      close(COPY);

      #
      # Then we process the PGP signed parts
      #

      foreach $copyfile_signed (@copyfiles_signed) {
	my($copyfile_signed_out) = $TMPDIR . "/dbupdscopyo.$$";
	my($somethingfound_new, $haserror_new);

	my($pgpsigstatus, $pgpsigid) = 
	    checkPGPmsg($copyfile_signed, $copyfile_signed_out);

	if (!defined($pgpsigstatus) || $pgpsigstatus == -1) { 
	  # no signature found by PGP or PGP error
	  # process the part as w/o signature (shouldn't happen?)
          $PGP_signed = undef;

	  if ($pgpsigstatus == -1) {
	    print ACK "*** Cannot check PGP signed part: $pgpsigid\n\n";
	    $haserror = $somethingfound = 1;
	  }

          open(SCOPY, $copyfile_signed)
	      or fatalerror("Cannot open $copyfile_signed: $!");

	  ($somethingfound_new, $haserror_new) = processdata(SCOPY, ACK);
	  close(SCOPY);
           
	  $somethingfound = $somethingfound_new if $somethingfound_new;
	  $haserror = $haserror_new if $haserror_new;
	} elsif ($pgpsigstatus == 0) { # PGP signature not OK, ignore this part
          $PGP_signed = undef;

	  print ACK "*** Ignoring PGP signed part with BAD signature"
	      . ($pgpsigid ? " from key ID $pgpsigid" : "") . "\n\n";
	  $haserror = $somethingfound = 1;

	} elsif ($pgpsigstatus == 1) { # signature by unknown keyID
	  $PGP_signed = undef;

	  print ACK "*** Beginning of PGP signed part "
	      . "from *UNKNOWN* key ID $pgpsigid\n\n";
	  $haserror = $somethingfound = 1;

          open(SCOPY, $copyfile_signed_out)
	      or fatalerror("Cannot open $copyfile_signed_out: $!");

	  ($somethingfound_new, $haserror_new) = processdata(SCOPY, ACK);
	  close(SCOPY);
           
	  $somethingfound = $somethingfound_new if $somethingfound_new;
	  $haserror = $haserror_new if $haserror_new;

	  print ACK "*** End of PGP signed part "
	      . "from *UNKNOWN* key ID $pgpsigid\n\n";

	} elsif ($pgpsigstatus == 2) { # PGP signature OK
          $PGP_signed = $pgpsigid;

	  print ACK "*** Beginning of PGP signed part "
	      . "from key ID $pgpsigid\n\n";
	  $somethingfound = 1;

          open(SCOPY, $copyfile_signed_out)
	      or fatalerror("Cannot open $copyfile_signed_out: $!");

	  ($somethingfound_new, $haserror_new) = processdata(SCOPY, ACK);
	  close(SCOPY);
           
	  $somethingfound = $somethingfound_new if $somethingfound_new;
	  $haserror = $haserror_new if $haserror_new;

	  print ACK "*** End of PGP signed part "
	      . "from key ID $pgpsigid\n\n";

	} elsif ($pgpsigstatus == 3) { # message encrypted & can't decrypt
          $PGP_signed = undef;

	  print ACK "*** Ignoring PGP encrypted part (can't decrypt)\n\n";
	  $haserror = $somethingfound = 1;

	} elsif ($pgpsigstatus == 4) { # message encrypted & can decrypt
          $PGP_signed = undef;

	  print ACK "*** Beginning of PGP encrypted part\n\n";
	  $haserror = $somethingfound = 1;

          open(SCOPY, $copyfile_signed_out)
	      or fatalerror("Cannot open $copyfile_signed_out: $!");

	  ($somethingfound_new, $haserror_new) = processdata(SCOPY, ACK);
	  close(SCOPY);
           
	  $somethingfound = $somethingfound_new if $somethingfound_new;
	  $haserror = $haserror_new if $haserror_new;

	  print ACK "*** End of PGP encrypted part\n\n";

	} else { # shouldn't ever happen :-)
          fatalerror("Unknown PGP sig state: $pgpsigstatus (shouldn't happen)");
	}
	unlink($copyfile_signed_out);
	$PGP_signed = undef;
      }

      # rewind the ack file generated by processdata
      &flush(ACK);
      seek(ACK, 0, 0);

      if ($opt_M) {

	# generate To: for reply message

	if ($stat) {

  	  if ($TESTMODE) {
	    push @ack, "To: ", $DEFMAIL, "\n";
	    push @ack, "X-Ack-Orig-Recipient: ", $REPLYTO, "\n";
	  }
	  else {
	    #
	    # note: don't use CC since it might cause horrible
	    #       mailloops as we discovered ...
	    push @ack, "To: ", $REPLYTO, "\n";
	  }
	  
	}
	else {
 	  push @ack, "Header could not be parsed ...\n";
	}

	#
	# generate output message

	# if all updates were successful
	#
	if ($somethingfound && !$haserror) {

	  push @ack, $SUCCESSTXT;

	  # otherwise some failed
	} else {

	  push @ack, $FAILURETXT;

	}
      }

      #
      # add output from processdata or 'no objects found'
      # to acknowlegement

      if ($somethingfound) {
	push @ack, <ACK>;
      } else {
	push @ack, "*** No objects were found ***\n" unless ($NETWORKUPDATE);
      }
      # include diagnostics from Subject: parser
      push @ack, @subj_ans;    
    }

#    if ($somethingfound) {
#
#      if ($haserror) {
#	push @ack, $ACKERR if ($opt_M);
#      }
#      else {
#	push @ack, $ACKOK if ($opt_M);
#      }  
#      push @ack, $ACKSIG if ($opt_M);
#
#    } else { # nothing found
#
#      unless $HELPREQUESTED {
#	push @ack, "*** No objects were found ***\n" if (!$NETWORKUPDATE);
#	push @ack, $ACKSIG if ($opt_M);
#      }
#
#    }

    #
    # We sent out the ack, now send out the notifications if needed

    if (%notify) {
#       &dpr("sending notifications (", keys %notify,")\n");
       &SendNotifications();
    }

    # send out cross notifications

    if (@::cross_notification_mailfiles) {
      send_cross_notifications(@::cross_notification_mailfiles);
    }

    if (%forward) {
#       &dpr("sending forwards(", keys %forward, ")\n");
       &SendForwardMails();
    }

    #
    # then we log the acknowledgement

    $filename = $LOGFILE{"ACKLOG"}."/".$DATE;
    $filename =~ /(.*)/;
    $realfile = $1;

    if (open(LOG, ">>$realfile")) {
    
#       &dpr("updating logs\n");
    
       &appendlock(LOG);
    
       if ($opt_M) {
          print LOG "\n>>> time: $TIME MAIL ACK <<<\n\n";
       }
       elsif ($NETWORKUPDATE) {
          print LOG "\n>>> time: $TIME $NETWORKUPDATE NETWORK ACK <<<\n\n";
       }
       else {
          print LOG "\n>>> time: $TIME STDIN ACK <<<\n\n";
       }
    
    
#       local(@ack)=<ACK>;
       print LOG "Not Sent - suppressed by notify to [$::DontAck]\n"
	 if ($::DontAck);
       print LOG @ack;
       print $output @ack if (!$opt_M);
       close(LOG);
    
    }
    else {
       &syslog("ERRLOG", "dbupdate cannot open $LOGFILE{\"ACKLOG\"}/$DATE"); 
    }

    close(ACK);

    

    #
    # Output the ack
    #
    # unless we have send a notify to the same address

    if ($opt_M) {
	if ($::DontAck) {
#	    &dpr("dbupdate - notification to [$REPLYTO] ",
#		 "suppresses ack to [$::DontAck]\n");
	    undef $::DontAck;
	} else {
#	    &dpr("dbupdate - sending ack\n");
#	system("$MAILCMD <".$ackfilename);
	    open(MAIL, "|$MAILCMD");
#	open(MAIL, "|/bin/cat");
	    print MAIL @ack;
	    close MAIL || warn "mail: $!"
#	system("/bin/cat <".$ackfilename);
	    }
    }
    
    #
    # remove the temp stuff we made

    unlink($copyfile_unsigned, $ackfilename, @copyfiles_signed);

}

1;
