/***************************************
  $Revision: 1.43 $

  gpg.c - core of the PA module. Contains functions that are used
  to check the PGP authentication in a message.

  Status: COMPLETE, REVUED, TESTED

  ******************/ /******************
  Filename            : gpg.c
  Authors             : Filippo Portera, Daniele Arena
  OSs Tested          : Solaris 7
  ******************/ /******************
  Copyright (c) 2000,2001,2002                    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.
  ***************************************/

#include "rip.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/param.h>


extern int  EP_TreeHeight;
extern int  EP_Node_ID;
extern int  EP_Debug;


/* static int parseMailBlock_nMsg;
   static int parseRecursionLevel; */

extern int sd1[2];
extern int spawn_job (char *path, char *argv[], 
		      int *in_fd, int *out_fd, int *err_fd);
extern time_t nfslock(char *path, char *namelock, int max_age, int notify);
extern int nfsunlock(char *path, char *namelock, int max_age, time_t birth);


static char* outputPrefix = NULL;
static char* keyRing = NULL;
static char* gpgCmd = NULL;
static char *tmpdir = NULL;

static void VerifySignAndExplodeFile(EPNodePtr ptr);
static void GetKeyID(struct ImportKeyObject *iKO);


/**************************************
 *
 * API functions
 *
 **************************************/

/*++++++++++++++++++++++++++++

Copies a string with memory allocation care.

char* dest The destination string
char* orig The origin string


++++++++++++++++++++++++++++*/

void pa_strcpy(char** dest, char* orig) {
  if (*dest) {
    free(*dest);
  }

  *dest = malloc(strlen(orig) + 1);
  strcpy(*dest, orig);
}

/*++++++++++++++++++++++++++++

Set the GPG temporary directory.

char* dir The directory


This memory allocated is never freed, maybe a PA_end function?

++++++++++++++++++++++++++++*/

void PA_SetTmpDir(char* dir) {
  pa_strcpy(&tmpdir, dir);
}

/*++++++++++++++++++++++++++++

Set the GPG command line.

char* path The path including command name


This memory allocated is never freed, maybe a PA_end function?

++++++++++++++++++++++++++++*/

void PA_SetGPGCmd(char* path) {
  pa_strcpy(&gpgCmd, path);
}

/*++++++++++++++++++++++++++++

Set the path to the Keyring.

char* path The path including file name

++++++++++++++++++++++++++++*/

void PA_SetKeyRing(char* path) {
  pa_strcpy(&keyRing, path);
}

/*++++++++++++++++++++++++++++

Set the path to the output file.

char* path The path including file name

++++++++++++++++++++++++++++*/

void PA_SetOutputPrefix(char* path) {
  pa_strcpy(&outputPrefix, path);
}

/*++++++++++++++++++++++++++++

Verify a detached PGP signature.

struct VerifySignObject *vSO	The signed object structure to be verified.

++++++++++++++++++++++++++++*/

