/***************************************
  $Revision: 2.22 $

  mm - MIME Parser module. Functions to parse a mail message part,
  find if it is MIME-encapsulated, dispatch the part to the
  appropriate drivers (also included) and return tree nodes 
  with all MIME information.

  Status: COMPLETE, NOT REVUED, TESTED

  Design and implementation by: daniele@ripe.net

  ******************/ /******************
  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.
  ***************************************/

/* Pieces of this code stolen and/or adapted from mtest.c, 
 * part of the IMAP toolkit by Mark Crispin:
 */

/* Original version Copyright 1988 by The Leland Stanford Junior University
 * Copyright 1999 by the University of Washington
 *
 *  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 notices appear in all copies and that both the
 * above copyright notices and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington or The
 * Leland Stanford Junior University not be used in advertising or publicity
 * pertaining to distribution of the software without specific, written prior
 * permission.  This software is made available "as is", and
 * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
 * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
 * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
 * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY 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, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */



/**************************


"Every program attempts to expand until it can read mail.
 Those programs which cannot so expand are replaced by
 ones which can."
		(Jamie Zawinski)


**************************/



/* Standard headers */
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
/* #include <sys/param.h> */
#include <netdb.h>
#include <regex.h>
#include <unistd.h>



/* This is the local header */
#include "mm.h"

/* Other RIP headers */
#include "erroutines.h"


/* 
 * Globals to store shared data for tree nodes 
 * These variables come from EP 
 */

extern char EP_outputPrefix[FILENAME_LENGTH];
extern char EP_keyRing[FILENAME_LENGTH];
extern int EP_TreeHeight;
extern int  EP_Node_ID;

/* Global variables to be used in this module */
long debug = DEFAULT_DEBUG;

char *supported_MIME_types[MAXSUPPTYPES] = {
  "UNKNOWN/UNKNOWN", "TEXT/PLAIN", "APPLICATION/PGP", "MULTIPART/SIGNED", 
  "MULTIPART/MIXED", "MULTIPART/ALTERNATIVE", "MULTIPART/DIGEST",
  "MESSAGE/RFC822"
};

long pass = 0;


/* 
   FIXMEs:
   - Revise the whole debug system, debug messages etc. - right now
     an enormous and globally useless quantity of information is dumped.
*/


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



/*++++++++++
 *
 * MM_store(). Stores a file (or stdin) in another file,
 * "escaping" the lines starting with "From " by adding
 * a ">" sign. This is necessary because we need to deal
 * with files that are "unix mailboxes".
 *
 * This function puts a limit to the line size that a mail
 * message may have; officially, there is no limit to this size,
 * but we prefer to add this limit to avoid buffer overflow.
 * The line size limit is MAXBUFSIZE, defined in mm.h .
 *
 ++++++++++*/


int MM_store (char *source_file, char *destination_file, long custom_debug)
{


/* The regexp we will be looking for */
#define REGEXP "^From "


  char line[MAXBUFSIZE];
  FILE *ifp;
  FILE *ofp;
  FILE *actualfile; /* Actual "file" to be opened (can be stdin) */
  char *tmpstr;
  short using_file = 0;
  long numlines = 0;
  time_t ti = time (0);



  if (custom_debug)
    debug = custom_debug;

  /* Check if we need to parse a file or stdin.
   * We parse stdin if source_file is "-" . 
   */

  if (strcmp(source_file,"-"))
    {
      if ((ifp = fopen(source_file,"r")) != NULL)
	{
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "MM_store: input file %s",source_file);
	  actualfile = ifp;
	  using_file = 1;
	}
      else
	{
	  /* XXX Use perror to state reason? */
	  ER_perror(FAC_MM, MM_CANTOPEN, "%s for reading", source_file);
	  die;
	}
    }
  else
    {
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "MM_store: input from stdin");
      actualfile = stdin;
    }

  if ((ofp = fopen(destination_file,"w")) != NULL)
    {
      while ((tmpstr = fgets(line, MAXBUFSIZE, actualfile)) != NULL)
	{
	  numlines++;
	  if (strlen(line) >= MAXBUFSIZE - 1)
	    {
	      ER_perror(FAC_MM, MM_LINETOOLONG, "%ld",numlines);
	      die;
	    }
	  if (numlines == 1)
	    {
	      /* If the first line is not a "^From " line, put a fake one */
	      if (!do_regex_test(REGEXP,(char *)line))
		  fprintf (ofp,"From dbase@whois.ripe.net %s",ctime (&ti));
	      fputs (line,ofp);
	    }
	  else
	    {
	      if (do_regex_test(REGEXP,(char *)line)) fprintf (ofp,">");
	      fputs (line,ofp);
	    }
	}
      fclose(ofp);
      if (using_file) fclose(ifp);
      return(0);
    }
  else
    {
      /* XXX Use perror to state reason? */
      ER_perror(FAC_MM, MM_CANTOPEN, "%s for writing", destination_file);
      die;
    }

  /* Even though we should never get here... */
  return(0);

} /* MM_store() */



