/***************************************
  $Revision: 1.27 $

  Email Parser module (ep) - wrapping functions to parse email,
  calling MM and PA.

  Status: NOT REVUED, TESTED

  ******************/ /******************
  Filename            : mail_parser.c
  Authors             : Filippo Portera, Daniele Arena
  OSs Tested          : Solaris 7
  ******************/ /******************
  Copyright (c) 2000                              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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/param.h>

#include "mm.h"
#include "gpg.h"
#include "mail_parser.h"

/* Parse the mail message stored in inputFile and develop it
   in distinct text files, writing them on the outputPath
   directory and using the variable keyRing to read public
   keys needed for the verification process.   
   
   The common use of this parse should look like this:
   
   p = EP_ParseMessage("mail.001", "/tmp", "~/.gnupg/pubring.gpg");

   < parse the tree: p->tree >

   EP_TreeCleanUp(p);
 
*/

/* Globals to store shared data for tree nodes */

char EP_outputPrefix[FILENAME_LENGTH];
char EP_keyRing[FILENAME_LENGTH];
char EP_gpgcmd[FILENAME_LENGTH];
int  EP_TreeHeight;
int  EP_Node_ID;
int  EP_Debug;

const char *vS_strRC[] = { "IS_VALID",
			   "IS_NOT_PGP",
			   "KO",
			   "CRC_ERROR",
			   "NO_PUBLIC_KEY",
			   "NO_OPENPGP_DATA",
			   "NO_IN_FILES",
			   "NO_OUT_FILES",
			   "TO_BE_PGPVERIFIED",
			   "UNABLE_TO_WRITE_FILE",
			   "UNMATCHED_PGP_DELIMITERS"
                         };

#define EP_TREEMAXHEIGHT 10;

EP_Mail_DescrPtr InitializeMailDescr( const char *inputFile ) {

  EP_Mail_DescrPtr ptr;
  /*  EPNodePtr rootNode; */
  int retcode;
  long debug = 0;

  ptr = UT_malloc(sizeof(EP_Mail_Descr));

  ptr->from = ptr->subject = ptr->date = 
  ptr->message_id = ptr->reply_to = ptr->cc = 
  ptr->content_type = NULL ;


  /* Obtain headers */
  retcode = MM_get_headers(inputFile, ptr, debug);

  ptr->tree = EP_InitializeRootNode(inputFile);
  
  return ptr;
}

/* ------------------------------------------------- */

EP_Mail_DescrPtr EP_ParseMail(const char *inputFile,
			      const char *outputPath,
			      const char *keyRing,
			      const char *gpgcmd) {
  EP_Mail_DescrPtr ptr;
  char hostname[MAXHOSTNAMELEN];
  int retcode;
  long debug = 0;
  char mail_file[FILENAMELEN];

  EP_Debug = debug;

  gethostname(hostname, MAXHOSTNAMELEN);
  sprintf(EP_outputPrefix, "%s/EPMtmp.%s.%d.", outputPath,
	  hostname, getpid());
  strcpy(EP_keyRing, keyRing);
  strcpy(EP_gpgcmd, gpgcmd);

  sprintf (mail_file,"%sunprocessed", EP_outputPrefix);      /* the file where the mail message will be stored */

  /* if ((retcode = MM_store((char*)inputFile,mail_file, debug)) != 0)
    exit (retcode); */

  MM_store((char*)inputFile,mail_file, debug);
  
  ptr = InitializeMailDescr(mail_file);
  /* Invoke the MIME parser */
  retcode = MM_extract_mime(mail_file, NULL, ptr->tree, debug);

  return ptr;
}

/* ------------------------------------------------- */

EPNodePtr EP_ParseText(const char *inputFile,
		       const char *outputPath,
		       const char *keyRing,
		       const char *gpgcmd) {
  EPNodePtr ptr;
  char hostname[MAXHOSTNAMELEN];

  EP_Debug = 0;

  gethostname(hostname, MAXHOSTNAMELEN);
  sprintf(EP_outputPrefix, "%s/EPTtmp.%s.%d.", outputPath,
	  hostname, getpid());

  strcpy(EP_keyRing, keyRing);
  strcpy(EP_gpgcmd, gpgcmd);

  ptr = EP_InitializeRootNode(inputFile);

  return PA_ParseMessage(ptr);
}


/* ------------------------------------------------- */