void PA_VerifySignature(struct VerifySignObject *vSO) {
  char *strArgs[10];
  char Args0[100];
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100],
    Args6[100], Args7[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  /* static int nMsgs = 0; */
  char txt[LINE_LENGTH];
  char *keyStr;
  /* int childRC; */

  int fIn,fOut;
  char tmpFileName[100],lfcrStr[10],strIn[10];
  char prevChar;
  FILE *mystdin;

  vSO->type = vSO_Type_Signed;

  strcpy(Args0, "--no-secmem-warning");
  strcpy(Args1, "--keyring");
  strcpy(Args2, keyRing);
  strcpy(Args3, "-o");
  if (!strcmp(vSO->iSigFilename, "")) {
    strcpy(Args4, vSO->oStream);
    strcpy(Args5, "-d");
    strcpy(Args6, vSO->iDocSigFilename);
    strArgs[6] = Args6;
    strArgs[7] = (char *)0;
  } else {
    /* change <cr> to <lf>+<cr> to be related-rfc compliant */
    fIn=open(vSO->iDocSigFilename,O_RDONLY);
    if (fIn==-1) {
      ER_perror(FAC_PA, PA_CANTREAD, "can't open %s for reading", vSO->iDocSigFilename);
      exit(1);
    }
    strcpy(tmpFileName,tmpdir);
    strcat(tmpFileName,"/patmpXXXXXXX");
    fOut=mkstemp(tmpFileName);
    if (fOut==-1) {
      ER_perror(FAC_PA, PA_NOTEMP, "%s", tmpFileName);
      exit(1);
    }
    prevChar=0;
    sprintf(lfcrStr,"%c%c%c",13,10,0);
    while(read(fIn,strIn,1)>0)
    {
      if ((strIn[0]==10)&&(prevChar!=13)) {
	write(fOut,lfcrStr,2);
      } else {
	write(fOut,strIn,1);
      }
      prevChar=strIn[0];
    }
    close(fOut);
    close(fIn);
    /* end change <cr> to <lf>+<cr> to be related-rfc compliant */

    strcpy(Args5, "--verify");
    strcpy(Args6, vSO->iSigFilename);
    strcpy(Args7, tmpFileName);
    
    strArgs[6] = Args6;
    strArgs[7] = Args7;
    strArgs[8] = (char *)0;
    strcpy(vSO->oStream, vSO->iDocSigFilename);
  }

  strArgs[0] = Args0;
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;
  strArgs[4] = Args4;
  strArgs[5] = Args5;
  
  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;
  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 )
    {
      ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
      exit(1);
    }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      exit(1);
    }
  unlink(tmpFileName);
  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
      exit(1);
    } else {
      /* Child exited, checking return code */
      /* childRC = (status & 0xF00) >> 8;
      if (childRC == 1) {
	fprintf (stderr, "Fatal: gpg child return code: %d\n", childRC);
	printf ("gpg failure\n");
	exit(1);
	} */
    }


  /* Parsing gpg output */
  mystdin = fdopen(0, "r");
  vSO->isValid = vSO_KO;
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
      /* 	printf ( "GPG output : %s\n", txt );   */
      if (strstr(txt, "Good signature") != NULL)
	vSO->isValid = vSO_IS_VALID;

      if (strstr(txt, "CRC error") != NULL)
	vSO->isValid = vSO_CRC_ERROR;

      if (strstr(txt, "public key not found") != NULL)
	vSO->isValid = vSO_NO_PUBLIC_KEY;

      if (strstr(txt, "no valid OpenPGP data found") != NULL)
	vSO->isValid = vSO_NO_OPENPGP_DATA;

      if ((keyStr = strstr(txt, "key ID")) != NULL) {
	keyStr += 7;
	sscanf(keyStr, "%8X\n", &vSO->keyID);
      }
    }
    
  if (sd1[0] != 0)  close ( sd1[0] ); 
}



/*++++++++++++++++++++++++++++

Decrypt a PGP-encrypted file.

struct ReadCryptedObject *rDO	The object to be decrypted


Note:
  This functions is not used by PA/EP/MM
  It can be useful in the future....  (FP)

++++++++++++++++++++++++++++*/

void PA_Decrypt(struct ReadCryptedObject *rDO) {
  
  char *strArgs[9];
  char clearTextExtension[4] = ".gpg";
  char Args0[100];
  char Args1[100];
  char Args2[100];
  char Args3[100];
  char Args4[100];
  char Args5[100];
  char Args6[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  int childRC;
  FILE *mystdin;

  strcpy(Args0, "--no-tty");
  strcpy(Args1, "--no-secmem-warning");
  strcpy(Args2, "--keyring");
  strcpy(Args3, rDO->keyRing);
  strcpy(Args4, "--output");
  strcpy(Args5, strcat(rDO->iFilename, clearTextExtension));
  strcpy(Args6, rDO->iFilename);
  
  strArgs[0] = Args0;
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;  
  strArgs[4] = Args4;  
  strArgs[5] = Args5;  
  strArgs[6] = Args6;  
  strArgs[7] = (char *) 0;   

  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;
  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 )
    {
      ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
      exit(1);
    }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      exit(1);
    }
  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
      exit(1);
    } else {
      /* Child exited, checking return code */
      childRC = (status & 0xF00) >> 8;
      if (childRC == 1) {
	ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	exit(1);
      } 
    }


  /* Parsing gpg output */
  mystdin = fdopen(0, "r");
  while (fgets (txt, STRING_LENGTH - 1, mystdin) != NULL)
    {
      
    }
  
  if (sd1[0] != 0)  close ( sd1[0] ); 
}



/*++++++++++++++++++++++++++++

Import a PGP key.

struct ImportKeyObject *iKO	The structure where the imported key goes

++++++++++++++++++++++++++++*/