/**********
 *
 * MM_get_msg_headers(). Get the headers of a mail contained in a file.
 *
 **********/

int MM_get_msg_headers(
		   const char *mail_file,			/* Input mail file */
		   EP_Mail_Descr *mail_descr,		/* Structure containing the headers */
		   long mesgno,				/* msg number in the input file */
		   long custom_debug			/* debug level */
		   )
{
  MAILSTREAM *stream = NULL;		/* MAILSTREAM is defined in c-client */
  char tmp[MAILTMPLEN];			/* MAILTMPLEN is set in c-client */
  int retcode;			        /* return code of the subroutine */
  STRINGLIST *lines;			/* STRINGLIST is defined in c-client */
  STRINGLIST *cur;
  BODY *body;

#include "linkage.c"		/* c-client requires it to be included... */


  /* If the supplied debug level is not null, then the global debug level
   * takes that value 
   */
  if (custom_debug)
    debug = custom_debug;


  /* open mailbox and get the mail stream */
  sprintf (tmp, "%s", mail_file);
  stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);

  /* Process the stream */
  if (!stream)
    {
      ER_perror(FAC_MM, MM_INVMBX, "%s", mail_file);
      die;
    }
  else
    {
      ER_inf_va (FAC_MM, ASP_MM_GEN, "Getting message headers.");
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message status:");
      status (stream);			/* report message status */
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "End of message status.");
      
      /* Get the headers */

      lines = mail_newstringlist ();      
      cur = lines;
      
      /* Get information about the mentioned lines in the header */


      mail_descr->from = get_mail_hdr_field(stream, mesgno, cur, "From");

      mail_descr->subject = get_mail_hdr_field(stream, mesgno, cur, "Subject");

      mail_descr->date = get_mail_hdr_field(stream, mesgno, cur, "Date");
      
      mail_descr->message_id = get_mail_hdr_field(stream, mesgno, cur, "Message-Id");
      
      mail_descr->reply_to = get_mail_hdr_field(stream, mesgno, cur, "Reply-To");
      
      mail_descr->cc = get_mail_hdr_field(stream, mesgno, cur, "Cc");
      


      mail_descr->content_type = (Mail_Header_Field *)malloc(sizeof(Mail_Header_Field));
      /* This gets all the line (with encoding etc.) */
      /* mail_descr->content_type = get_mail_hdr_field(stream,mesgno,cur,"Content-Type"); */

      /* This only gets the content-type itself in canonized form: */
      mail_fetchstructure(stream,mesgno,&body);
      if (body)
	{
	  mail_descr->content_type->field = (char *)malloc(STR_M);
	  mail_descr->content_type->next = NULL;
	  sprintf(mail_descr->content_type->field,"%s",body_types[body->type]);
	  if (body->subtype)
	    sprintf(mail_descr->content_type->field+strlen(mail_descr->content_type->field),"/%s",body->subtype);
	  sprintf(mail_descr->content_type->field+strlen(mail_descr->content_type->field),"\n\n");
	}
      
      mail_free_stringlist (&lines);
      
      ER_inf_va (FAC_MM, ASP_MM_GEN, "Got message headers.");
      


      mail_close(stream);

      retcode = 0;

      
    }


  return(retcode);

} /* MM_get_msg_headers() */



