#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "eps.h"

static int atom_begin(header_t *);
static atom_t *atom_new(header_t *);
static int atom_kill(atom_t *);
static header_t *header_alloc(void);
static atom_t *header_fetch_atoms(header_t *, unsigned char *);
static int header_parse_atom(header_t *, unsigned char *);

int atom_begin(header_t *h)
{
  h->atoms = (atom_t *)malloc(sizeof(atom_t));
  if (h->atoms == NULL)
     return 0;

  h->atoms->next = NULL;
  h->atail = h->atoms;

  return 1;
}

atom_t *atom_new(header_t *h)
{
  atom_t *a = NULL;

  a = (atom_t *)malloc(sizeof(atom_t));
  if (a) {
     memset((atom_t *)a, 0, sizeof(atom_t));

     a->next = NULL;
     h->atail->next = a;
     h->atail = a;
  }

  return a;
}

int atom_kill(atom_t *al)
{
  atom_t *a = NULL, *oa = NULL;

  if (!al)
     return 0;

  a = al;
  while(a->next) {
    oa = a->next;
    a->next = a->next->next;

    if (oa->name)
       free(oa->name);

    if (oa->data)
       free(oa->data);

    free(oa);
  }

  free(al);
  return 1;
}

header_t *header_alloc(void)
{
  header_t *h = NULL;
  
  h = (header_t *)malloc(sizeof(header_t));
  if (h == NULL)
     return NULL;

  memset((header_t *)h, 0, sizeof(header_t));
  return h;
}

header_t *header_parse(unsigned char *line)
{
  header_t *hdr = NULL;
  unsigned char *data = NULL, *nlc = NULL, *orig = NULL;

  hdr = header_alloc();
  if (hdr == NULL)
     return NULL;

  orig = mstrdup(line);
  if (orig == NULL)
     return hdr;

  nlc = rfc2822_remove_comments(line);
  if (nlc == NULL) {
     free(orig);
     return hdr;
  }

/*
  data = rfc2822_next_token(nlc, ':', " ");

  RFC2822 says to allow for obsolete/bad syntax on header names,
  which includes allowing for spaces in the header name.
*/
  
  data = rfc2822_next_token(nlc, ':', NULL);

  if (((!data) || (!(*data))) || ((data) && (*data == ' '))) {
     free(orig);
     free(nlc);
     return hdr;
  }

  *data++ = '\0';

  if ((!(*data)) || (!(*nlc))) {
     free(orig);
     free(nlc);
     return hdr;
  }

  hdr->orig = orig;
  hdr->name = mstrdup(nlc);

  while(*data == ' ')
    data++;

  hdr->data = mstrdup(data);
  hdr->atoms = header_fetch_atoms(hdr, data);

  free(nlc);
  return hdr;
}

/*
   Return an atom linked list if the header
   is structured.  Otherwise, return NULL.

   FORMAT: atom; atom_name=atom_data; etc
*/
atom_t *header_fetch_atoms(header_t *hh, unsigned char *data)
{
  unsigned char *h = NULL, *t = NULL, *p = NULL;

  if (!data)
     return NULL;

  else if (!(*data))
     return NULL;
 
#ifdef DEBUG
  printf("HEADER FETCH ATOMS: BEGIN\n<-- %s\n", data);
#endif

  atom_begin(hh);

  for (p = t = data; ((h = rfc2822_next_token(p, ';', NULL)) != NULL);) {
#ifdef DEBUG
      printf("HEADER FETCH ATOMS: Looping on atom token\n");
#endif

      if (*h) {
         /*
            Remove WSP after atom
         */
         if (rfc2822_is_wsp(*(h - 1))) {
            h--;

            for (; rfc2822_is_wsp(*h); h--);

            ++h; *h = '\0';
         }

         *h++ = '\0';
         p = h;     
      }

      /*
         Remove WSP at the end of the line
         set h to NULL so we know we're done.
      */
      else {
         if (rfc2822_is_wsp(*(h - 1))) {
            --h;

            for (; rfc2822_is_wsp(*h); h--);

            h++; *h = '\0';
         }

         h = NULL;
      }

      /*
         Skip any WSP before atom
      */
      for (; rfc2822_is_wsp(*t); t++);

      if (*t)
         header_parse_atom(hh, t);
#ifdef DEBUG
      else
         printf("HEADER FETCH ATOMS: Atom is blank\n");
#endif

      if (!h)
         break;

      t = p;
  }

  return hh->atoms;
}