EPNodePtr EP_MIMEParse(const EPNodePtr p)
{
  char mail_file[FILENAMELEN];
  int retcode;
  FILE * fin;
  char *strptr;
  int found = 0, headers_end = 0;
  char txt[MAX_LINE_BUF];

  sprintf (mail_file,"%s%d.unprocessed", EP_outputPrefix, p->nodeID);      /* the file where the mail message will be stored */
  
  /* Quest for a mail header:
     look for a mail header of type (content-type || mime version).
  */

  if ((fin = fopen(p->file, "r")) != NULL) { 
    while ( !headers_end && !found && 
	    (strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL) {
      if ( do_regex_test("^Content-Type:", txt) || 
	   do_regex_test("^MIME-Version:", txt)) {
	found = 1;
	fclose(fin);
	
	/* if ((retcode = MM_store((char*)p->file,mail_file, EP_Debug)) != 0) {
	   fprintf(stderr, "Error on MM_Store: %d\n", retcode );
	   } */

	MM_store((char*)p->file,mail_file, EP_Debug);

 	/* Invoke the MIME parser */
	retcode = MM_extract_mime(mail_file, NULL, p, EP_Debug);
      } else
	if ( do_regex_test("^ *\n", txt) ) 
	  headers_end = 1; 
    }

    if (!found) {
      fclose(fin);
      PA_ParseMessage(p);
    }

  } else {
    p->isValidPGPSignature = vS_NO_IN_FILES;
  }

  return p;
}

/* ------------------------------------------------- */

EPNodePtr EP_InitializeRootNode( const char *inputFile ) {
  EPNodePtr rootNode;

  EP_TreeHeight = EP_Node_ID = 0;

  rootNode = UT_malloc(sizeof(struct EPNode));

  rootNode->nodeID = 0;
  rootNode->isValidPGPSignature = vS_IS_NOT_PGP;
  rootNode->keyID = 0;
  rootNode->MIMEContentType = -1;
  rootNode->strMIMEContentType = NULL;
  rootNode->file = strdup(inputFile);
  rootNode->inner = NULL;
  rootNode->next = NULL;

  return rootNode; 
}

/* ------------------------------------------------- */

EPNodePtr EP_InitializeNode( const char *inputFile, const int nodeID ) {
  EPNodePtr node;

  node = UT_malloc(sizeof(struct EPNode));

  node->nodeID = nodeID;
  node->isValidPGPSignature = vS_IS_NOT_PGP;
  node->keyID = 0;
  node->MIMEContentType = -1;
  node->strMIMEContentType = NULL;
  node->file = strdup(inputFile);
  node->inner = NULL;
  node->next = NULL;

  return node; 
}

/* ------------------------------------------------- */

EPNodePtr EP_DefineNewNode( const int nodeID,
			    const short isValidPGPSignature,
			    const t_MM_type MIMEContentType,
			    const char *strMIMEContentType,
			    const u32 keyID) {
  EPNodePtr node;

  node = (EPNodePtr) UT_malloc(sizeof(EP_mail_node));

  /*  printf("node: %d, %p\n", nodeID, node); */

  node->nodeID = nodeID;
  node->isValidPGPSignature = isValidPGPSignature;
  node->keyID = keyID;
  node->MIMEContentType = MIMEContentType;
  node->strMIMEContentType = (strMIMEContentType == NULL ? NULL : 
			      strdup(strMIMEContentType) );
  node->inner = NULL;
  node->next = NULL;
  EP_BuildFilename(node);

  return node; 
}

/* ------------------------------------------------- */
/* Deallocate parsing tree and remove files */

void EP_TreeCleanUp(const EPNodePtr ptr) {

  if (ptr->file != NULL) {
    unlink(ptr->file);
    /*    printf("node: %d, %p\n", ptr->nodeID, ptr); */
    free(ptr->file);
  }
  if (ptr->strMIMEContentType != NULL) {
    free(ptr->strMIMEContentType);
  }

  if (ptr->inner != NULL) EP_TreeCleanUp(ptr->inner);
  if (ptr->next != NULL) EP_TreeCleanUp(ptr->next);

  free(ptr);
}

/* ------------------------------------------------- */
void MailHeaderFieldCleanUp(Mail_Header_FieldPtr p) {
  Mail_Header_FieldPtr ptmp = p, prev; 

  while (ptmp != NULL) {
    prev = ptmp;
    ptmp = ptmp->next;
    if (prev->field != NULL)
      free(prev->field);
    free(prev);
  }
}


/* ------------------------------------------------- */

/* Deallocate parsing tree and remove files */

void EP_MailDescrCleanUp(const EP_Mail_DescrPtr ptr) {

  if (ptr != NULL) {

    MailHeaderFieldCleanUp(ptr->from);
    MailHeaderFieldCleanUp(ptr->subject);
    MailHeaderFieldCleanUp(ptr->date);
    MailHeaderFieldCleanUp(ptr->message_id);
    MailHeaderFieldCleanUp(ptr->reply_to);
    MailHeaderFieldCleanUp(ptr->cc);
    MailHeaderFieldCleanUp(ptr->content_type);

    EP_TreeCleanUp(ptr->tree);
    free(ptr);
  }
}

/* ------------------------------------------------- */
/* Build a node filename */

void EP_BuildFilename(const EPNodePtr ptr) {
  char file[FILENAME_LENGTH];

  sprintf(file, "%s%d", EP_outputPrefix, ptr->nodeID);
  ptr->file = strdup(file);
}

/* ------------------------------------------------- */

void EP_ShowTree(const EPNodePtr p) {
  if (p != NULL) {
    /* if (EP_HasContent(p)) { */
      printf("Node ID: %d\n", p->nodeID);
      printf("isValidPGPSignature: %s\n",  vS_strRC[p->isValidPGPSignature]);  
      printf("MIMEContentType: %d\n", p->MIMEContentType);
      printf("Key ID: %0X\n", p->keyID);
      printf("file: %s\n\n\n", p->file);
      /* } */
    if (p->inner != NULL)
      EP_ShowTree(p->inner);
    if (p->next != NULL)
      EP_ShowTree(p->next);
  }
}

/* ------------------------------------------------- */

EPTokenPtr EP_DefineNewToken( const t_MM_type MIMEContentType,
			      const char *file,
			      const EPTokenKeysPtr keysList ) {
  EPTokenPtr token;
  EPTokenKeysPtr head = NULL, p = keysList, pnew, prev = NULL;
  
  token = (EPTokenPtr) UT_malloc(sizeof(EPToken));
  token->file = (char*)file;
  token->MIMEContentType = MIMEContentType;


  /* generate head, and build the key list for this result node */
  if (p != NULL) {
    pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
    pnew->isValidPGPSignature = p->isValidPGPSignature;
    pnew->keyID = p->keyID;
    pnew->next = NULL;
    head = prev = pnew;
    p = p->next;
  }

  while (p != NULL) {
    pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
    pnew->isValidPGPSignature = p->isValidPGPSignature;
    pnew->keyID = p->keyID;
    pnew->next = NULL;
    prev->next = pnew;
    prev = pnew;
    p = p->next;
  }

  token->keys = head;
  token->next = token->prev = NULL;

  return token;   
}

/* ------------------------------------------------- */

EPTokenKeysPtr AddKeyInfo( EPTokenKeysPtr keysList, const EPNodePtr p ){
  EPTokenKeysPtr ptk;

  ptk = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
  ptk->isValidPGPSignature = p->isValidPGPSignature;
  ptk->keyID = p->keyID;
  ptk->next = NULL;
  if (keysList == NULL)
    return ptk;
  else {
    ptk->next = keysList;
    return ptk;
  }
}

/* ------------------------------------------------- */

EPTokenKeysPtr RemoveKeyInfo( const EPTokenKeysPtr keysHead ) {
  EPTokenKeysPtr tmp = keysHead->next;

  free(keysHead);
  return tmp;
}
/* ------------------------------------------------- */

EPTokenPtr EP_GetTokens(const EPNodePtr p, const EPTokenPtr head, 
			EPTokenKeysPtr keysList) {
  EPTokenPtr pt, ptmp = head;
  EPTokenKeysPtr kl = keysList;
  
  if (p != NULL) {
    if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
      kl = AddKeyInfo(kl, p);
    }
    if (EP_HasContent(p)) {
      pt = EP_DefineNewToken(p->MIMEContentType, p->file, kl);
      if (ptmp != NULL) {
	pt->next = ptmp;
	ptmp->prev = pt;
	ptmp = pt;
      } else 
	ptmp = pt;
    } else
      ptmp = EP_GetTokens(p->inner, ptmp, kl);

    if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
      kl = RemoveKeyInfo(kl);
    }

    ptmp = EP_GetTokens(p->next, ptmp, kl);
  }
  return ptmp;
}

/* ------------------------------------------------- */
void EP_PrintTokens(EPTokenPtr head) {
  EPTokenPtr p = head;
  EPTokenKeysPtr ptk;

  while (p != NULL) {
    printf("Token: %s, MIMEtype: %d\n", p->file, p->MIMEContentType);
    ptk = p->keys;
    while (ptk != NULL) {
      printf("     key: %0X, isValid: %s\n", 
	     ptk->keyID, vS_strRC[ptk->isValidPGPSignature]);
      ptk = ptk->next;
    }
    p = p->next;
  }
}

/* ------------------------------------------------- */

void EP_CleanTokens(const EPTokenPtr head) {
  EPTokenPtr prevp, p = head;
  EPTokenKeysPtr ptk, prevptk;

  while (p != NULL) {
    ptk = p->keys;
    while (ptk != NULL) {
      prevptk = ptk;
      ptk = ptk->next;
      free(prevptk);
    }
    prevp = p;
    p = p->next;
    free(prevp);
  }
}