/* 
 * MM_extract_mime(): extract MIME information
 * This function was inspired by display_body() in mtest.c,
 * in the IMAP distribution. It has been largely re-engineered
 * to support MIME, and be modular.
 * It now only acts as an initializer of the mail stream,
 * sending then the stream to be dispatched to the appropriate
 * MIME drivers.
 */




int MM_extract_mime (
		     const char *sourcefile,			/* Input file containing the mail */
		     char *pfx,				/* "prefix": this can be NULL at the
							 * first call of the function */
		     EP_mail_node *mailnode,		/* initialized node where to stock info */
		     long custom_debug			/* debug level */
		     )
{

  MAILSTREAM *stream = NULL;		/* MAILSTREAM is defined in c-client */
  BODY *body;				/* BODY is defined in c-client */
  char tmp[MAILTMPLEN];			/* MAILTMPLEN is set in c-client */
  int retcode = 0;			/* return code of the subroutine */
  long mesgno = 1;


#include "linkage.c"		/* c-client requires it to be included... */

  /* 
   * This (global) variable counts the number of times we pass through
   * MM_extract_mime().
   * It is useful in generating unique temporary files (see below).
   */

  pass++;
  ER_inf_va (FAC_MM, ASP_MM_GEN, "MM_extract_mime, pass %ld",pass);

  if (custom_debug)
    debug = custom_debug;

  /* ER_dbg_va (FAC_MM, ASP_MM_GEN, " EP_outputPrefix: %s",EP_outputPrefix);
     ER_dbg_va (FAC_MM, ASP_MM_GEN, " EP_keyRing: %s",EP_keyRing); */


  /* open file and get the mail stream from there*/

  sprintf (tmp, "%s", sourcefile);
  
  stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);

  /* Process the stream */
  if (!stream)
    {
      ER_perror(FAC_MM, MM_INVMBX, "%s", sourcefile);
      die;
    }
  else
    {
      if (debug >=2)
	{
	  ER_inf_va (FAC_MM, ASP_MM_GEN, "Getting message headers.");
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message status:");
	  status (stream);			/* report message status */
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "End of message status.");
	}
      if (debug >= 2) 
	ER_dbg_va (FAC_MM, ASP_MM_GEN, "Calling mail_fetchstructure...");
      mail_fetchstructure (stream,mesgno,&body);

      if (body)
	{
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "Got body, dispatching to drivers...");
	  dispatch_to_driver(stream, body, pfx, mailnode);
	}
      
    }

  mail_close(stream);

  return(retcode);

} /* MM_extract_mime() */



/*********************************************/

/***************************************
 *
 * End of API functions
 *
 ***************************************/



/* Internal functions */

t_MM_type is_supported_MIMEtype (BODY *body)
{

  char *mimetype_string;
  char tmpstr[STR_S];
  char *tmptype = tmpstr;
  int i;
  t_MM_type mtypecode = 0;

  
  /* mimetype_string is the MIME type of the message */
  mimetype_string = (char *)malloc(STR_S);
  sprintf (mimetype_string,"%s",body_types[body->type]);
  if (body->subtype)
    sprintf (mimetype_string + strlen(mimetype_string),"/%s",body->subtype);

  /* 
   * We cycle to compare the MIME type of the message
   * to each of the MIME types we support
   */
  i = 0;
  tmptype = supported_MIME_types[i];

  while ((i < MAXSUPPTYPES) && (tmptype))
    {
      if (!strcmp(tmptype,mimetype_string))
	{
	  mtypecode = i;
	  break;
	}
      tmptype = supported_MIME_types[++i];
    }

  free(mimetype_string);

  return(mtypecode);

} /* is_supported_MIMEtype() */



