/***************************************
  $Revision: 1.7 $

  mm - MIME Parser module. Functions to parse a mail message,
  find if it is MIME-encapsulated, and return the parts of
  the message which are supported by the UP module.

  Status: NOT REVUED 

  Design and implementation by: Daniele Arena

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

/* Parts of this code stolen 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.
 *
 */



/* Standard headers */
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <libgen.h>	/* This is a Solaris (System V) library. Not standard. */


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


/* Comments about this module:

   - Still need to free() the allocated chunks. This is not strictly necessary,
     as this module will be called each time from anew and then will bail out,
     so all the memory will be freed anyway.
     But for the sake of cleanness, this needs to be done.
   - A good idea would be to use glib for allocations, linked lists etc.
     This still needs to be done.
   - Comments to be added.
   - Cleanup of internal functions.
   - printfs should be replaced with calls to ER module

   */



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

/* MM_decode. The main API function:
   it parses the file mail_file, at the message mesgno,
   and returns a structure pointing to files containing 
   all the different MIME parts, plus more information.
   It also returns some headers of the message.
*/

int MM_decode (
	       char *mail_file,			/* filename of the "mailbox" */
	       MM_header *mail_header,		/* Headers: to be returned */
	       MM_xmp_list *part_list,		/* List of MIME parts: to be returned */
	       long mesgno,			/* Message number in the mailbox */
	       long debug			/* debug level */
	       )
{

  MAILSTREAM *stream = NULL;		/* MAILSTREAM is defined in c-client */
  char tmp[MAILTMPLEN];			/* MAILTMPLEN is set in c-client */
  int mm_retcode;			/* return code of the subroutine */


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

  sprintf (tmp, "%s", mail_file);
  
  /* open mailbox and get the mail stream */
  stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);
  
  
  /* Process the stream */
  if (!stream)
    {
      printf ("Invalid mailbox: %s\n", mail_file);
      return (1);
    }
  else
    {

      if (debug)
	{
	  printf ("------------------ Message status:\n");
	  status (stream);			/* report message status */
	  printf ("------------------ End of message status\n");
	  if (debug >= 2) 
	    printf ("================== DEBUG: Calling mm function...\n");
	}

      /* run "user interface" */
      mm_retcode = mm (stream,mail_header,part_list,mesgno,debug);	

      return (mm_retcode);
    }

  /* We should never get here... */
  /* return(1); */

}


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


/* MM_store. Store stdin in a file. */