void PA_ImportKey(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100];
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr, *pos;
  const char lockFilename[] = ".PAlock";
  char keyRingLockFile[1000], keyRingPath[1000];
  time_t lockBirthDate;
  FILE *mystdin;
  int childRC;
  int key_count;
  GList *key_list = NULL;
  GList *next = NULL;
  struct ImportKeyObject iKO_toBeRemoved;

  iKO->rc = iKO_GENERALFAILURE;

  strcpy(Args0, "--no-tty");
  strcpy(Args1, "--no-secmem-warning");
  strcpy(Args2, "--keyring");
  strcpy(Args3, iKO->keyRing);
  strcpy(Args4, "--import");
  strcpy(Args5, iKO->iFilename);

  strArgs[0] = Args0;  
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;  
  strArgs[4] = Args4;  
  strArgs[5] = Args5;
  strArgs[6] = (char *)0;

  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

  strcpy(keyRingLockFile, iKO->keyRing);
  if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
    strcpy(pos + 1, lockFilename);
    strcpy(keyRingPath, keyRingLockFile);
    keyRingPath[pos - keyRingLockFile] = 0;
  } else {
    strcpy(keyRingLockFile, lockFilename);
    strcpy(keyRingPath, "");
  }
  
  lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);

  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
    ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
    exit(1);
  }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
      exit(1);
    }

  nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);

  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
    } 
    else 
    {
      /* Child exited, checking return code */
      childRC = (status & 0xF00) >> 8;
      if (tracing)
        printf("TRACING: PA_ImportKey child return code [%d]\n", childRC);
      if (childRC == 1) 
      {
        ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
        /*  exit(1);  */
      }
    }


  /* Parsing gpg output */
  /*   while (read(0, txt, 1000) != 0)
       fprintf(stderr, "child read %s\n", txt); */

  mystdin = fdopen(0, "r");
  iKO->rc = iKO_GENERALFAILURE;
  key_count = 0;
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
  {
    /* printf ( "GPG output : %s\n", txt );      */

    if ((keyStr = strstr(txt, "imported")) != NULL)
    {
	  iKO->rc = iKO_OK;
    }

    if ((keyStr = strstr(txt, "CRC error")) != NULL)
    {
	  iKO->rc = iKO_CRC_ERROR;
    }

    if ((keyStr = strstr(txt, "no valid OpenPGP")) != NULL)
    {
	  iKO->rc = iKO_NO_OPENPGP_DATA;
    }

    if ((keyStr = strstr(txt, "invalid armor header")) != NULL)
    {
	  iKO->rc = iKO_INVALID_ARMOR_HEADER;
    }

    if (((keyStr = strstr(txt, "unchanged")) != NULL) || 
	  ((keyStr = strstr(txt, "not changed")) != NULL))
    {
	  iKO->rc = iKO_UNCHANGED;
    }

    if ((keyStr = strstr(txt, "key")) != NULL)
    {
	  keyStr += 4;
	  sscanf(keyStr, "%8X\n", &iKO->keyID); 
      key_count++;
      /* and put the key ID into the keys list (if it was new to the keyring)*/
      if(strstr(txt, "imported") != NULL)
      {
        key_list = g_list_append(key_list, GINT_TO_POINTER(iKO->keyID));
      }
    } 
  }

  if (sd1[0] != 0)  close ( sd1[0] ); 

  if(key_count > 1){/* if there were more than one keys imported */
    iKO->rc = iKO_MULTIPLE_KEYS; /* this is an error */
    /* now, roll-back, remove the added keys from key-ring */
    for( next = key_list; next != NULL; next = g_list_next(next) ){
      strcpy(iKO_toBeRemoved.keyRing, 
          iKO->keyRing);
      iKO_toBeRemoved.keyID = (u32)(next->data);
      PA_RemoveKey_withKeyID(&iKO_toBeRemoved);
    }
    
  }else{
    /* Get the finger print */
    GetFingerPrint(iKO);
    GetKeyOwner(iKO);
  }
}



/*++++++++++++++++++++++++++++

Remove a PGP key.

struct ImportKeyObject *iKO	The structure containing the key to be removed

++++++++++++++++++++++++++++*/