/****
 *
 * dispatch_to_driver()
 * This function dispatches a message to the proper driver
 * which will parse it, following the MIME type
 *
 ****/

void dispatch_to_driver(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode)
{

  t_MM_type is_supported;

  is_supported = is_supported_MIMEtype(body);
  /* We assign the given MIME Type to the node */
  mailnode->MIMEContentType = is_supported;


  ER_dbg_va (FAC_MM, ASP_MM_GEN, " mailnode->MIMEContentType: %s",supported_MIME_types[mailnode->MIMEContentType]);

  if (!strcmp(supported_MIME_types[is_supported],"TEXT/PLAIN"))
    {
      parse_text_plain(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"APPLICATION/PGP"))
    {
      parse_application_pgp(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/ALTERNATIVE"))
    {
      parse_multipart_alternative(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/MIXED"))
    {
      parse_multipart_mixed(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/SIGNED"))
    {
      parse_multipart_signed(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/DIGEST"))
    {
      parse_multipart_digest(stream, body, pfx, mailnode);
    }
  else if (!strcmp(supported_MIME_types[is_supported],"MESSAGE/RFC822"))
    {
      parse_message_rfc822(stream, body, pfx, mailnode);
    }
  else
    {
      /* It's not a supported MIMEtype... */
      parse_unknown_unknown(stream, body, pfx, mailnode);
    }

} /* dispatch_to_driver() */


void parse_text_plain(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode)
{

  char tmp[MAILTMPLEN];
  char *mailtext;

  
  if (debug >= 2)
    ER_dbg_va (FAC_MM, ASP_MM_GEN, " Lines: %lu",body->size.lines);

  if (pfx == NULL)			/* If top level, is not inside a multipart */
    {
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file);
      /* The filename of the root node has to be redefined to the processed file */
      remove(mailnode->file);
      free(mailnode->file);
      mailnode->file = (char *)malloc(FILENAME_LENGTH);
      sprintf(mailnode->file,"%s%d",EP_outputPrefix,mailnode->nodeID);
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file);
    }
  else

  ER_dbg_va (FAC_MM, ASP_MM_GEN, " mailnode->file: %s",mailnode->file);

  /* Get the plain text contents of the message */
  mailtext = tmp;
  mailtext = mail_fetchtext(stream, 1);

  if (debug >= 2)
    {
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message contents:");
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "\n\n%s\n",mailtext); 
    }
      

  /* Place the results in the file pointed by the node*/
  write_file(mailnode->file,mailtext,strlen(mailtext));

  PA_ParseMessage(mailnode);

  /* if (debug) printf ("mailnode->nodeID: %d\n",mailnode->nodeID); */
  /* if (debug) printf ("mailnode->MIMEContentType: %d\n",mailnode->MIMEContentType); */

}

void parse_message_rfc822(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode)
{

  /* The idea here is to strip the message/rfc822 part from its mail headers,
   * and store it in a file to resend to MM_extract_mime().
   */

  char tmp[MAILTMPLEN];
  char *mailtext;
  char *content;
  char tmpfile[FILENAMELEN];
  time_t ti = time (0);
  
  
  if (pfx == NULL)			/* If top level, is not inside a multipart */
    {
      /* pfx = (char *)malloc(STR_L);
      pfx = "";		*/	/* Dummy prefix */
      /* The filename of the root node has to be redefined to the processed file */
      mailnode->file = (char *)malloc(FILENAME_LENGTH);
      sprintf(mailnode->file,"%s%d",EP_outputPrefix,mailnode->nodeID);
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file);
    }

  /* Get the plain text contents of the message */
  mailtext = tmp;
  mailtext = mail_fetchtext(stream, 1);


  /* This buffer has to be dumped in a file from where it will be read by MM_extract_mime():
   * another stream will be opened, so the format of the file must be correct.
   * The LINELENGTH is to take the first 2 lines into account.
   */

  content = (char *)malloc(2*LINELENGTH + strlen(mailtext) + 2);
  sprintf (content,"From dbase@whois.ripe.net %s",ctime (&ti));
  sprintf (content+strlen(content), "%s\n", mailtext);
     

  /* Generation of a temporary file:
   * The file must be unique inside the process. If we rewrite
   * on the same tmp file, which is used as a mailbox by c-client,
   * the c-client library has problems because it sees the mailbox changes
   * (I had problems with multipart/digest and message/rfc822).
   * "pass" is a global variable which increases every time we pass
   * through MM_extract_mime(): it should hence be unique each time
   * we call a driver.
   */
  sprintf (tmpfile,"%s.tmp.%ld",mailnode->file,pass);
  write_file(tmpfile,content,strlen(content));

  MM_extract_mime(tmpfile, pfx, mailnode, debug);

  /* Clean up... */
  free(content);
  remove(tmpfile);

}

void parse_multipart_alternative (MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode)
{

  char tmppfx[MAILTMPLEN];
  char childpfx[MAILTMPLEN];
  char tmppart[MAILTMPLEN];
  /*   char *s = tmppfx; */
  EP_mail_node *newnode;
  EP_mail_node *parsednode;
  EP_mail_node *nextnode;
  long i;
  PART *part;
  char *result;
  char *content;
  unsigned long length;
  time_t ti = time (0);
  char tmpfile[FILENAMELEN];
  char nodefile[FILENAMELEN];
  

  if (debug >= 2)
    ER_dbg_va (FAC_MM, ASP_MM_GEN, "Bytes: %lu",body->size.bytes);

  /* if not first time, extend prefix */
  if (pfx == NULL) 
    {
      tmppfx[0] = '\0';
      pfx = tmppfx;
    }


  /* Initialize the first node: it is an inner node */

  /* The tree height increases */
  EP_TreeHeight++;

  /* The number of nodes increases */
  EP_Node_ID++;

  sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID);
  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "inner-nodefile: %s",nodefile);

  newnode = EP_InitializeNode(nodefile, EP_Node_ID);
  mailnode->inner = newnode;

  for (i = 1,part = body->nested.part; part; part = part->next)
    {
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "i: %ld, pfx: %s",i,pfx);
      if (debug >= 3) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MYDEBUG: pfx=%s, tmppfx=%s,",pfx,tmppfx);
      sprintf (tmppart,"%ld",i);
      result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0);
      if (debug >= 3)
	{
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu",body->size.bytes);
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu",(&part->body)->size.bytes);
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu",length);
	}
      

      /* This buffer has to be dumped in a file from where it will be read by MM_extract_mime():
       * another stream will be opened, so the format of the file must be correct.
       * The LINELENGTH is to take the first 2 lines into account 
       */
      content = (char *)malloc(2*LINELENGTH + length + (&part->body)->size.bytes + 2);
      sprintf (content,"From dbase@whois.ripe.net %sMIME-Version: 1.0\n",ctime (&ti));
      /* snprintf (content+strlen(content), (size_t)(length + (&part->body)->size.bytes) + 2, "%s\n", result); */
      g_snprintf ((gchar *)(content+strlen(content)), (gulong)(length + (&part->body)->size.bytes) + 2, "%s\n", result);
      
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content);

      /* Generation of a temporary file:
       * The file must be unique inside the process. If we rewrite
       * on the same tmp file, which is used as a mailbox by c-client,
       * the c-client library has problems because it sees it changes
       * (I had problems with multipart/digest and message/rfc822).
       * "pass" is a global variable which increases every time we pass
       * through MM_extract_mime(): it should hence be unique each time
       * we call a driver.
       */
      sprintf (tmpfile,"%s.tmp.%ld",newnode->file,pass);
      write_file(tmpfile,content,strlen(content));

      /* This is needed to extend the prefix */
      sprintf (childpfx,"%s%ld.",pfx,i);
      if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "childpfx: %s",childpfx);
      MM_extract_mime(tmpfile, childpfx, newnode, debug);

      /* Clean up... */
      free(content);
      remove(tmpfile);

      /* Initialize the next node (if it exists) */

      if (part->next != NULL)
	{
	  EP_Node_ID++;
	  sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID);
	  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "next-nodefile: %s",nodefile);
	  nextnode = EP_InitializeNode(nodefile, EP_Node_ID);
	  parsednode = newnode;
	  newnode = nextnode;
	  parsednode->next = newnode;
	}

      i++;
      
    }
  
} /* parse_multipart_alternative() */


