#include "ep_internal.h"

/*
  Please see ep_text_driver first
*/

/*
  Processes a piece of PGP signed text (with signature).

  split       - Splited lines of input with PGP content.
  credentials - Current credentials.

  return - List of authenticated_data.

Recursively calls ep_split_text.
 */
GList* ep_grab_pgp(GString* split, GList* credentials) {
  GList* return_list;
  GList* creds;
  KM_key_return_t* kr;
  CR_credential_t* cred;
  gchar** gsplit;
  const gchar* unescaped_content;
  gboolean sig_ok;
  ep_authenticated_data_t* data;

  kr = KM_signature_verify(KM_PGP, split->str, NULL);

  creds = g_list_copy(credentials);
  //should we dupplicate credentials? not in this case
  sig_ok = KM_key_return_get_signature_ok(kr);
  creds = CR_credential_list_duplicate(creds);
  if (KM_key_return_get_status(kr) == KM_OK) {
    cred  = CR_credential_new(CR_PGP, KM_key_return_get_key_id(kr), sig_ok);
    creds = g_list_append(creds, cred);
    unescaped_content = KM_key_return_get_signed_text(kr);
    gsplit = g_strsplit(unescaped_content, "\n", -1);
    return_list = ep_split_text(gsplit, creds);
    g_strfreev(gsplit);
  }
  else {
    gsplit = g_strsplit(split->str, "\n", -1);
    gsplit[0][0] = '*'; //fool the parser - HACK ALERT
    return_list = ep_split_text(gsplit, creds);
    data = (ep_authenticated_data_t*)return_list->data;
    data->data->data->str[0] = '-'; //restate the data
    g_strfreev(gsplit);
  }

  //free(unescaped_content);
  KM_key_return_free(kr);
  /*
  g_list_free(creds);
  if (sig_ok) {
    CR_credential_free(cred);
  }
  */
  return return_list;
}

/*
  Removes passwords from a text piece of an authenticated_data.

  ad           - Authenticated data with text piece.
  crendentials - Current list of credentials (which can be changed).
 */
void ep_password_clean(ep_authenticated_data_t* ad, GList** credentials) {
  GString* dirty_string;
  GString* clean_string;
  gchar** dirty;
  gchar** current_dirty;
  CR_type type;
  gchar *password_pos;

  dirty_string = ad->data->data;
  clean_string = g_string_new("");

  dirty = g_strsplit(dirty_string->str, "\n", -1);
  current_dirty = dirty;
  while (*current_dirty) {
    type = -1;
    if (strncasecmp("password:", *current_dirty, strlen("password:")) == 0) {
      type = CR_PASSWORD;
      password_pos = *current_dirty + strlen("password:");
    }
    if (strncasecmp("override:", *current_dirty, strlen("override:")) == 0) {
      type = CR_OVERRIDE;
      password_pos = *current_dirty + strlen("override:");
    }
    if (type != -1) {
      *credentials = g_list_append(*credentials, CR_credential_new(type, g_strchug(password_pos), TRUE));
      //free password_pos? possibly not needed
    }
    else {
      g_string_append(clean_string, *current_dirty);
      g_string_append(clean_string, "\n");
    }
    current_dirty++;
  }

  ad->data->data = clean_string;
  ad->credentials = g_list_concat(ad->credentials,CR_credential_list_duplicate(*credentials));
  g_string_free(dirty_string, TRUE);
  g_strfreev(dirty);
}

/*
  Generates a list of pieces of authenticated_data with text

  split           - text to be considered.
  credentials     - current credentials.
  new_credentials - new credentials for the top-level.

  return - list of authenticated_data.

  Passwords are collected.
 */
GList* ep_grab_text(GString* split, GList* credentials, GList** new_credentials) {
  ep_authenticated_data_t* new_data;
  GList* return_list;
  GList* creds;

  new_data = ep_authenticated_data_new();

  //new_data->credentials = g_list_copy(*credentials);
  //should we dupplicate credentials? Yes, most probably
  new_data->data->type = EP_TEXT;
  new_data->data->data = g_string_new(split->str);
  new_data->credentials = CR_credential_list_duplicate(credentials);
  ep_password_clean(new_data, new_credentials);

  return_list = g_list_append(NULL, new_data);
  return return_list;
}