void PA_RemoveKey(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100]= "gpg";
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100], Args6[100], Args7[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr, *pos;
  const char lockFilename[] = ".PAlock";
  char keyRingLockFile[1000], keyRingPath[1000];
  time_t lockBirthDate;
  FILE *mystdin;
  int childRC;

  iKO->rc = iKO_GENERALFAILURE;

  GetKeyID(iKO);   /* getting key-id */

  /*   printf("Key id = %08lX\n", iKO->keyID); */

  if ((iKO->rc == iKO_OK) || (iKO->rc == iKO_UNCHANGED)) {    
    strcpy(Args1, "--batch");
    strcpy(Args2, "--yes");
    strcpy(Args3, "--no-secmem-warning");
    strcpy(Args4, "--keyring");
    strcpy(Args5, iKO->keyRing); 
    strcpy(Args6, "--delete-key");
    sprintf(Args7, "%08X", iKO->keyID);

    strArgs[0] = Args0;  
    strArgs[1] = Args1;  
    strArgs[2] = Args2;  
    strArgs[3] = Args3;  
    strArgs[4] = Args4;  
    strArgs[5] = Args5;
    strArgs[6] = Args6;
    strArgs[7] = Args7;
    strArgs[8] = (char *)0;  
  

    gpg_in_fd = INPUT_FD;
    out_fd = OUTPUT_FD;
    err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

    strcpy(keyRingLockFile, iKO->keyRing);
    if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
      strcpy(pos + 1, lockFilename);
      strcpy(keyRingPath, keyRingLockFile);
      keyRingPath[pos - keyRingLockFile] = 0;
    } else {
      strcpy(keyRingLockFile, lockFilename);
      strcpy(keyRingPath, "");
    }
  
    lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);

    if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
				&gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
      ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
      exit(1);
    }

    /*    printf("Child pid = %d\n", gpg_pid); */
  
    if (waitpid (gpg_pid, &status, 0) < 0)
      {
	ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
	exit(1);
      }

    nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);

    if (WIFEXITED(status) == 0)
    {
	ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
	exit(1);
    }
    else
    {
	  /* Child exited, checking return code */
	  childRC = (status & 0xF00) >> 8;
	  if (childRC == 1)
      {
	    ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	  }
    }


    mystdin = fdopen(0, "r");
    iKO->rc = iKO_OK;
    while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
	  /* printf ( "GPG output : %s\n", txt );        */

	  if ((keyStr = strstr(txt, "delete key failed")) != NULL)
      {
	    iKO->rc = iKO_GENERALFAILURE;
	  }
	  if ((keyStr = strstr(txt, "there is a secret key for this public key")) != NULL)
      {
	    iKO->rc = iKO_SECRET_KEY_PRESENT;
	  }
    }

    if (sd1[0] != 0)  close ( sd1[0] ); 
  }
}

/*++++++++++++++++++++++++++++

Remove a PGP key, using its KeyID (otherwise it's the same as PA_RemoveKey

struct ImportKeyObject *iKO	The structure containing the key to be removed

++++++++++++++++++++++++++++*/

void PA_RemoveKey_withKeyID(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100]= "gpg";
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100], Args6[100], Args7[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr, *pos;
  const char lockFilename[] = ".PAlock";
  char keyRingLockFile[1000], keyRingPath[1000];
  time_t lockBirthDate;
  FILE *mystdin;
  int childRC;

  iKO->rc = iKO_GENERALFAILURE;


    strcpy(Args1, "--batch");
    strcpy(Args2, "--yes");
    strcpy(Args3, "--no-secmem-warning");
    strcpy(Args4, "--keyring");
    strcpy(Args5, iKO->keyRing); 
    strcpy(Args6, "--delete-key");
    sprintf(Args7, "%08X", iKO->keyID);

    strArgs[0] = Args0;  
    strArgs[1] = Args1;  
    strArgs[2] = Args2;  
    strArgs[3] = Args3;  
    strArgs[4] = Args4;  
    strArgs[5] = Args5;
    strArgs[6] = Args6;
    strArgs[7] = Args7;
    strArgs[8] = (char *)0;  
  

    gpg_in_fd = INPUT_FD;
    out_fd = OUTPUT_FD;
    err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

    strcpy(keyRingLockFile, iKO->keyRing);
    if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
      strcpy(pos + 1, lockFilename);
      strcpy(keyRingPath, keyRingLockFile);
      keyRingPath[pos - keyRingLockFile] = 0;
    } else {
      strcpy(keyRingLockFile, lockFilename);
      strcpy(keyRingPath, "");
    }
  
    lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);


    if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
				&gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
      ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
      exit(1);
    }

    /*    printf("Child pid = %d\n", gpg_pid); */
  
    if (waitpid (gpg_pid, &status, 0) < 0)
      {
	ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
	exit(1);
      }

    nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);

    if (WIFEXITED(status) == 0)
      {
	ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
	exit(1);
      }  else {
	/* Child exited, checking return code */
	childRC = (status & 0xF00) >> 8;
	if (childRC == 1) {
	  ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	}
      }


    mystdin = fdopen(0, "r");
    iKO->rc = iKO_OK;
    while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
      {
	/* printf ( "GPG output : %s\n", txt );        */

	if ((keyStr = strstr(txt, "delete key failed")) != NULL) {
	  iKO->rc = iKO_GENERALFAILURE;
	}
	if ((keyStr = strstr(txt, "there is a secret key for this public key")) != NULL) {
	  iKO->rc = iKO_SECRET_KEY_PRESENT;
	}

      }

    if (sd1[0] != 0)  close ( sd1[0] ); 
  
}




