#include "cr.h"
#include <stdlib.h>
#include <string.h>

/*
  This is the credential data structure.
 */
typedef struct {
  CR_type type;
  char* value;
  gboolean valid;
} cr_credential_t;

/*
  Creates a new credential.

  type  - The credential type (PGP, password, override)
  value - the value

  return - a credential
 */
CR_credential_t* CR_credential_new(CR_type type, const char* value,
  gboolean valid) {
  cr_credential_t* credential;

  credential = g_malloc(sizeof(cr_credential_t));
  credential->type = type;
  credential->value = g_strchomp(strdup(value));
  credential->valid = valid;

  return (CR_credential_t*) credential;
}

/*
  Frees a credential.
 */
void CR_credential_free(CR_credential_t* credential) {
  cr_credential_t* cred;

  cred = (cr_credential_t*)credential;
  g_free(cred->value);
  g_free(cred);
}
 
/*
  Returns the type of a credential.

  credential - the credential

  return - the type
*/
CR_type CR_credential_get_type(CR_credential_t* credential) {
  cr_credential_t* cred;

  cred = (cr_credential_t*)credential;
  return cred->type;
}

/*
  Returns the value of a credential.

  credential - the credential

  return - the type
*/
const char* CR_credential_get_value(CR_credential_t* credential) {
  cr_credential_t* cred;

  cred = (cr_credential_t*)credential;
  return cred->value;
}

/*
  Returns the validity of a credential.

  credential - the credential

  return - the type
*/
gboolean CR_credential_get_validity(CR_credential_t* credential) {
  cr_credential_t* cred;

  cred = (cr_credential_t*)credential;
  return cred->valid;
}

#include "../au/crypt-md5.c" //An hack for now.
#include "../au/misc.c" //An hack for now.
extern char *crypt(const char *key, const char *salt);
gboolean cr_check_crypted(const gchar* plain, const gchar* crypted) {
  gchar* my_crypted;
  MD5_CTX ctx;

  if (crypted[0] == '$') {
    my_crypted = crypt_md5(plain, crypted);
    if (strcmp(my_crypted, (char*)crypted) == 0) {
      return TRUE;
    }
  }
  else {
    my_crypted = crypt(plain, crypted);
    if (strcmp(my_crypted, crypted) == 0) {
      // BUG, no need free(my_crypted);
      return TRUE;
    }
    //free(my_crypted);
  }
  return FALSE;
}

/*
  Checks if a credential list has a specific credential, the value is
  the crypted version of the credential.

  list            - credential list to be checked
  type            - credential type
  value           - credential value
  include_invalid - includes invalid credentials

  return - TRUE if the credential is contained AND valid, FALSE if not
 */
gboolean CR_credential_list_check(GList* list,
  CR_type type, const char *value, gboolean include_invalid) {
  cr_credential_t* credential;

  if (type != CR_PASSWORD && type != CR_OVERRIDE) {
    return CR_credential_list_has_credential(list, type,
              value, include_invalid);
  }
  while(list) {
    credential = (cr_credential_t*) list->data;
    if (credential->type == type &&
        cr_check_crypted(credential->value, value)) {
      if (CR_credential_get_validity(credential) || include_invalid) {
        return TRUE;
      }
    }
    list = list->next;
  }

  return FALSE;
}

/*
  Checks if a credential list has a specific credential, the value is
  the plain version of the credential.

  list            - credential list to be checked
  type            - credential type
  value           - credential value
  include_invalid - includes invalid credentials

  return - TRUE if the credential is contained AND valid, FALSE if not
 */
gboolean CR_credential_list_has_credential(GList* list,
  CR_type type, const char *value, gboolean include_invalid) {
  cr_credential_t* credential;

  while(list) {
    credential = (cr_credential_t*) list->data;
    if (credential->type == type && strcmp(credential->value, value) == 0 ) {
      if (CR_credential_get_validity(credential) || include_invalid) {
        return TRUE;
      }
    }
    list = list->next;
  }

  return FALSE;
}

/*
  Duplicates a list of credentials

  Note: duplicates everything - list and credentials
*/
GList* CR_credential_list_duplicate(GList* credentials) {
  GList* new_credentials;
  CR_credential_t* cred;

  new_credentials = NULL;

  while (credentials) {
    cred = (CR_credential_t*) credentials->data;
    new_credentials =
      g_list_append(new_credentials,
                    CR_credential_new(
                      CR_credential_get_type(cred),
                      CR_credential_get_value(cred),
                      CR_credential_get_validity(cred)));
    credentials = credentials->next;
  }

  return new_credentials;
}


/*
  Frees a list of credentials

  Note: Frees everyting - credentials and list
*/
void CR_credential_list_free(GList* creds) {
  GList* del;

  del = creds;

  while(del) {
    CR_credential_free((CR_credential_t*)del->data);
    del = del->next;
  }

  g_list_free(creds);
}

CR_credential_t* CR_credential_duplicate(CR_credential_t* credential) {
  return CR_credential_new(
    CR_credential_get_type(credential),
    CR_credential_get_value(credential),
    CR_credential_get_validity(credential));
}