void parse_multipart_signed (MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode)
{

  char tmppfx[MAILTMPLEN];
  char tmppart[MAILTMPLEN];
  EP_mail_node *newnode;
  PART *part;
  char *result;
  char *content;
  unsigned long length;
  char tmpfile[FILENAMELEN];
  char nodefile[FILENAMELEN];
  struct VerifySignObject vSO;
  /*  int retcode; */

  if (debug >= 2)
    ER_dbg_va (FAC_MM, ASP_MM_GEN, "Bytes: %lu",body->size.bytes);


  /* if not first time, extend prefix */
  if (pfx == NULL) 
    {
      tmppfx[0] = '\0';
      pfx = tmppfx;
    }


  /* Initialize the inner node */

  /* The tree height increases */
  EP_TreeHeight++;

  /* The number of nodes increases */
  EP_Node_ID++;

  sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID);
  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "inner-nodefile: %s",nodefile);

  newnode = EP_InitializeNode(nodefile, EP_Node_ID);
  mailnode->inner = newnode;

  /* We give the same content-type to the child so as not to leave the default
     value (-1) */
  newnode->MIMEContentType = mailnode->MIMEContentType;

  /* We must get the two parts of the message. The signed part
   * and the signature. There can't be more than two parts
   * (see RFC2015).
   */

  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "pfx: %s",pfx);

  /* Signed part: it is the first part of the message. */

  part = body->nested.part;

  sprintf (tmppart,"%s1",tmppfx);
  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "tmppart: %s",tmppart);

  result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0);
  if (debug >= 3)
    {
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu",body->size.bytes);
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu",(&part->body)->size.bytes);
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu",length);
    }

  /* The signed part must be dumped in a file together with the MIME headers */

  content = (char *)malloc(length + (&part->body)->size.bytes + 2);
  snprintf (content,(size_t)(length + (&part->body)->size.bytes) + 2, "%s\n", result);

  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content);

  if (debug) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MSG file: %s",newnode->file);
  write_file(newnode->file,content,strlen(content));


  free(content);

  /* Signature */

  part = part->next;
  sprintf (tmppart,"%s2",tmppfx);
  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "tmppart: %s",tmppart);
  
  result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0);
  if (debug >= 2)
    {
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu\n",body->size.bytes);
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu\n",(&part->body)->size.bytes);
      ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu\n",length);
    }

  /* The signature must be dumped _without_ MIME headers instead! 
   * Check where is the "length" variable... 
   */

  content = (char *)malloc((&part->body)->size.bytes + 2);

  snprintf (content,(size_t)((&part->body)->size.bytes) + 2, "%s\n", result + length);

  if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content);

  sprintf (tmpfile,"%s.sig",newnode->file);
  if (debug) ER_dbg_va (FAC_MM, ASP_MM_GEN, "SIG file: %s",tmpfile);
  write_file(tmpfile,content,strlen(content));

  /* Calling the verification procedure */

  strcpy(vSO.iDocSigFilename, newnode->file);
  strcpy(vSO.iSigFilename, tmpfile);
  strcpy(vSO.keyRing, EP_keyRing);
  
  PA_VerifySignature(&vSO);

  newnode->isValidPGPSignature = vSO.isValid;
  newnode->keyID= vSO.keyID;

  EP_MIMEParse(newnode);

  free(content);
  remove(tmpfile);


} /* parse_multipart_signed */