void MM_store (char *destination_file, long debug)
{


#define LINESIZE STR_S
#define REGEXP "^From "
#define FIRSTCHARS 10

  int c;
  FILE *fd;
  time_t ti = time (0);
  char line[LINESIZE];
  char *tmpstr;
  int linechars = 0;
  int i;
  short charcount = 0;
  char firstline[LINESIZE];

  if ((fd = fopen(destination_file,"w")) != NULL)
    {
      /* fprintf (fd,"From dbase@whois.ripe.net %s",ctime (&ti)); */
      
      /* This works. However, it can't be used since there is 
	 no line length limitation in e-mail messages... */
      /* while (tmpstr = fgets(line, LINESIZE, stdin))
	 {
	 if (perform_regex_test(REGEXP,tmpstr)) fprintf (fd,">");
	 fputs (line,fd);
	 } */
	
      /* A non-trivial file dump from stdin.
	 The problem here is that we need to store 
	 the beginning of each line to check if
	 the line starts with "From", in order to escape it with a ">".
	 The string-only method cannot be used, for mail messages don't have
	 a limit in line length 
	 (we cannot use "gets" for buffer overflow risks).
	 Thus we need to use a "mixed" method,
	 grabbing the first "LINESIZE" characters in a string to check with
	 regexp. This string is then dumped. All the characters not
	 at the beginning of the string are directly dumped with putc. */

      /* This is not a very generic algorithm... 
	 It is only fit when you are looking
	 for a match at the beginning of a line. 
	 BTW, the LINESIZE should be bigger
	 than the regexp you are trying to match... 
	 And: it only starts to work at the second line of the text... 
	 Basically, it's ugly but it fits our needs. */

      /* Reset string */
      for (i = 0; i < LINESIZE; i++)
	firstline[i] = 0;


      while ((c = getchar()) != EOF)
	{
	  /* This is done to write the file so that it can be 
	     interpreted by c-client as a mailbox in "unix" format:
	     the first line must start with "From " */

	  /* Get first characters to see if the first line is a "^From " line */
	  if (charcount < FIRSTCHARS)
	    {
	      firstline[charcount] = c;
	      charcount++;
	      continue;
	    }
	  if (charcount == FIRSTCHARS)
	    {
	      /* If the first line is not a "^From " line, put a fake one */
	      if (!perform_regex_test(REGEXP,firstline))
		  fprintf (fd,"From dbase@whois.ripe.net %s",ctime (&ti));
	      charcount++; /* otherwise it executes this block forever */
	      fprintf (fd,"%s",firstline); /* dump all the string anyway */
	    }


	  /* Work with the rest of the message */
	  if ((c == 10)	||			/* new line or */
	      (linechars >= LINESIZE))		/* defined string length passed */
	    {
	      /* If there is a string in the buffer, the string is full or we have
		 a new line. We have to:
		 - check for the regexp
		 - dump the string in the file 
		 - reset the string */
	      if (linechars)
		{
		  tmpstr = line;
		  if (perform_regex_test(REGEXP,tmpstr))	/* got regexp: */
		    fprintf (fd,">");				/* Escape the line */
		  fprintf (fd,"%s",line);			/* dump string anyway */

		  /* Reset string */
		  linechars = 0;
		  for (i = 0; i < LINESIZE; i++)
		    line[i] = 0;
		}
	      
	      /* If we are at a new line, then start to get the string */
	      if (c == 10)
		linechars = 1;
	      putc (c,fd);	/* Dump the character anyway */
	    }
	  else if (linechars)		/* We are getting the string */
	    {
	      sprintf (line+linechars-1,"%c",c);
	      linechars++;
	    }
	  else				/* Too far from the start of the line: */
	    putc (c,fd);		/* We just dump the character to the file */
	} 
      fclose(fd);
    }
  else
    printf ("Error: couldn't open file %s for writing\n",destination_file);
}


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

/* MM_cleanup. Cleans the files containing the MIME parts
   when they're not needed anymore.
   Todo: also clean memory. */

void MM_cleanup (MM_xmp_list *part_list, long debug)
{
  MM_xmp *partptr;

  partptr = part_list->head;

  while (partptr != NULL)
    {
      if (debug) printf ("Removing file %s...\n",partptr->file);
      remove(partptr->file);
      partptr = partptr->next;
    }

}


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



/* User interface */