/*++++++++++++++++++++++++++++

Parse a file and look for PGP-signed elements inside. 

EPNodePtr ptr	The pointer to the EP treenode containing the file to be parsed.

++++++++++++++++++++++++++++*/

EPNodePtr PA_ParseMessage(EPNodePtr ptr) {

  /* Assumptions:
     - ptr is describing a text file, not MIME
     - input file is broken down to pieces, plain text or PGP blocks  
     - if input file is doesn't have any PGP block, this is a leaf
     - otherwise send each block to the proper handler.
  */

  FILE *fin, *fout;
  char txt[MAX_LINE_BUF], *strptr;
  /* char blockFilename[LINE_LENGTH]; */
  const char PGP_prefix_msg[] =       "-----BEGIN PGP MESSAGE";
  const char PGP_suffix_msg[] =       "-----END PGP MESSAGE";
  const char PGP_prefix_signed[] =    "-----BEGIN PGP SIGNED MESSAGE";
  const char PGP_suffix_signature[] = "-----END PGP SIGNATURE";
  int found_prefix = 0, found_suffix = 0;
  EPNodePtr p = ptr, prev = ptr;
  int end_of_fin = 0, text_block = 1;


  ER_dbg_va (FAC_PA, ASP_PA_GEN, "Entering PA_ParseMessage...");
  

  if ((fin = fopen(ptr->file, "r")) != NULL) { 

    do {
      /* this is needed because a text block parser ends when it finds
	 a PGP prefix, so we already have a txt buffer.   */ 
      
      if (!text_block || (prev == ptr)) {
	strptr = fgets(txt, MAX_LINE_BUF, fin);
	if (strptr == NULL ) end_of_fin = 1;
      }

      if (!end_of_fin && (found_prefix || (strstr(txt, PGP_prefix_msg) != NULL) ||
			  (strstr(txt, PGP_prefix_signed) != NULL))) {
	/* PGP block */
	found_prefix = 1;
	text_block = 0;
	
	p = EP_DefineNewNode(++EP_Node_ID, vS_TO_BE_PGPVERIFIED, 
			     ptr->MIMEContentType, ptr->strMIMEContentType, 0);
	
	if (prev != ptr)
	  prev->next = p;
	else
	  ptr->inner = p;
	
	if ((fout = fopen(p->file, "w")) != NULL ) {
	  fputs(txt, fout);
	  /* To be replaced by fwrite(), more efficient */
	  while ((found_prefix != found_suffix) && 
		 ((strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL)) {
	    if (strstr(txt, PGP_prefix_msg) != NULL) found_prefix++;
	    if (strstr(txt, PGP_prefix_signed) != NULL) found_prefix++;
	    if (strstr(txt, PGP_suffix_msg) != NULL) found_suffix++;
	    if (strstr(txt, PGP_suffix_signature) != NULL) found_suffix++;
	    fputs(txt, fout);
	  }
	  if (strptr == NULL ) end_of_fin = 1;
	  
	  fclose(fout);
	  
	  if (found_prefix == found_suffix) {
	    found_prefix = found_suffix = 0;
	    
	    VerifySignAndExplodeFile(p);
	    
	    /* Called form EP_ParseMail or EP_PArseText ? */
	    
	    if (strstr(outputPrefix, "EPMtmp") != NULL)
	      {
		ER_dbg_va (FAC_PA, ASP_PA_GEN, "Found prefix and suffix; calling EP_MIMEParse...");
		EP_MIMEParse(p);
	      }
	    else {
              PA_ParseMessage(p);
            }
	    prev = p;
	  } else {
	    /* Wrong PGP delimiters order. */
	    p->isValidPGPSignature = vS_UNMATCHED_PGP_DELIMITERS;
	  }
	} else {
	  p->isValidPGPSignature = vS_UNABLE_TO_WRITE_FILE;
	  return p;
	}
	
      } else {
	/* Clear text block */

	text_block = 1;

	if (strptr == NULL) end_of_fin = 1;
	else {
	  p = EP_DefineNewNode(++EP_Node_ID, vS_IS_NOT_PGP, 
			       ptr->MIMEContentType, 
			       ptr->strMIMEContentType, 0);
	
	  if (prev != ptr)
	    prev->next = p;
	  else
	    ptr->inner = p;

	  if ((fout = fopen(p->file, "w")) != NULL ) {
	    fputs(txt, fout);
	    /* To be replaced by fwrite(), more efficient */
	    while ((!found_prefix && 
		    (strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL)) {
	      if ((strstr(txt, PGP_prefix_msg) != NULL) ||
		  (strstr(txt, PGP_prefix_signed) != NULL)) found_prefix++;
	      else 
		fputs(txt, fout);
	    }
	    if (strptr == NULL ) end_of_fin = 1;

	    fclose(fout);

	    /* Check if the blockfile is finished and this is the first
	       segment. If so this is a text leaf */
	    if (found_prefix || (prev != p)) {
	      if (prev->MIMEContentType == -1)
		{
		  if (strstr(outputPrefix, "EPMtmp") != NULL)
		    {
		      ER_dbg_va (FAC_PA, ASP_PA_GEN, "PA_ParseMessage: sending to EP_MIMEParse");
		      EP_MIMEParse(p);
		    }
		  else {
		    PA_ParseMessage(p);
                  }
		}
	    
	      prev = p;
	    }
	    
	  } else {
	    p->isValidPGPSignature = vS_UNABLE_TO_WRITE_FILE;
	    return p;
	  }
	}
      }
    } while (!end_of_fin);
  } else {
    p->isValidPGPSignature = vS_NO_IN_FILES;
  }

  return ptr;
}


/**************************************
 *
 * Internal functions
 *
 **************************************/



/*++++++++++++++++++++++++++++

Get the fingerprint of a PGP key.

ImportKeyObject *iKO	The imported key object

++++++++++++++++++++++++++++*/

void GetFingerPrint(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100] ;
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr;
  FILE *mystdin;
  int childRC;

  strcpy(Args0, "--no-tty");
  strcpy(Args1, "--no-secmem-warning");
  strcpy(Args2, "--keyring");
  strcpy(Args3, iKO->keyRing);
  strcpy(Args4, "--fingerprint");
  sprintf(Args5, "%08X", iKO->keyID);

  strArgs[0] = Args0;  
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;  
  strArgs[4] = Args4;  
  strArgs[5] = Args5;
  strArgs[6] = (char *)0;

  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
    ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
    exit(1);
  }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      exit(1);
    }

  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
      exit(1);
    } else {
      /* Child exited, checking return code */
      childRC = (status & 0xF00) >> 8;
      if (childRC == 1) {
	ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	exit(1);
      }
    }


  mystdin = fdopen(0, "r");
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
      /* printf ( "GPG output : %s\n", txt );      */

      if ((keyStr = strstr(txt, "Key fingerprint =")) != NULL) {
	strcpy(iKO->fingerPrint, keyStr + 18);
	iKO->fingerPrint[strlen(iKO->fingerPrint)-1] = 0;
      }

      if ((keyStr = strstr(txt, "key")) != NULL) {
	 keyStr += 4;
	 sscanf(keyStr, "%8X\n", &iKO->keyID); 
      } 
    }

  if (sd1[0] != 0)  close ( sd1[0] ); 
}