/* MM status report
 * Accepts: MAIL stream
 */

void status (MAILSTREAM *stream)
{
  long i;
  char date[MAILTMPLEN];
  rfc822_date (date);
  ER_dbg_va (FAC_MM, ASP_MM_GEN, "%s",date);
  if (stream) 
    {
      if (stream->mailbox)
	{
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, " %s mailbox: %s",
		     stream->dtb->name,stream->mailbox);
	  ER_dbg_va (FAC_MM, ASP_MM_GEN, " %lu messages, %lu recent",
		     stream->nmsgs,stream->recent);
	}
      else  ER_dbg_va (FAC_MM, ASP_MM_GEN, "% No mailbox is open on this stream");
      if (stream->user_flags[0]) 
	{
	   ER_dbg_va (FAC_MM, ASP_MM_GEN, "Keywords: %s",stream->user_flags[0]);
	  for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
	   ER_dbg_va (FAC_MM, ASP_MM_GEN,", %s",stream->user_flags[i]);
	  /* puts (""); */
	}
    }
} /* status() */


Mail_Header_Field *get_mail_hdr_field (MAILSTREAM *stream, 
				       long mesgno, 
				       STRINGLIST *cur, 
				       const char *hdr_title)
{

  char *tmphdr;
  char tmpline[MAXBUFSIZE];
  int i, j, c, tmpsize, titlesize;
  Mail_Header_Field *hdr_field;
  Mail_Header_Field *mhfp;
  Mail_Header_Field *newmhfp;

  mhfp = hdr_field = newmhfp = NULL;

  tmphdr = get_header_line(stream,mesgno,cur,hdr_title);

  tmpsize = strlen(tmphdr);

  /* Length of the header title plus ": "*/
  titlesize = strlen(hdr_title) + 2;

  j = 0;

  /* Get one line at a time, and put the header lines in the Mail Header Field */

  for (i = 0; i < tmpsize; i++)
    {
      c = tmphdr[i];
      if (c == 10) /* EOL */
	{
	  if ((j > 1) || ((mhfp == NULL) && (i == tmpsize - 1))) /* j>1 and not j>0 because "\r" is always read;
								  * The second option is needed for
								  * the empty headers */
	    {
	      newmhfp = (Mail_Header_Field *)malloc(sizeof(Mail_Header_Field));
	      newmhfp->next = NULL;
	      newmhfp->field = (char *)malloc(j + 2);
	      if (j > 1)
		/* We do not copy here the header title */
		sprintf (newmhfp->field,"%s\n",tmpline + titlesize);
	      else
		sprintf (newmhfp->field,"\n");


	      if (mhfp == NULL)
		{
		  mhfp = newmhfp;
		  hdr_field = newmhfp;
		}
	      else
		{
		  mhfp->next = newmhfp;
		  mhfp = newmhfp;
		}
	    }
	  j = 0;	  
	}
      else
	{
	  sprintf (tmpline + j++,"%c", c);
	}

    }

  free(tmphdr);

  return (hdr_field);

} /* get_mail_hdr_field() */