int mm (MAILSTREAM *stream, MM_header *hdr, MM_xmp_list *part_list, long mesgno, long debug)
{

  char *section;
  char *result;
  char tmp[MAILTMPLEN];
  char strtmp[MAILTMPLEN];
  char *mailtext;
  unsigned long length;
  long flags;
  BODY *body;
  STRINGLIST *lines;
  STRINGLIST *cur;
  char fileprefix[FILENAMELEN];
  struct timeval *currenttime;
  pid_t proc_id;
  MM_b_section *secptr;
  MM_bs_list *section_list;
  MM_b_section *tmpsecptr;
  char *tmpsection;
  MM_xmp *newpart;
  int retcode = 0;


  /* Initialize the list of the body sections */

  section_list = (MM_bs_list *)malloc(sizeof(MM_bs_list));
  MM_bs_list_init (section_list);
  
  /* Create the filename prefix for the output files */
  
  currenttime = (struct timeval *)malloc(sizeof(struct timeval));
  if (!gettimeofday(currenttime,NIL))
    {
      if (proc_id = getpid())
	{
	  sprintf (fileprefix,"%s/%s.%ld-%d",TEMPDIR,GLOBALPREFIX,currenttime->tv_sec,(int)proc_id);
	}
      else printf ("ERROR: could not get Process ID\n");
    }
  else printf ("ERROR: Could not gettimeofday\n");


  if (mesgno && (mesgno <= stream->nmsgs)) 
    {

      /* Get the headers we need. */
      
      if (debug >= 2) printf ("================== DEBUG: my headers\n");
      
      
      lines = mail_newstringlist ();      
      cur = lines;
      
      /* Get information about the mentioned lines in the header */
      
      hdr->from = get_header_line(stream,mesgno,cur,"From");
      
      hdr->subject = get_header_line(stream,mesgno,cur,"Subject");
      
      hdr->date = get_header_line(stream,mesgno,cur,"Date");
      
      hdr->message_id = get_header_line(stream,mesgno,cur,"Message-ID");
      
      hdr->reply_to = get_header_line(stream,mesgno,cur,"Reply-To");
      
      hdr->cc = get_header_line(stream,mesgno,cur,"Cc");
      
      mail_free_stringlist (&lines);
      
      if (debug >= 2) printf ("================== DEBUG: After getting headers\n");
      
      
      
      if (debug) printf ("Message number: %lu. Total messages: %lu\n", mesgno, stream->nmsgs);
      
      
      /* Get structure of the message: body 
	 (and envelope, which is unused) */

      if (debug >= 2) 
	printf ("================== DEBUG: Calling mail_fetchstructure...\n");
      mail_fetchstructure (stream,mesgno,&body);


      if (debug >= 2) 
	printf ("================== DEBUG: Printing body information...\n");

      if (body) 
	{
	  /*printf ("Body encoding: %d (%s)\n", body->encoding, body_encodings[body->encoding]);*/

	  /* 
	   * Switch by supported body types.
	   * The supported body types are:
	   * - discrete:
	   * text/plain
	   * application/pgp
	   * application/pgp-signature (inside multipart/signed)
	   * - composite:
	   * multipart/mixed
	   * multipart/alternative
	   * multipart/signed
	   */
	  if (debug >= 2) printf ("================== DEBUG: Calling get_body_info...\n");
	  get_body_info (body,NIL,(long) 0, section_list, debug);
	  if (debug >= 2) printf ("================== DEBUG: After get_body_info...\n");
		      
	  secptr = section_list->head;

	  if (debug >= 3)
	    {
	      printf ("================== DEBUG 3: number: %s\n",secptr->number);
	      printf ("================== DEBUG 3: type: %s\n",secptr->type);
	    }



	  switch (body->type)
	    {

	    case TYPETEXT:
	      mailtext = tmp;
	      if ((body->subtype) && (!strcmp(body->subtype,"PLAIN")))
		{

		  /* Can this explode with huge messages? */
		  mailtext = mail_fetchtext(stream,mesgno);

		  if (debug >= 3)
		    {
		      printf ("Type text/plain\n");
		      printf ("Message contents:\n");
		      printf ("%s\n",mailtext); 
		    }

		  secptr->supported = 1;

		}
	      else
		{
		  sprintf (mailtext,"Unsupported content type: %s",
			  body_types[body->type]);
		  if (body->subtype) sprintf (mailtext+strlen(mailtext),"/%s\n",body->subtype);
		  /* printf ("%s",mailtext); */
		  secptr->supported = 0;
		}

		  /* Write in a file */
		  
		  put_in_file (fileprefix,"1",mailtext,strlen(mailtext));

	      break;

	    case TYPEAPPLICATION:

	      mailtext = tmp;
	      if ((body->subtype) && (!strcmp(body->subtype,"PGP")))
		{
		  mailtext = mail_fetchtext(stream,mesgno);

		  /* printf ("Type application/pgp\n");
		     printf ("Message contents:\n");
		     printf ("%s\n",mailtext); */

		  secptr->supported = 1;

		}
	      else
		{
		  sprintf (mailtext,"Unsupported content type: %s",
			  body_types[body->type]);
		  if (body->subtype) sprintf (mailtext+strlen(mailtext),"/%s\n",body->subtype);
		  /* printf ("%s",mailtext); */
		  secptr->supported = 0;
		}

	      /* Write in a file */
	      
	      put_in_file (fileprefix,"1",mailtext,strlen(mailtext));

	      break;

	    case TYPEMULTIPART:
	      if (body->subtype)
		{
		  if ((!strcmp(body->subtype,"MIXED")) || (!strcmp(body->subtype,"ALTERNATIVE")) || (!strcmp(body->subtype,"SIGNED")))
		    {
		      /* printf ("Supported content type: %s/%s\n",body_types[body->type],body->subtype); */
		      
		      
		      flags = 0;
		      if (debug) printf ("Sections:\n");
		      while (secptr != NULL)
			{
			  section = secptr->number;
			  if (debug) 
			    {
			      printf("++++++++++++++++++++++++++++++++++++++++++++\n");
			      printf ("%s\n",section);
			    }
			  /*printf ("%s\n",secptr->type);*/
			  
			  if ((!strcmp(secptr->type,"TEXT/PLAIN")) || (!strcmp(secptr->type,"APPLICATION/PGP-SIGNATURE")))
			    {
			      secptr->supported = 1;
			      result = mail_fetch_mime (stream, mesgno, section, &length, flags);
			      
			      
			      if (debug) 
				{
				  printf ("Supported content type: %s\n",secptr->type);
				  printf ("Length: %lu . Result: \n",length);
				}
			      
			      /* secptr->size: size of the contents of the body part.
				 length: size of the MIME header of the body part. */
			      
			      secptr->mime_headers = (char *)malloc(length);
			      
			      strncpy(secptr->mime_headers,result,(size_t)length);
			      
			      /* printf ("--MIME headers:\n%s\n--End of MIME headers\n",secptr->mime_headers); */
			      
			      secptr->contents = (char *)malloc(secptr->size);
			      
			      strncpy(secptr->contents,result + length,(size_t)secptr->size);

			      /* Write in a file */

			      put_in_file (fileprefix,section,secptr->contents,secptr->size);
			      
			    }
			  else
			    {
			      sprintf (strtmp,"Unsupported content type: %s\n",secptr->type);
			      secptr->supported = 0;
			      /* printf ("%s",strtmp); */
			      /* Write in a file */
			      put_in_file (fileprefix,section,strtmp,strlen(strtmp));
			    }
			  
			  
			  printf ("\n\n");
			  
			  
			  
			  secptr = secptr->next;
			}
		    }
		  else
		    {
		      sprintf (strtmp,"Unsupported content type: %s/%s\n",body_types[body->type],body->subtype);
		      secptr->supported = 0;
		      /* printf ("%s",strtmp); */
		      /* Write in a file */
		      put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
		      /* Problem here - the notice is only written in the first file.
			 It is right, for we only should have one file for multipart/unsupported.
			 But from get_body_info, section_list is composed of all the parts
			 anyway...
			 a solution is to reduce here section_list to only one member,
			 as follows. */
		      secptr->next = NULL;
		      section_list->size = 1;

		    }
		}
	      else
		{

		  /* In current c-client implementation, we _should_ never get here,
		     since the subtype "unknown" is added if no subtype is
		     specified. */

		  sprintf (strtmp,"Unknown multipart subtype\n");
		  secptr->supported = 0;
		  /* printf ("%s",strtmp); */
		  /* Write in a file */
		  put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
		  /* Same problem here as above: the notice is
		     only written in the first file. We reduce the list to
		     a single member. */
		  secptr->next = NULL;
		  section_list->size = 1;

		}

	      break;

	    default:
	      sprintf (strtmp,"Unsupported content type: %s",body_types[body->type]);
	      secptr->supported = 0;
	      if (body->subtype) sprintf (strtmp+strlen(strtmp),"/%s\n",body->subtype);
	      
	      /* printf ("%s",strtmp); */

	      /* Write in a file */
	      put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
	      break;
	    }


	  /* Copy the relevant information to the structure used
	     by the API, MM_xmp */

	  tmpsecptr = section_list->head;
	  
	  while (tmpsecptr != NULL)
	    {

	      newpart = (MM_xmp *)malloc(sizeof(MM_xmp));

	      /* printf("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); */
	      tmpsection = tmpsecptr->number;

	      newpart->number = (char *)malloc(strlen(tmpsection) + 1);
	      sprintf (newpart->number,"%s",tmpsection);
	      newpart->file = (char *)malloc(strlen(fileprefix) + strlen(tmpsection) + 2);
	      sprintf (newpart->file,"%s-%s",fileprefix,tmpsection);
	      newpart->type = (char *)malloc(strlen(tmpsecptr->type));
	      sprintf (newpart->type,"%s",tmpsecptr->type);
	      /* printf ("%s\n",newpart->number);
		 printf ("%s\n",newpart->file);
		 if (debug) printf ("Reading file %s...\n",newpart->file);
		 read_file(newpart->file); */

	      newpart->supported = tmpsecptr->supported;
	      /* printf("Supported: %hd\n",newpart->supported); */

	      MM_xmp_list_ins_last(part_list, newpart);
	      tmpsecptr = tmpsecptr->next;
	    }
	  

	  
	}
      else
	{
	  puts ("No body information available");
	  retcode = 1;
	}
      


    }
  else 
    {
      printf ("Wrong message number: %lu. The maximum number of messages is %lu.\n",mesgno,stream->nmsgs);
      retcode = 1;
    }
  
  return(retcode);
  
}


