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

static mime_t *mime_new_instance(void);
static int mime_header(eps_t *, mime_t *, header_t *);
static void mime_content_type(eps_t *, header_t *, void *);
static void mime_transfer_encoding(eps_t *, header_t *, void *);
static void mime_content_disposition(eps_t *, header_t *, void *);

struct i_header_t _m_i_headers[] = {
  { "Content-Type", mime_content_type },
  { "Content-Transfer-Encoding", mime_transfer_encoding },
  { "Content-Disposition", mime_content_disposition },
  { NULL, NULL }
};

/*
   Allocate, and initialize a new mime_t
   structure to return.
*/
mime_t *mime_new_instance(void)
{
  mime_t *m = NULL;

  m = (mime_t *)malloc(sizeof(mime_t));
  if (m == NULL)
     return NULL;

  memset((mime_t *)m, 0, sizeof(mime_t));
  
  m->content_type = CON_TEXT;
  m->encoding     = ENC_TEXT;
  m->disposition  = DIS_NONE;

  return m;
}

int mime_header(eps_t *eps, mime_t *m, header_t *h)
{
  int i = 0;

  for (i = 0; _m_i_headers[i].name; i++) {
      if (!(strcasecmp(_m_i_headers[i].name, h->name))) {
         _m_i_headers[i].func(eps, h, (void *)m);
         return 1;
      }
  }

  return 0;
}

void mime_content_type(eps_t *eps, header_t *h, void *mx)
{
  int len = 0;
  char *p = NULL;
  mime_t *m = (mime_t *)mx;
  
  if ((!h) || (!h->atoms) || (!h->atoms->next) || (!h->atoms->next->data))
     m->content_type = CON_TEXT;

  else {
    if (m->h_content_type)
       free(m->h_content_type);

    len = strlen(h->atoms->next->data);

    m->h_content_type = (char *)malloc(len + 1);
    if (m->h_content_type) {
       memset(m->h_content_type, 0, len + 1);
       memcpy(m->h_content_type, h->atoms->next->data, len);
    }

     m->content_type = content_parse(h->atoms->next->data, TYP_CON);
  }

  if (!(m->filename)) {
     p = header_fetch_atom(h, "name");
     if (p)
        m->filename = mstrdup(p);
  }

  if (m->content_type & CON_MULTI) {
     p = header_fetch_atom(h, "boundary");
     if (p)
        boundary_add(eps, p);
  }
}

void mime_transfer_encoding(eps_t *eps, header_t *h, void *mx)
{
  mime_t *m = (mime_t *)mx;

  if ((!h) || (!h->atoms) || (!h->atoms->next) || (!h->atoms->next->data))
     m->encoding = ENC_TEXT;

  else
     m->encoding = content_parse(h->atoms->next->data, TYP_ENC);
}

void mime_content_disposition(eps_t *eps, header_t *h, void *mx)
{
  char *p = NULL;
  mime_t *m = (mime_t *)mx;

  if ((!h) || (!h->atoms) || (!h->atoms->next) || (!h->atoms->next->data))
     m->disposition = DIS_NONE;

  else
     m->disposition = content_parse(h->atoms->next->data, TYP_DIS);  

  if (!(m->filename)) {
     p = header_fetch_atom(h, "filename");
     if (p)
        m->filename = mstrdup(p);
  }
}

void mime_kill(mime_t *m)
{
  if (m->filename)
     free(m->filename);

  if (m->boundary)
     free(m->boundary);

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

  if (m->h_content_type)
     free(m->h_content_type);

  free(m);
}

int mime_init_stream(eps_t *eps)
{
  char *p = NULL;

  if (eps->m)
     mime_kill(eps->m);

  eps->u->eof = 0;
  eps->m = mime_new_instance();

  p = boundary_fetch(eps, eps->b->cdepth);
  if (p) {
     eps->m->boundary = mstrdup(p);
     eps->m->depth    = eps->b->cdepth;
  }

  else
     eps->m->depth    = -1;

#ifdef MIME_DEBUG
  printf("New MIME: [%s](%d)\n", p ? p : "NONE", eps->m->depth);
#endif

  return 1;
}

header_t *mime_next_header(eps_t *eps)
{
  unsigned char *l = NULL;

  l = unfold_next_line(eps->u);
  if (l == NULL) {
#ifdef MIME_DEBUG
     printf("Unfold ends\n");
#endif
     return NULL;
  }

  if (eps->h)
     header_kill(eps->h);

  eps->h = header_parse(l);
  if (eps->h) {
     if ((eps->h->name) && (eps->h->data))
        mime_header(eps, eps->m, eps->h);
  }

  eps_source(eps, l);

  return eps->h;
}

unsigned char *mime_next_line(eps_t *eps)
{
  int ret = 0;
  unsigned char *l = NULL;

  l = buffer_next_line(eps->u->b);
  if (l == NULL)
     return NULL;

  eps_source(eps, l);

  if ((*l == '-') && (*(l + 1) == '-')) {
     ret = boundary_is(eps, (l + 2));
     if (ret == 1) {
        if (eps->m->orig)
           free(eps->m->orig);

        eps->m->orig = (char *)malloc(strlen(l) + 1);
        if (eps->m->orig) {
           memset((char *)eps->m->orig, 0, strlen(l) + 1);
           memcpy((char *)eps->m->orig, (char *)l, strlen(l));
        }
	
        return NULL;
     }

     else if (ret == 2) {
#ifdef MIME_DEBUG
       printf("Boundary [%s] terminates; removing\n", eps->m->boundary);
#endif
       eps->m->depth = -1;
       
       boundary_remove_last(eps);

       if (eps->b->cdepth == 0) {
#ifdef MIME_DEBUG
          printf("Reached 0 depth: EOF\n");
#endif
          eps->u->b->eof = 1;
       }

       if (eps->m->orig)
          free(eps->m->orig);

       eps->m->orig = (char *)malloc(strlen(l) + 1);
       if (eps->m->orig) {
          memset((char *)eps->m->orig, 0, strlen(l) + 1);
          memcpy((char *)eps->m->orig, (char *)l, strlen(l));
       }

       return NULL;
     }
  }

  if (eps->m->orig) {
     free(eps->m->orig);
     eps->m->orig = NULL;
  }

  return l;
}