char *get_header_line (MAILSTREAM *stream, long mesgno, STRINGLIST *cur, const char *hdr_title)
{

  unsigned long offset;
  size_t tmplength;
  char *curtmp;
  char *hdr_attr;
  long a,b;


  /* We need to insert the header title into a STRINGLIST structure, as
   * this is the type that must be supplied to mail_fetchheader_full.
   */

  cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) 
				     cpystr (hdr_title)));
  
  /* If we don't want to return the header title, but only the contents,
   * this offset allows us to strip the header title. The "magic number" 2
   * is the string ": " of the header.
   * This method is uneffective for multiple headers (ex. Cc, Reply-To, etc.).
   */
  
  offset = cur->text.size + 2;
  
  /* Get the header line, if it exists */
  
  curtmp = mail_fetchheader_full (stream,mesgno,cur,NIL,NIL);

  tmplength = strlen(curtmp);
  hdr_attr = (char *)malloc(tmplength + 4);
  
  /* cur contains the header title string, like "From:", "Subject:" etc.
   * tmplength is the length of the corresponding header line extracted
   * from the message. If a real line is returned, the header title
   * ("From:", "Subject:" etc.) will be contained within, hence
   * tmplength >= cur->text.size . This means that if
   * (cur->text.size > tmplength), no such header is present in the mail:
   * we must return an (almost) empty string.
   */
  
  a = (long)tmplength;
  b = (long)cur->text.size;
  if (a > b)
    {
      /* If we want to strip the header */
      /*sprintf (hdr_attr,"%s",curtmp + offset); */
      sprintf (hdr_attr,"%s",curtmp);
      /* printf ("%s",hdr_attr); */
    }
  else
    {
      sprintf (hdr_attr,"\n\n");
    }
  
  return (hdr_attr);
} /* get_header_line() */