/* Internal functions */


/* MM get body information
 * Accepts: BODY structure pointer
 *          prefix string
 *          index
 *          section list pointer
 *          debug switch
 */

/* This function has been taken almost unchanged from mtest.c,
 * in the IMAP distribution. There, it is called display_body.
 */


void get_body_info (BODY *body,char *pfx,long i, MM_bs_list *section_list, long debug)
{

  char tmp[MAILTMPLEN];
  char sectno[MAILTMPLEN];
  char sectype[MAILTMPLEN];
  char *s = tmp;
  PARAMETER *par;
  PART *part;                  
  MM_b_section *newsection;


  if (body->type == TYPEMULTIPART) 
    {
      if (debug) printf ("++++multipart\n");
      /* if not first time, extend prefix */
      if (pfx) sprintf (tmp,"%s%ld.",pfx,++i);
      else tmp[0] = '\0';
      for (i = 0,part = body->nested.part; part; part = part->next)
	get_body_info (&part->body,tmp,i++, section_list, debug);
    }
  else 
    {                        /* non-multipart, output oneline descriptor */
      if (debug) printf ("++++nonmultipart\n");
      if (!pfx) pfx = "";         /* dummy prefix if top level */

      sprintf (s," %s%ld %s",pfx,++i,body_types[body->type]);

      newsection = (MM_b_section *)malloc(sizeof(MM_b_section));

      newsection->number = (char *) malloc (strlen(sectno)+1);
      sprintf (sectno,"%s%ld",pfx,i);
      sprintf (newsection->number,"%s",sectno);

      sprintf (sectype, "%s",body_types[body->type]);

      if (body->subtype) 
	{
	  sprintf (s += strlen (s),"/%s",body->subtype);
	  sprintf (sectype + strlen (sectype),"/%s",body->subtype);
	}

      newsection->type = (char *) malloc (strlen(sectype)+1);

      sprintf (newsection->type,"%s",sectype);     

      /* Insert an element at the end of the list */

      MM_bs_list_ins_last (section_list, newsection);


 
      if (body->description) sprintf (s += strlen (s)," (%s)",body->description);


      if ((par = body->parameter)) do
	sprintf (s += strlen (s),";%s=%s",par->attribute,par->value);
				 while ((par = par->next));
      if (body->id) sprintf (s += strlen (s),", id = %s",body->id);
      switch (body->type) {       /* bytes or lines depending upon body type */
      case TYPEMESSAGE:           /* encapsulated message */
      case TYPETEXT:              /* plain text */
	sprintf (s += strlen (s)," (%lu lines)",body->size.lines);
	newsection->size = body->size.bytes;
	sprintf (s += strlen (s),"\n   size: %lu",body->contents.text.size);
	break;
      default:
	sprintf (s += strlen (s)," (%lu bytes)",body->size.bytes);
	newsection->size = body->size.bytes;
	break;
      }
      if (debug) puts (tmp);                 /* output this line */

      /* We should never arrive here, since this body type is not supported.
	 This part should be deleted. */
      /* encapsulated message? */
      /* if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") &&
       * (body = body->nested.msg->body)) 
       * {
       * if (body->type == TYPEMULTIPART) get_body_info (body,pfx,i-1, section_list, debug);
       * else 
       * {                    /* build encapsulation prefix */
      /* sprintf (tmp,"%s%ld.",pfx,i);
       * get_body_info (body,tmp,(long) 0, section_list, debug); 
       * }
       * }
       */
    }

  return;

}


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

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