/*++++++++++++++++++++++++++++

Get the owner of a PGP key.

ImportKeyObject *iKO	The imported key object

++++++++++++++++++++++++++++*/

void GetKeyOwner(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100] ;
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr;
  FILE *mystdin;
  int childRC;

  strcpy(Args0, "--no-tty");
  strcpy(Args1, "--no-secmem-warning");
  strcpy(Args2, "--keyring");
  strcpy(Args3, iKO->keyRing);
  strcpy(Args4, "--fingerprint");
  sprintf(Args5, "%08X", iKO->keyID);

  strArgs[0] = Args0;  
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;  
  strArgs[4] = Args4;  
  strArgs[5] = Args5;
  strArgs[6] = (char *)0;

  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
    ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
    exit(1);
  }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      exit(1);
    }

  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
      exit(1);
    } else {
      /* Child exited, checking return code */
      childRC = (status & 0xF00) >> 8;
      if (childRC == 1) {
	ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	exit(1);
      }
    }


  mystdin = fdopen(0, "r");
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
      /* printf ( "GPG output : %s\n", txt );      */

      if ((keyStr = strstr(txt, "pub "/*"Key fingerprint ="*/)) == txt /*!= NULL*/) {
	strcpy(iKO->keyOwner, keyStr + 30);
	iKO->keyOwner[strlen(iKO->keyOwner)-1] = 0;
      }

      if ((keyStr = strstr(txt, "key")) != NULL) {
	 keyStr += 4;
	 sscanf(keyStr, "%8X\n", &iKO->keyID); 
      } 
    }

  if (sd1[0] != 0)  close ( sd1[0] ); 
}



