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

LG_context_t* mm_ctx;

/*
  Initiates the MM module.
 */
void MM_init(LG_context_t* ctx) {
  //mail_link (&unixdriver);
  mail_link (&mmdfdriver);
  mm_ctx = ctx;
}

/*
  Extracts general mail and MIME information from a mail.

  stream - Mail in string format.

  return - Structured information about the mail.

  This is the fundamental entry point from the exterior.
 */
MM_mail_info_t* MM_extract_mail_info(const gchar* stream) {
  MAILSTREAM* mail_stream;
  BODY* body;
  STRINGLIST* cur;
  MM_mail_info_t* ret;
  long len;
  ENVELOPE* env;
  char* from;
  char* temp;
  char* header;
  char* date;
  char* reply_to;
  char* cc;
  char* message_id;
  char* t_header;
  char* subject;
  char* contents;
  int f;
  char tmpname[L_tmpnam+1];
  gchar ones[] = { 1, 1, 1, 1, '\n'};
  STRINGLIST list;
  gchar From[] = {'F', 'r', 'o', 'm'};

  mail_stream = NIL;
  //cur = mail_newstringlist();

  tmpnam(tmpname);
  f = open(tmpname, O_CREAT|O_WRONLY, 0600);
  write(f, ones, 5);
  write(f, stream, strlen(stream));
  close(f);
  mail_stream = mail_open(NULL, tmpname, NIL);
  unlink(tmpname);
  list.text.data = (unsigned char*)From;
  list.text.size = 4;
  list.next = NULL;
  
  //printf("XXX%sXXX\n", mail_fetch_header(mail_stream, 1, NIL, &list, NIL, NIL));
  
  env = mail_fetch_structure(mail_stream, 1, &body, NIL);

  header = mail_fetch_header(mail_stream, 1, NIL, &list, NIL, NIL);
  if (header && strlen(header)>6) {
    from = g_malloc(strlen(header) + 1);
    temp = from;
    t_header = header;
    header += 6;
    while (*header != 0 && *header != '\n' && *header != '\r') {
      *temp = *header;
      header++;
      temp++;
    }
    *temp = 0;
  }
  else {
    from = NULL;
  }

/*
  from = g_malloc (strlen(env->sender->mailbox) + strlen(env->sender->host) + 2);
  sprintf(from, "%s@%s", env->sender->mailbox, env->sender->host);
*/
  subject = strdup(env->subject? env->subject : "");
  date = strdup(env->date? env->date : "");
  message_id = strdup(env->message_id? env->message_id : "");
  cc = NULL;
  if(env->cc) {
    cc = g_malloc (strlen(env->cc->mailbox) + strlen(env->cc->host) + 2);
    sprintf(cc, "%s@%s", env->cc->mailbox, env->cc->host);
  }
  reply_to = NULL;
  if(env->reply_to) {
    reply_to = g_malloc (strlen(env->reply_to->mailbox) + strlen(env->reply_to->host) + 2);
    sprintf(reply_to, "%s@%s", env->reply_to->mailbox, env->reply_to->host);
  }

  contents = mail_fetchtext(mail_stream, 1);
  ret = mm_mail_info_new(from, subject, date, reply_to, cc, message_id);
  //ret = mm_mail_info_new(from, subject, cc, date, message_id, reply_to);
  ret->content = mm_explode_mime(body, contents);
  mail_close(mail_stream);
  if (from) {
    g_free(from);
  }
  g_free(subject);
  return ret;
}

/*
  Releases a mail_info structure.

  mail - Structure to be relasead.
 */
void MM_mail_info_free(MM_mail_info_t* mail) {
  mm_header_free(mail->header);
  if (mail->content) {
    mm_content_free(mail->content);
  }
  g_free(mail);
}


/*
  Creates a mail_info structure.

  from    - From: header value.
  subject - Subject: header value.

  return - a mail_info structure.
 */
MM_mail_info_t* mm_mail_info_new(
  const gchar* from, const gchar* subject,
  const gchar* date, const gchar* reply_to, const gchar* cc,
  const gchar* message_id) {
  MM_mail_info_t* ret;

  ret = g_malloc(sizeof(MM_mail_info_t));
  ret->header = mm_header_new(from, subject, date, reply_to, cc, message_id);

  return ret;
}

/*
  Creates a new content strcture.

  type         - MIME type.
  content      - Cleaned (ready to use) content.
  bulk_content - Bulk content (mainly for PGP signature verification).
  parts        - Subparts of this type.

  return - Content structure.

  content, bulk_content and parts can be NULL, it depends on the type.
 */