/* Subroutine for writing in a file */

void write_file (char *filename, char *text, size_t text_size)
{

  FILE *fd;
  size_t i;

  /* printf ("%s\n",filename); */
  
  if ((fd = fopen(filename,"w")) != NULL)
    {
      for (i = 0; i < text_size; i++)
	if (text[i] != 13) 
	  fprintf (fd, "%c",text[i]);
      fclose(fd);
    }
  else
    {
      ER_perror(FAC_MM, MM_CANTOPEN, "%s for writing\n",filename);
      die;
    }
  
} /* write_file() */


void read_file (const char *filename)
{

  FILE *fd;
  int c;

  if ((fd = fopen (filename,"r")) != NULL)
    {
      while ((c = getc(fd)) != EOF)
	putc (c, stdout);
      fclose (fd);
    }
  else
    {
      ER_perror(FAC_MM, MM_CANTOPEN, "%s for reading\n",filename);
      die;
    }

} /* read_file() */


void put_in_file (char *fileprefix, char *extension, char *text, size_t text_size)
{

  char filename[FILENAMELEN];


  /* Write in a file */
  
  sprintf (filename,"%s-%s",fileprefix,extension);
  /* printf ("%s\n",filename); */
  
  write_file(filename,text,text_size);
  
}/* put_in_file() */


/* Stolen from which_keytypes.c and converted to use regex.h instead of libgen.h */


int do_regex_test (const char *pattern, char *string)
{

  int match = 0;

  /* These are not used, since REG_NOSUB is specified in regcomp() */
  size_t nmatch = 0;
  regmatch_t pmatch[1];

  regex_t *re;

  re = (regex_t *)malloc(STR_XL);

  regcomp(re, pattern, REG_NOSUB || REG_NEWLINE);
  if (regexec(re, string, nmatch, pmatch, 0))
    match = 0;
  else
    match = 1;

  regfree(re);

  /* Caution! regfree() does not do this job... */
  free(re);

  return(match);

} /* do_regex_test() */


/* Interfaces to c-client.
 * They must be here for the code to be compiled,
 * but most can stay empty.
 */

void mm_searched (MAILSTREAM *stream,unsigned long number)
{
}


void mm_exists (MAILSTREAM *stream,unsigned long number)
{
}


void mm_expunged (MAILSTREAM *stream,unsigned long number)
{
}


void mm_flags (MAILSTREAM *stream,unsigned long number)
{
}

void mm_notify (MAILSTREAM *stream,char *string,long errflg)
{
}

void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
{
}

void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
{
}

void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
{
}

void mm_log (char *string,long errflg)
{
  switch ((short) errflg) {
  case NIL:
    ER_dbg_va (FAC_MM, ASP_MM_GEN, "[%s]",string);
    break;
  case PARSE:
  case WARN:
    ER_perror (FAC_MM, MM_WARNCCL, "%%%s",string);
    break;
  case ERROR:
    ER_perror (FAC_MM, MM_ERRCCL, "%s",string);
    break;
  }
}

void mm_dlog (char *string)
{
  puts (string);
}

void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
{
}

void mm_critical (MAILSTREAM *stream)
{
}

void mm_nocritical (MAILSTREAM *stream)
{
}

long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
{
#if UNIXLIKE
  kill (getpid (),SIGSTOP);
#else
  abort ();
#endif
  return NIL;
}

void mm_fatal (char *string)
{
  ER_perror(FAC_MM, MM_FATCCL, "%s\n",string);
  die;
}

