/*
**
** Copyright (C) 1994 Swedish University Network (SUNET)
**
**
** This program is developed by UDAC, Uppsala University by commission
** of the Swedish University Network (SUNET). 
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITTNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
** 
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**
**                                        Martin.Wendel@udac.uu.se
**                                        Torbjorn.Wictorin@udac.uu.se
**
**                                        UDAC	
**                                        P.O. Box 174
**                                        S-751 04 Uppsala
**                                        Sweden
**
*/


#include "emil.h"
#define HQUOTE -1

/* Function definitions */
void process_header_line(struct header *, char *);
void save_header(struct message *, char *, short, int);
void clear_end(char *);
void add_header_item();
void add_header_delimiter();
static void code_header(struct header *);
static void code_hvalue(struct hprs *, struct data *);
/* 
 * 
 */

int
load_header(struct message *m)
{
  char line_buf[HDRLEN];
  struct data *indata;
  int linelen;
  long startoffset;
  long startline;
  char *buf;

  indata = m->sd;


  /*
   * Make sure there is data to process, else exit.
   */
  if (indata->end <= indata->offset)
    return(OK);


  startoffset = indata->offset;
  startline = indata->loffset;
  bzero(line_buf, HDRLEN);
  /*
   * If current header is not empty, look for folded header lines.
   */
  while (1)
    {
      buf = indata->contents + indata->offset;

      if ((linelen = getline(indata)) == 0)
	{
	  /* End of data, illegal header */
	  indata->offset = startoffset; /* Rewind */
	  indata->loffset = startline; /* Rewind */
	  m->h = NULL;
	  return(NOK);
	}
      indata->loffset += 1;
      if ((*buf == ' ' || *buf == '\t') && *line_buf != '\0') 
	{
	  /* Unfold header line and append to previos line */
	  clear_end(line_buf);
	  assert(linelen > 0);
	  strncat(line_buf, buf, (unsigned) linelen);
	}
      else
	/* Not folded yet */
	{
	  if (*line_buf != '\0')
	  /* If no header line yet, append header line */
	  /* Already got header line */
	    {
	      if (strncasecmp(line_buf, "from ", 5) == 0 ||
		  strncasecmp(line_buf,">from ", 6) == 0)
		{
		  /* Process folded UNIX from line */
		  save_header(m, line_buf, (short) UNIXFROM, RFC822);
		  bzero(line_buf, HDRLEN);
		}
	      else
		{
		  if (isheader(line_buf))
		    {
		      save_header(m, line_buf, (short) HEADER, m->sd->format);
		      bzero(line_buf, HDRLEN);
		    }
		  else
		    {
		      /* Syntax error or end of header */
		      indata->offset = startoffset; /* Rewind */
		      sprintf(ebuf, "load_header: illegal end of header: %s", line_buf);
		      logger(LOG_ERR, ebuf);
		      m->h = NULL;
		      return(NOK);
		    }
		}
	    }
	}
      if(linelen == 1 && *buf == '\n')
	{
	  /* End of header */
	  indata->offset++;
	  return(OK);
	}
      
      if (*line_buf == '\0')
	{
          assert(linelen > 0);
	  strncpy(line_buf, buf, (unsigned) linelen);
	}
      indata->offset += linelen;
    }
  /* Not reached */
}

/* 
 * 
 */

void
clear_end(char *line)
{
  line += strlen(line) - 1;
  if (*line == '\n') 
    {
      *line = '\0';
      line--;
      if (*line == '\r') 
	{
	  *line = '\0';
	  line--;
	}
    }
}

/* 
 * save_header()
 *
 * Save the header field and header value in a list of header structures.
 *
 */


void
save_header(struct message *mess, char *line, short type, int format)
{
  struct header *hdr, *thdr;
  char *value;
  hdr = (struct header *)Yalloc(sizeof(struct header));
  hdr->type = type;
  hdr->format = format;
  clear_end(line);
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "+ Save header: %s\n", line);
#endif
  hdr->field = (struct data *)Yalloc(sizeof(struct data ));
  if (type == UNIXFROM)
    append_data(hdr->field, line, strlen(line));
  else
    {
      hdr->value = (struct data *)Yalloc(sizeof(struct data ));
      value = index(line, ':');	assert(value);
      *value = '\0';
      value++;
      while(isspace(*value))
	value++;
      append_data(hdr->field, line, strlen(line));
      append_data(hdr->value, value, strlen(value));
    }
  if (mess->h == NULL)
    mess->h = hdr;
  else
    {
      for (thdr = mess->h; thdr->next != NULL; thdr = thdr->next)
	;
      thdr->next = hdr;
    }
  code_header(hdr);
}

void
add_header(struct message *mess, char *field, char *value, int format)
{
  struct header *hdr, *thdr;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "+ Add header: %s: %s\n", field, value);
#endif

  if (mess->h != NULL)
    for (thdr = mess->h; thdr != NULL; thdr = thdr->next)
      {
	if (thdr->field->end != 0 && 
	    cmatch(thdr->field->contents, field) == TRUE)
	  {
	    thdr->value->offset = 0;
	    thdr->value->end = 0;
	    thdr->value->bodyend = 0;
	    append_data(thdr->value, value, strlen(value));
	    append_char(thdr->value, '\0'); /* NULL terminate */
	    thdr->format = format;
	    thdr->hvalue = NULL;
	    return;
	  }
	hdr = thdr;
      }
  thdr = hdr;
  hdr = (struct header *)Yalloc(sizeof(struct header));
  hdr->type = HEADER;
  hdr->format = format;
  hdr->field = (struct data *)Yalloc(sizeof(struct data ));
  hdr->value = (struct data *)Yalloc(sizeof(struct data ));
  append_data(hdr->field, field, strlen(field));
  if (value != NULL)
    append_data(hdr->value, value, strlen(value));

  if (mess->h == NULL)
    mess->h = hdr;
  else
    thdr->next = hdr;
}

int
isheader(char *string)
{
  char *t, *p;
  if ((t = index(string, ':')) == NULL || t == string)
    return(0);
  if ((p = index(string, ' ')) != NULL && p < t)
    return(0);
  return(1);
}


static	void code_header(struct header *th)
{
  struct data *outbuf;
  
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "+ code_header.\n");
#endif
  parse_rfc1522(th);
  if (th->hvalue != NULL)
    {
      outbuf = (struct data *)Yalloc(sizeof(struct data));
      (void)code_hvalue(th->hvalue, outbuf);
      th->value = outbuf;
    }
}

static void code_hvalue(struct hprs *h, struct data *outbuf)
{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "+ code_hvalue.\n");
#endif

  switch (h->type)
    {
    case ATOM:
    case DELIMITER:
    case RFC1522:
      append_data(outbuf, h->td->contents + h->td->bodystart,
		  h->td->bodyend - h->td->bodystart);
      break;
    case DLITERAL:
      append_char(outbuf, '[');
      break;
    case COMMENT:
      append_char(outbuf, '(');
      break;
    case HQSTRING:
      append_char(outbuf, '"');
      break;
    case RADDR:
      append_char(outbuf, '<');
      break;
    default:
      break;
    }
  if (h->child != NULL)
    code_hvalue(h->child, outbuf);
  switch(h->type)
    {
    case DLITERAL:
      append_char(outbuf, ']');
      break;
    case COMMENT:
      append_char(outbuf, ')');
      break;
    case HQSTRING:
      append_char(outbuf, '"');
      break;
    case RADDR:
      append_char(outbuf, '>');
      break;
    default:
      break;
    }
  
  if (h->sibling != NULL)
    code_hvalue(h->sibling, outbuf);
}
