#include "emil.h"
/*
 * int
 * parse_message(inbuf, message)
 *
 * Parse message and divide into a hierarchical structure of message parts.
 *
 */

int
parse_rfc822_message(struct message *m)
{
  struct data *inbuf;
  int linelen;
  int encoding;
  static int do_sibling = FALSE;

  /* Initialize inbuf */
  inbuf = (struct data *)m->sd;

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "+ Parsing body part %d at level %d and offset %lu.\n", 
	    inbuf->count,
	    m->level,
	    inbuf->offset);
#endif

  /* Exit on empty input */
  if (inbuf->size == 0 || (inbuf->end <= inbuf->offset))
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** Empty input (failed).\n");
#endif
      logger(LOG_ERR, "parse_message: Empty input");
      return(NOK);
    }

  /* Body starts here */
  inbuf->bodystart = inbuf->offset;
  inbuf->linestart = inbuf->loffset;
  

  /* Check for encoding */
  if (inbuf->encoding == 0)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "+   No encoding (7bit)\n");
#endif
      inbuf->encoding = E7BIT;
    }


  /* Loop through rest of the message */
  while ((linelen = getline(inbuf)) != 0)
    {
      if (inbuf->bodyend <= inbuf->offset &&
	  inbuf->lineend <= inbuf->loffset)
	/* Either bodyend not set or bodyend reached */
	{
	  
	  if ((encoding = boundary_check(m)) != 0 || do_sibling == TRUE)
	    {
	      /* If boundary check successful, create sibling.
	       */
	      struct data *sibbuf;
	      struct message *sibm;
	      
#ifdef DEBUG
	      if (edebug)
		fprintf(stderr, "+   Boundary check OK.\n");
#endif
	      do_sibling = FALSE;
	      /* Allocate siblings data sructure */
	      sibm = (struct message *)copy_mstruct(m, 0);
	      sibbuf = sibm->sd;
	      sibbuf->encoding = encoding;
	      sibbuf->end = inbuf->end;
	      sibbuf->lend = inbuf->lend;
	      sibm->bigsib = m->bigsib;
	      sibm->level = m->level;
	      sibm->parent = m->parent;

	      /* Run Sibling */
	      if (parse_message(sibm) == OK)
		{
#ifdef DEBUG
		  if (edebug)
		    fprintf(stderr, "Child ended at offset %lu\n", sibbuf->offset);
#endif
		  /* If sibling went successful set end of current 
		   * part and attach sibling to current.
		   */
		  if (m->sd->format != RFC822)
		    {
		      m->sibling = sibm;
		      if (m->parent)
			m->parent->children += 1;
		    }
		  inbuf->bodyend = inbuf->offset;
		  inbuf->lineend = inbuf->loffset;

		    
		  /* Successful return from current part */
		  return(OK);
		}
	      else
	      /* parse_message(sibm) failed, data is already rewound 
	       * and sibling remains unattached.
	       */
		{
		  inbuf->offset += linelen;
		  inbuf->loffset += 1;
		}
	    }
	  else
	    {
	      /* boundary_check(m) failed, continue with current */
	      if (inbuf->bodyend == 0)
		/* End not reached because boundary check failed;
		 * data processing may continue 
		 */
		{
		  /* Process data.
		   * If typed data trust types
		   * and perform boundary checks. If untyped raw rfc822
		   * data, uuencode or BinHex (who have an explicit
		   * end), process until that end is found.
		   */
		  if (inbuf->encoding == EBINHEX)
		    {
		      if (decode_binhex(m) == NOK)
			{
			  sprintf(ebuf, "WARNING: parse_message: BinHex decode failed :%lu", inbuf->loffset);
#ifdef DEBUG
			  if (edebug)
			    fprintf(stderr, "%s\n", ebuf);
#endif
			  logger(LOG_WARNING, ebuf);
			  return(NOK);
			}
			else
			  {
			    inbuf->lineend = inbuf->loffset;
			    inbuf->bodyend = inbuf->offset;
			    /* return(OK); */
			  }
		    } 
		  else
		    if (inbuf->encoding == EUUENCODE)
		      {
			if (decode_uuencode(m) == NOK)
			  {
			    sprintf(ebuf, "WARNING: parse_message: UUdecode failed :%lu", inbuf->loffset);
#ifdef DEBUG
			  if (edebug)
			    fprintf(stderr, "%s\n", ebuf);
#endif
			    logger(LOG_WARNING, ebuf);
			    return(NOK);
			  }
			else
			  {
			    inbuf->lineend = inbuf->loffset;
			    inbuf->bodyend = inbuf->offset;
			    /* return(OK); */
			  }
		      }
		    else
		      /* Trust what we've got and look for boundary */
		      {
			inbuf->offset += linelen;
			inbuf->loffset += 1;
		      }
		}
	      else
		/* inbuf->bodyend != 0 
		 * Body end set and reached.
		 */
		{
		  do_sibling = TRUE;
		}
	    }
	}
      else
	/* Body end set but not reached, process data */
	{
	  /* Process data.
	   * If typed data trust types
	   * and perform boundary checks. If untyped raw rfc822
	   * data, uuencode or BinHex (who have an explicit
	   * end), process until that end is found. Done above.
	   */
	  inbuf->offset += linelen;
	  inbuf->loffset += 1;
	}
      
    }
  /* End of data */
  if (inbuf->bodyend == 0)
    inbuf->bodyend = inbuf->offset;
  if (inbuf->lineend == 0)
    inbuf->lineend = inbuf->loffset;
  return(OK);
}