MM_content_t* mm_content_new(MM_type type, gchar* content, gchar* bulk_content, GList* parts) {
  MM_content_t* ctx;

  ctx = g_malloc(sizeof(MM_content_t));
  ctx->type = type;
  ctx->content = g_strdup(content);
  ctx->bulk_content = g_strdup(bulk_content);
  ctx->parts = parts;

  return ctx;
}
  
/*
  Support function to free a GList of parts.

  part  - The part
  extra - paramater required by foreach function, ignored.
 */
void mm_content_parts_free(gpointer part, gpointer extra) {
  mm_content_free((MM_content_t*)part);
}

/*
  Frees a content structure.

  content - Content structure.
 */
void mm_content_free(MM_content_t* content) {
  if (!content) return;
  if (content->content) {
    g_free(content->content);
  }
  if (content->bulk_content) {
    g_free(content->bulk_content);
  }
  if (content->parts) {
    g_list_foreach(content->parts, mm_content_parts_free, NULL);
    g_list_free(content->parts);
  }
  g_free(content);
}

/*
  Frees a mail header structure.

  header - Mail header strcture.
 */
void mm_header_free(MM_header_t* header) {
  g_free(header->from);
  g_free(header->subject);
  g_free(header->date);
  g_free(header->reply_to);
  g_free(header->cc);
  g_free(header->message_id);
  g_free(header);
}

/*
  Returns the requested header of a mail_info structure.

  mail - mail_info structure.

  header - required header.

  return - header.
 */
const gchar* MM_mail_info_header(MM_mail_info_t* mail, const gchar* header) {
  if (strcasecmp(header,"from"))
    return ((MM_mail_info_t*)mail)->header->from;
  if (strcasecmp(header,"subject"))
    return ((MM_mail_info_t*)mail)->header->subject;
  if (strcasecmp(header,"date"))
    return ((MM_mail_info_t*)mail)->header->date;
  if (strcasecmp(header,"reply_to"))
    return ((MM_mail_info_t*)mail)->header->reply_to;
  if (strcasecmp(header,"cc"))
    return ((MM_mail_info_t*)mail)->header->cc;
  if (strcasecmp(header,"message_id"))
    return ((MM_mail_info_t*)mail)->header->message_id;
  return NULL;
}

/*
  Creates a new header structure.

  from    - From: header.
  subject - Subject: header.

  return - header structure.
 */
MM_header_t* mm_header_new(const gchar* from, const gchar* subject,
  const gchar* date, const gchar* reply_to, const gchar* cc,
  const gchar* message_id) {
  MM_header_t* header;

  header = g_malloc(sizeof(MM_header_t));
  header->from       = g_strdup(from);
  header->subject    = g_strdup(subject);
  header->date       = g_strdup(date);
  header->reply_to   = g_strdup(reply_to);
  header->cc         = g_strdup(cc);
  header->message_id = g_strdup(message_id);

  return header;
}


/*
  Processes a multipart/signed part.

  body    - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_process_signed(BODY* body, char* contents) {
  BODY* signed_content;
  BODY* signature;
  gchar* content;
  gchar* bulk_content;
  int size;
  MM_content_t* signed_part;
  MM_content_t* signature_part;
  GList* sons;
  MM_content_t* ctx;
  int x;

  sons = NULL;
  signed_content = &body->nested.part->body;
  signature =  &body->nested.part->next->body;

  bulk_content  = g_strndup(contents + signed_content->mime.offset, signed_content->contents.text.size + signed_content->mime.text.size);
  content  = g_strndup(contents + signed_content->contents.offset, signed_content->contents.text.size);


  signed_part = mm_explode_mime(signed_content, contents);
  g_free(content);

  content  = g_strndup(contents + signature->contents.offset, signature->contents.text.size);
  signature_part = mm_content_new(MM_PGP_SIGNATURE,
                                  content,
                                  NULL,
                                  NULL);
  g_free(content);

  sons = g_list_append(sons, signed_part);
  sons = g_list_append(sons, signature_part);

  ctx = mm_content_new(MM_MULTIPART_SIGNED,
                       NULL,
                       bulk_content,
                       sons);
  g_free(bulk_content);

  return ctx;
}

/*
  Processes a multipart/mixed-alternative part.

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_process_normal_multipart(BODY* body, char* contents, MM_type type) {
  GList* childeren;
  PART* part;
  MM_content_t* cnt;

  part = body->nested.part;
  childeren = NULL;
  while (part) {
    cnt = mm_explode_mime(&part->body, contents);
    if (cnt) {
      childeren = g_list_append(childeren, cnt);
    }
    part = part->next;
  }

  return mm_content_new(type, NULL, NULL, childeren);
}

/*
  Processes a multipart/alternative part.

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_process_alternative(BODY* body, char* contents) {
  return mm_process_normal_multipart(body, contents, MM_MULTIPART_ALTERNATIVE);
}

/*
  Processes a multipart/mixed part.

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_process_mixed(BODY* body, char* contents) {
  return mm_process_normal_multipart(body, contents, MM_MULTIPART_MIXED);
}

/*
  Processes a multipart part.

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.

  Hello, I am only a dispatcher.
 */