/*
  ep_split_pgp - Deals with a pgp piece of text

  Finds boundaries, processes pgp and recursively calls the text
  processor.
*/
GList* ep_split_pgp(gchar*** split, GList* credentials) {
  GString* my_split;
  gchar* current_line; 
  GList* return_list;
  GList* new_credentials;

  return_list = NULL;
  my_split = g_string_new(**split);
  g_string_append(my_split, "\n");

  (*split)++;
  current_line = **split;
  while(current_line != NULL) {
    g_string_append(my_split, current_line);
    g_string_append(my_split, "\n");
    if (strncmp(current_line, "-----END PGP SIGNATURE-----", 27) == 0) {
      return_list = ep_grab_pgp(my_split, credentials);
      (*split)++;
      //CHECK free of my_split
      return return_list;
    }
    (*split)++;
    current_line = **split;
  }
  // this is in case of badly formed message, not good for the user
  new_credentials = NULL;
  return_list = ep_grab_text(my_split, credentials, &new_credentials);

  g_string_free(my_split, TRUE);
  return return_list;
}

/*
  Adds a list of credentials to all authenticated_data elements
  belonging to the list.

  pieces      - authenticated_data list.
  credentials - Credentials to add.
*/
void ep_patch_credentials(GList* pieces, GList* credentials) {
  CR_credential_t* credential;
  GList* my_credentials;
  ep_authenticated_data_t* data;

  while(pieces) {
    my_credentials = credentials;
    data = (ep_authenticated_data_t*) pieces->data;

    while(my_credentials) {
      credential = my_credentials->data;
      if (!CR_credential_list_has_credential(data->credentials,
            CR_credential_get_type(credential),
            CR_credential_get_value(credential),
            CR_credential_get_validity(credential))) {
        data->credentials = g_list_append(data->credentials, CR_credential_duplicate(credential));
      }
      my_credentials=my_credentials->next;
    }
    pieces = pieces->next;
  }
}

/*
  Splits text in pieces and processes it.

  The pieces are: all PGP signed content + one piece representing the
  rest of the text, example:
  text1
  pgp1
  text2
  pgp2
  text3

  is separated in 3 pieces: pgp1, pgp2 and text1+text2+text3
  text is put in front of the list
*/
GList* ep_split_text(gchar** split, GList* credentials) {
  GString* my_split;
  gchar** split_iter;
  gchar* current_line;
  gboolean first_line;
  GList* return_list;
  GList* new_credentials;
  GList* text_part;
  GList* text_credentials;

  split_iter = split;
  return_list = NULL;
  new_credentials = NULL;
  text_credentials = NULL;
  current_line = *split_iter;
  my_split = g_string_new("");
  first_line = TRUE;

  while(current_line != NULL) {
    //RFC number justifying the boundaries
    if (strncmp(current_line, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
      if (!first_line) {
        text_part = ep_grab_text(my_split, credentials, &new_credentials);
        return_list = g_list_concat(return_list, text_part);
        first_line = TRUE;
        g_string_free(my_split, TRUE);
        my_split = g_string_new("");
      }
      return_list = g_list_concat(return_list, ep_split_pgp(&split_iter, credentials));
    } else {
      g_string_append(my_split, current_line);
      g_string_append(my_split, "\n");
      first_line = FALSE;
      split_iter++;
    }
    current_line = *split_iter;
  }
  if (!first_line) {
    text_part = ep_grab_text(my_split, credentials, &new_credentials);
    return_list = g_list_concat(return_list, text_part);
  }
  // adds new_credentials to return_list
  ep_patch_credentials(return_list, new_credentials);

  g_string_free(my_split, TRUE);
  return return_list;
}

/*
  ep_text_driver - This is the entry point for the text driver

  This code is recursive. There is a contract regarding the
  credential list: if a function wants to use it (attach it to another
  structure then it has to copy it completely - list plus credentials).

  In practice it simply calls ep_split_text.

  Please note that the recursion comes from pgp (not mime application/signed)
  content: text can have pgp, that has text, that can have pgp, ...

  Also note that we prefer to deal with ASCII armoring of PGP here.

  See split_text for more info.
*/
ep_input_structure_t* ep_text_driver(ep_authenticated_data_t* data) {
  GList* return_list;
  ep_input_structure_t* input;
  GString* text;
  gchar** split;

  input = ep_input_structure_new();
  text = g_string_new(data->data->data->str);

  split = g_strsplit(text->str, "\n", -1);
  return_list = ep_split_text(split, data->credentials);
  g_strfreev(split);
  input->is_mail = FALSE;
  input->flattened_authenticated_data = return_list;

  return input; 
}