/*Initialize body_section list */

void MM_bs_list_init (MM_bs_list *section_list)
{

  section_list->size = 0;
  section_list->head = NULL;
  section_list->tail = NULL;
  /* return; */

}

/* Insert an element at the end of the body_section list */

void MM_bs_list_ins_last (MM_bs_list *section_list, MM_b_section *newsection)
{
  
  if (section_list->size == 0)
    {
      section_list->head = newsection;
      section_list->tail = newsection;
      section_list->size++;
    }
  else
    {
      section_list->tail->next = newsection;
      section_list->tail = newsection;
      section_list->size++;
    }

      newsection->next = NULL;

}


/*Initialize extracted_mimepart list */

void MM_xmp_list_init (MM_xmp_list *part_list)
{

  part_list->size = 0;
  part_list->head = NULL;
  part_list->tail = NULL;
  /* return; */

}


/* Insert an element at the end of the body_section list */

void MM_xmp_list_ins_last (MM_xmp_list *part_list, MM_xmp *newpart)
{
  
  if (part_list->size == 0)
    {
      part_list->head = newpart;
      part_list->tail = newpart;
      part_list->size++;
    }
  else
    {
      part_list->tail->next = newpart;
      part_list->tail = newpart;
      part_list->size++;
    }

      newpart->next = NULL;

}