MM_content_t* mm_process_multipart(BODY* body, char* contents) {
  if (strcasecmp(body->subtype, "signed") == 0) {
    return mm_process_signed(body, contents);
  }
  if (strcasecmp(body->subtype, "alternative") == 0) {
    return mm_process_alternative(body, contents);
  }
  if (strcasecmp(body->subtype, "mixed") == 0) {
    return mm_process_mixed(body, contents);
  }
  return NULL;

}

/*
  Processes a message content.

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_process_message(BODY* body, char* contents) {
  gchar* message;
  MM_content_t* content;

  message = g_malloc(body->contents.text.size + 1);
  strncpy(message, contents + body->contents.offset, body->contents.text.size);
  message[body->contents.text.size] = 0;
  content =  mm_content_new(MM_MESSAGE, message, NULL, NULL) ;
  g_free(message);

  return content;
}

/*
  Strips \r's from a string.

  str - String.
  len - String size.

  return - The cleaned string.
 */
gchar* mm_r_stripper(gchar* str, int len) {
  gchar* return_str;
  gchar* pos;
  int i;

  return_str = g_malloc(len+1);
  pos = return_str;

  for (i=0; i<len; i++) {
    if (str[i] != 13) {
      *pos = str[i];
      pos++;
    }
  }
  *pos = 0;

  return return_str;
}

/*
  Quoted-printable decoder.

  dirty - String quoted-printable encoded.

  return - Decoded string.
 */
char* mm_qp_clean(char* dirty) {
  char* clean;
  char* cur_pos;
  unsigned char char_code;

  clean = g_malloc(strlen(dirty) + 1);
  cur_pos = clean;

  while(*dirty) {
    if (*dirty == '=') {
      if (*(dirty + 1) == '\n') {
        dirty++;
      }
      // This protects against end of string boundaries (isalnum)
      else if (isalnum(*(dirty+1)) && isalnum(*(dirty+2)) ) {
        //this is not completely correct, but is acceptable
        char_code = 16 * ((*(dirty+1)>='A') ?
                             *(dirty+1)-'A' + 10 : *(dirty+1)-'0');
        char_code += (*(dirty+2)>='A') ?
                            *(dirty+2)-'A' + 10 : *(dirty+2)-'0';
        dirty++;
        dirty++;
        *cur_pos = (char) char_code;
        cur_pos++;
      }
    }
    else {
      *cur_pos = *dirty;
      cur_pos++;
    }
    dirty++;
  }
  *cur_pos = 0;

  return clean;
}

/*
  Explodes MIME (translates from the MIME library format to our own).

  body     - Object with mail/MIME information structure.
  contents - The actual contents refered from body

  return - Content structure.
 */
MM_content_t* mm_explode_mime(BODY* body, char* contents) {
  gchar* type;
  gchar* subtype;
  MM_content_t* ctx;
  int size; // should not be ignored?
  char* content;
  char* old_content;

  if ((body->type == TYPETEXT && strcasecmp(body->subtype,"plain") == 0) ||
   (body->type == TYPEAPPLICATION && strcasecmp(body->subtype,"pgp") == 0)) {
    if (body->contents.text.size) {
      content = mm_r_stripper(contents + body->contents.offset, body->contents.text.size);
      if (body->encoding == ENCQUOTEDPRINTABLE) {
        old_content = content;
        content = mm_qp_clean(old_content);
        g_free (old_content);
      }
      ctx = mm_content_new(MM_PLAIN,
                           content,
                           NULL,
                           NULL);
      g_free(content);
    }
    else {
      ctx = NULL;
    }
  }
  else if (body->type == TYPEMULTIPART ) {
    ctx = mm_process_multipart(body, contents);
  }
  else if (body->type == TYPEMESSAGE ) {
    ctx = mm_process_message(body, contents);
  }
  else {
    ctx = NULL;
  }

  return ctx;

}

/*
  c-client event handling
 */
#include "mm_hook.c"