/*
   Parse just one peice of atom data.
   We are not handed the trailing semi-colons
   
   FORMATS:
           data
           name = ["]data["]

   Returns 1 on success, 0 on 'failure'
*/
int header_parse_atom(header_t *hh, unsigned char *data)
{
  atom_t *a = NULL;
  unsigned char *h = NULL, *t = NULL, *p = NULL;
  
  if ((!data) || (!(*data)))
     return 1;

#ifdef DEBUG
  printf("HEADER PARSE ATOM: BEGIN\n<-- %s\n", data);
#endif

/*
   What's the point in allocating before we
   even know if we have data.  Let's not
   allocate space for empty atoms.

   Not sure if this breaks RFCs or not :)
   <vol@inter7.com>

   a = atom_new();
*/

  a = NULL;
  h = t = data;

  h = rfc2822_next_token(data, '=', NULL);

  /*
     No equal sign.
  */
  if ((!h) || (!(*h))) {
#ifdef DEBUG
     printf("HEADER PARSE ATOM: No variable definition; just data\n");
#endif

     if (!(*data)) {
#ifdef DEBUG
        printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif
        return 1;
     }

     p = rfc2822_convert_literals(data);

     if (*p) {
        a = atom_new(hh);
        a->data = mstrdup(p);
     }

#ifdef DEBUG
     else
        printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif

     free(p);
     return 1;
  }

  /*
	  Remove WSP after atom name
  */
  
  if (rfc2822_is_wsp(*(h - 1))) {
	 p = (h - 1);
	  
	 while(rfc2822_is_wsp(*(p - 1)))
		p--;

	 *p = '\0';
  }

  *h++ = '\0';  

  /*
	  Remove WSP from before atom data
  */

  if (rfc2822_is_wsp(*h))
     h++;

  if (!(*t)) {
#ifdef DEBUG
     printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif
     return 1;
  }

  p = rfc2822_convert_literals(t);

  if (*p) {
     a = atom_new(hh);
     a->name = mstrdup(p);

     free(p);
  
     p = rfc2822_convert_literals(h);

     if (!(*p)) {
#ifdef DEBUG
        printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif	
        free(p);
        return 1;
     }

     a->data = mstrdup(p);
     free(p);
  }

  else {
#ifdef DEBUG
     printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif    
     free(p);
     return 1;
  }

#ifdef DEBUG
  if (a) {
     printf("HEADER PARSE ATOM: New atom\n");
 
     if (a->name)
        printf("  %s=%s\n", a->name, a->data);
     else
        printf("  %s\n", a->data);
  }

  else
     printf("HEADER PARSE ATOM: Blank atom, no allocations made\n");
#endif

  return 1;
}

void header_kill(header_t *h)
{
  if (!h)
     return;

  if (h->name)
     free(h->name);

  if (h->data)
     free(h->data);

  if (h->orig)
     free(h->orig);

  if (h->atoms)
     atom_kill(h->atoms);  

  free(h);
}

#ifdef HEADER_DEBUG
/*
   Debugging.  Show header data.
*/
void header_show(header_t *h)
{
  atom_t *a = NULL;

  if (!h)
     return;

  if ((h->name == NULL) || (h->data == NULL))
     return;

  printf("HEADER:\n" \
         "  Name: [%s]\n" \
         "  Original data: [%s]\n",
         h->name, h->data);

  if (h->atoms) {
     printf("  ATOMS:\n");

     for (a = h->atoms; a->next; a = a->next) {
         if (a->next->name)
            printf("    [%s] = [%s]\n", a->next->name, a->next->data);
         else
            printf("    [%s]\n", a->next->data);
     }
  }
}
#endif

/*
   Return an atom's data by atom name from
   a header structure
*/
unsigned char *header_fetch_atom(header_t *h, unsigned char *name)
{
  atom_t *a = NULL;

  if (!(h->atoms))
     return NULL;

  for (a = h->atoms; a->next; a = a->next) {
      if (a->next->name) {
         if (!(strcasecmp(a->next->name, name)))
            return a->next->data;
      }
  }

  return NULL;
}