char *get_header_line (MAILSTREAM *stream, long mesgno, STRINGLIST *cur, 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)));
  
  /* We don't want to return the header title, but only the contents.
   * This offset allows us to strip the header title.
   */
  
  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);
  
  /* 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)
    {
      sprintf (hdr_attr,"%s",curtmp + offset);
      /* printf ("%s",hdr_attr); */
    }
  else
    {
      sprintf (hdr_attr,"\n\n");
    }
  
  return (hdr_attr);
}


/* 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++)
	fprintf (fd, "%c",text[i]);
      fclose(fd);
    }
  else
    printf ("Error: could not open file %s\n",filename);
  
}


void read_file (char *filename)
{

  FILE *fd;
  int c;

  if ((fd = fopen (filename,"r")) != NULL)
    {
      while ((c = getc(fd)) != EOF)
	putc (c, stdout);
      fclose (fd);
    }
  else
    printf ("Error: could not open file %s\n",filename);

}


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);
  
}


/* Stolen from which_keytypes.h */
/* Actually, it only works with SysV libgen.h
 * It should be rendered POSIX-compliant, and use regexp.h
 */

static int perform_regex_test(const char *pattern, char *string) 
{
  int match;

  char *re;

  re = regcmp(pattern, (char*)0);
  if (regex(re, string) == NULL) {
    match = 0;
  }
  else {
    match = 1;
  }

  free(re); /* not a wrapper, because we have not allocated it */

  return match;
} /* perform_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:
    printf ("[%s]\n",string);
    break;
  case PARSE:
  case WARN:
    printf ("%%%s\n",string);
    break;
  case ERROR:
    printf ("?%s\n",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)
{
  printf ("?%s\n",string);
}