/*++++++++++++++++++++++++++++

Verify the PGP signature and extract the signed part in a file.

EPNodePtr ptr	The pointer to the EP treenode containing the originating file

++++++++++++++++++++++++++++*/

void VerifySignAndExplodeFile(EPNodePtr ptr) {
  char *strArgs[10];
  char Args0[100];
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100],
    Args6[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  /* char hostname[MAXHOSTNAMELEN]; */
  char oFile[FILENAME_LENGTH];
  char *keyStr;
  /* int childRC; */
  FILE *mystdin;

  sprintf(oFile, "%s.%d.exp", ptr->file, ptr->nodeID);

  strcpy(Args0, "--no-secmem-warning");
  strcpy(Args1, "--keyring");
  strcpy(Args2, keyRing);
  strcpy(Args3, "-o");
  strcpy(Args4, oFile);
  strcpy(Args5, "-d");
  strcpy(Args6, ptr->file);
  strArgs[6] = Args6;
  strArgs[7] = (char *)0;
  
  strArgs[0] = Args0;
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;
  strArgs[4] = Args4;
  strArgs[5] = Args5;
  
  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;

  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
      ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
      exit(1);
  }
  
  if (waitpid (gpg_pid, &status, 0) < 0) {
    ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
    exit(1);
  }
  if (WIFEXITED(status) == 0) {
    ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
    exit(1);
  }
  else {
  }


  /* Parsing gpg output */
  mystdin = fdopen(0, "r");
  ptr->isValidPGPSignature = vS_KO;
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
      /* 	printf ( "GPG output : %s\n", txt );   */
      if (strstr(txt, "Good signature") != NULL)
	ptr->isValidPGPSignature = vS_IS_VALID;

      if (strstr(txt, "CRC error") != NULL)
	ptr->isValidPGPSignature = vS_CRC_ERROR;

      if (strstr(txt, "public key not found") != NULL)
	ptr->isValidPGPSignature = vS_NO_PUBLIC_KEY;

      if (strstr(txt, "no valid OpenPGP data found") != NULL)
	ptr->isValidPGPSignature = vS_NO_OPENPGP_DATA;

      if ((keyStr = strstr(txt, "key ID")) != NULL) {
	keyStr += 7;
	sscanf(keyStr, "%8X\n", &ptr->keyID);
      }
    }
    
  if (ptr->isValidPGPSignature == vS_NO_OPENPGP_DATA) {
    FILE* f;
    f= fopen(oFile, "w");
    fprintf(f, " \n");
    fclose(f);
  }

  unlink(ptr->file);
  UT_free(ptr->file);
  ptr->file = UT_strdup(oFile);
  if (sd1[0] != 0)  close ( sd1[0] ); 
}


/*++++++++++++++++++++++++++++

Get the KeyID of a PGP key.

struct ImportKeyObject *iKO	The structure containing the key of which we want the KeyID

++++++++++++++++++++++++++++*/

void GetKeyID(struct ImportKeyObject *iKO) {
  
  char *strArgs[9];
  char Args0[100];
  char Args1[100], Args2[100], Args3[100], Args4[100], Args5[100];
  int gpg_pid;
  int gpg_in_fd, out_fd, err_fd;
  int status;
  char txt[LINE_LENGTH];
  char *keyStr, *pos;
  const char lockFilename[] = ".PAlock";
  char keyRingLockFile[1000], keyRingPath[1000];
  time_t lockBirthDate;
  FILE *mystdin;
  int childRC;

  iKO->rc = iKO_GENERALFAILURE;

  strcpy(Args0, "--no-tty");
  strcpy(Args1, "--no-secmem-warning");
  strcpy(Args2, "--keyring");
  strcpy(Args3, iKO->keyRing);
  strcpy(Args4, "--import");
  strcpy(Args5, iKO->iFilename);

  strArgs[0] = Args0;  
  strArgs[1] = Args1;  
  strArgs[2] = Args2;  
  strArgs[3] = Args3;  
  strArgs[4] = Args4;  
  strArgs[5] = Args5;
  strArgs[6] = (char *)0;

  gpg_in_fd = INPUT_FD;
  out_fd = OUTPUT_FD;
  err_fd = ERROR_FD;

  /* create lock file filenames for NFS */

  strcpy(keyRingLockFile, iKO->keyRing);
  if ((pos = strrchr(keyRingLockFile, '/')) != NULL) {
    strcpy(pos + 1, lockFilename);
    strcpy(keyRingPath, keyRingLockFile);
    keyRingPath[pos - keyRingLockFile] = 0;
  } else {
    strcpy(keyRingLockFile, lockFilename);
    strcpy(keyRingPath, "");
  }
  
  lockBirthDate = nfslock(keyRingPath, (char*)lockFilename, 0, 0);

  if ( ( gpg_pid = spawn_job (gpgCmd, strArgs,
			      &gpg_in_fd, &out_fd, &err_fd) ) < 0 ) {
    ER_perror(FAC_PA, PA_CANTSPWN, "gpg");
    exit(1);
  }
  
  if (waitpid (gpg_pid, &status, 0) < 0)
    {
      ER_perror(FAC_PA, PA_REAP, "gpg process: %s", ERRSTRING);
      nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);
      exit(1);
    }

  nfsunlock(keyRingPath, (char*)lockFilename, 0, lockBirthDate);

  if (WIFEXITED(status) == 0)
    {
      ER_perror(FAC_PA, PA_CHST, "%d - %s", status, ERRSTRING);
      exit(1);
    } else {
      /* Child exited, checking return code */
      childRC = (status & 0xF00) >> 8;
      if (childRC == 1) {
	ER_perror(FAC_PA, PA_CHRC, "%d", childRC);
	exit(1);
      }
    }


  /* Parsing gpg output */
  /*   while (read(0, txt, 1000) != 0)
       fprintf(stderr, "child read %s\n", txt); */

  mystdin = fdopen(0, "r");
  iKO->rc = iKO_GENERALFAILURE;
  while (fgets (txt, LINE_LENGTH - 1, mystdin) != NULL)
    {
      /*          printf ( "GPG output : %s\n", txt );      */

      if ((keyStr = strstr(txt, "imported")) != NULL) {
	iKO->rc = iKO_OK;
      }

      if ((keyStr = strstr(txt, "CRC error")) != NULL) {
	iKO->rc = iKO_CRC_ERROR;
      }

      if ((keyStr = strstr(txt, "no valid OpenPGP")) != NULL) {
	iKO->rc = iKO_NO_OPENPGP_DATA;
      }

      if (((keyStr = strstr(txt, "unchanged")) != NULL) || 
	  ((keyStr = strstr(txt, "not changed")) != NULL)) {
	iKO->rc = iKO_UNCHANGED;
      }

      if ((keyStr = strstr(txt, "gpg: key ")) != NULL) {
	 keyStr += 9;
	 sscanf(keyStr, "%8X\n", &iKO->keyID); 
      } 
    }

  if (sd1[0] != 0)  close ( sd1[0] ); 

}

