/***************************************
  $Revision: 1.10 $

  GPG module.

  Status: REVIEWED, TESTED

 Author(s):       Tiago Antao

  ******************/ /******************
  Modification History:
        tiago (10/04/2003) Created.
  ******************/ /******************
  Copyright (c) 2003               RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ***************************************/
#include <rip.h>
#include "km_internal.h"
#include <sys/file.h>

/*
  KM_pgp_trans_prepare NOT processed!
  Move errors
 */


#undef fclose
#undef close

static gboolean on_transaction = FALSE;
static gchar* key_ring = NULL;
static gchar* trs_key_ring = NULL;
static gchar* temporary_directory = NULL;
static gchar* gpg_path = NULL;
static int lock_file = 0;
static LG_context_t* ctx;

/*
  Initializes the PGP sub-system.

  configuration - configuration data. expected are: key_ring, gpg_path,
                  temporary_directory.

  return - the status.
 */
KM_status_t km_pgp_init(LG_context_t* _ctx, KM_context_data_t* configuration) {
  GHashTable* hash;

  ctx = _ctx;
  hash = (GHashTable*) configuration;

  key_ring = g_strdup(g_hash_table_lookup(configuration, "key_ring"));
  LG_log(ctx, LG_DEBUG, "km_pgp_init keyring %s", key_ring ? key_ring : "Not defined");
  temporary_directory = g_strdup(g_hash_table_lookup(configuration, "temporary_directory"));
  gpg_path = g_strdup(g_hash_table_lookup(configuration, "gpg_path"));
  if (!gpg_path) {
    gpg_path  = g_strdup("gpg");
  }
  if (!temporary_directory) {
    temporary_directory = g_strdup("/tmp");
  }
  if (!key_ring) {
    LG_log (ctx, LG_ERROR, "km_pgp_init key ring missing");
    return KM_PARAMETER_LACKING;
  }

  return KM_OK;
}

void km_pgp_end() {
  g_free(key_ring);
  g_free(temporary_directory);
  g_free(gpg_path);
}

static KM_status_t km_pgp_trans_prepare() {
  gchar* lock;
  gchar buffer[10000];
  ssize_t rd;
  int kr;
  int tkr;

  if (on_transaction) {
    return KM_OK;
  }

  LG_log (ctx, LG_DEBUG, "Entering prepare for the first time");
  on_transaction = TRUE;
  lock = g_malloc(strlen(temporary_directory) + 1 + 8);

  sprintf(lock, "%s/KM_lock", temporary_directory);
  lock_file = creat(lock, 00700);

  if (lock_file<0) {
    LG_log(ctx, LG_ERROR, "Could not lock for transaction %s", lock);
    g_free(lock);
    return KM_INTERNAL;
  }
  g_free(lock);
  lockf(lock_file, F_LOCK, 0);
  
  LG_log(ctx, LG_DEBUG, "keyring is %s", key_ring);
  kr = open(key_ring, O_RDONLY);
  if (kr<0) {
    return KM_INTERNAL;
  }
  trs_key_ring = g_malloc(strlen(temporary_directory) + 11 + 1);
  sprintf(trs_key_ring, "%s/pubring.gpg", temporary_directory);
  LG_log(ctx, LG_DEBUG, "temporary keyring is %s", trs_key_ring);
  unlink(trs_key_ring);
  tkr = open(trs_key_ring, O_CREAT|O_TRUNC|O_RDWR, 00600);
  if (tkr<0) {
    close(kr);  //a goto here for repeated free code would be nice
    close(lock_file);
    g_free(trs_key_ring);
    trs_key_ring = NULL;
    return KM_INTERNAL;
  }
  while (rd=read(kr, buffer, sizeof(buffer))) {
    if (rd<0 || write(tkr, buffer, rd) != rd) {
      close(kr);
      close(tkr);
      close(lock_file);
      g_free(trs_key_ring);
      trs_key_ring = NULL;
      return KM_INTERNAL;
    }
  }
  close(tkr);
  close(kr);

  return KM_OK;
}

/*
  Returns a char with the content of a file (should be text).

  file - The file handle.

  return - The file content as a NULL terminated string.
 */
static gchar* km_file_to_char(FILE *file) {
  GString* g;
  char buf[100];
  gchar* ret;

  g = g_string_new("");
  do {
    fgets(buf, 99, file);
    if (!feof(file)) {
       g_string_append(g, buf);
    }
   } while (!feof(file));
  ret = g->str;
  g_string_free(g, FALSE);
  return ret;
}

/*
  Verifies a signature.

  See KM_signature_verify for parameters.
 */
KM_key_return_t* km_pgp_signature_verify(gchar* text, gchar* signature) {
  km_key_return_t* key_ret;
  FILE* general;
  gboolean valid;
  gchar* key_str;
  gchar key_id[100];
#define LINE_LENGTH 400
  gchar txt[LINE_LENGTH];
  GString* gpg_line;
  gchar out_file[LINE_LENGTH];
  gchar status_file[LINE_LENGTH];
  gchar signature_file[LINE_LENGTH];
  gchar in_file[LINE_LENGTH];
  gchar tmp[LINE_LENGTH];
  gchar* char_file;

  LG_log(ctx, LG_DEBUG, "Entering km_pgp_signature_verify");
  valid = FALSE;
  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }
  sprintf(out_file,"%s/km_out_%d", tmp, getpid());
  sprintf(status_file,"%s/km_status_%d", tmp, getpid());
  sprintf(signature_file,"%s/km_sig_%d", tmp, getpid());
  sprintf(in_file,"%s/km_in_%d", tmp, getpid());

  general = fopen(in_file,"w");
  fprintf(general, "%s", text);
  fclose(general);
  if (signature) {
    general = fopen(signature_file,"w");
    fprintf(general, "%s", signature);
    fclose(general);
  }
  //awful
  gpg_line = g_string_new(gpg_path);
  g_string_append(gpg_line, " --no-default-keyring --no-secmem-warning ");
  if (key_ring) {
    g_string_append(gpg_line, "--keyring ");
    g_string_append(gpg_line, on_transaction ? trs_key_ring : key_ring);
  }
  g_string_append(gpg_line, " -o ");
  g_string_append(gpg_line, out_file);
  if (signature) {
    g_string_append(gpg_line, " --verify ");
    g_string_append(gpg_line, signature_file);
  }
  else {
    g_string_append(gpg_line, " -d");
  }
  g_string_append(gpg_line, " ");
  g_string_append(gpg_line, in_file);
  g_string_append(gpg_line, " >");
  g_string_append(gpg_line, status_file);
  g_string_append(gpg_line, " 2>");
  g_string_append(gpg_line, status_file);
  LG_log(ctx, LG_DEBUG, "command_line: %s", gpg_line->str);
  system(gpg_line->str);
  g_string_free(gpg_line, TRUE);
  /* Parsing gpg output */

  general = fopen(status_file, "r");
  while (fgets (txt, LINE_LENGTH - 1, general) != NULL) {
    LG_log(ctx, LG_DEBUG, "gpg returns: %s", txt);
    if (strstr(txt, "Good signature") != NULL)
      valid = TRUE; 

    if ((key_str = strstr(txt, "key ID")) != NULL) {
      key_str += 7;
      strcpy((char*)key_id, key_str);
      key_id[8] = 0;
    }
  }
  fclose(general);

  if (!signature) {
    general = fopen(out_file,"r");
    if (!general) {
      key_ret = km_key_return_new(NULL, FALSE, NULL, KM_INTERNAL);
    }
    else {
      char_file = km_file_to_char(general);
      key_ret = km_key_return_new((gchar*)key_id,
        valid, char_file, KM_OK);
      g_free(char_file);
      fclose(general);
    }
  }
  else {
    key_ret = km_key_return_new((gchar*)key_id,
      valid, text, KM_OK);
  }

  unlink(in_file);
  unlink(out_file);
  unlink(status_file);
  if (signature) {
    unlink(signature_file);
  }
  return key_ret;
}

/*
  Removes a key.

  See KM_key_remove for parameters.
 */
KM_status_t km_pgp_key_remove(gchar* key_id) {
  FILE* general;
  gboolean removed;
  gchar* key_str;
#define LINE_LENGTH 400
  gchar txt[LINE_LENGTH];
  GString* gpg_line;
  gchar out_file[LINE_LENGTH];
  gchar status_file[LINE_LENGTH];
  gchar secret_key_ring[LINE_LENGTH];
  gchar tmp[LINE_LENGTH];
  gchar* char_file;

  km_pgp_trans_prepare();
  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }
  sprintf(out_file,"%s/km_out_%d", tmp, getpid());
  sprintf(status_file,"%s/km_status_%d", tmp, getpid());
  sprintf(secret_key_ring,"%s/km_secret_%d.gpg", tmp, getpid());

  //awful
  
  gpg_line = g_string_new(gpg_path);
  g_string_append(gpg_line, " --no-default-keyring --batch --yes --no-secmem-warning ");
  g_string_append(gpg_line, "--secret-keyring ");
  g_string_append(gpg_line, secret_key_ring);
  if (key_ring) {
    g_string_append(gpg_line, " --keyring ");
    g_string_append(gpg_line, trs_key_ring);
  }
  g_string_append(gpg_line, " --delete-key ");
  g_string_append(gpg_line, key_id);
  g_string_append(gpg_line, " > ");
  g_string_append(gpg_line, status_file);
  g_string_append(gpg_line, " 2> ");
  g_string_append(gpg_line, status_file);
  LG_log(ctx, LG_DEBUG, "%s", gpg_line->str);
  system(gpg_line->str);
  g_string_free(gpg_line, TRUE);

  /* Parsing gpg output */
  general = fopen(status_file, "r");
  removed = TRUE;
  LG_log(ctx, LG_DEBUG, "gpg output for key remove follows");
  while (fgets (txt, LINE_LENGTH - 1, general) != NULL) {
    LG_log(ctx, LG_DEBUG, "%s", txt);
    if (strstr(txt, "delete key failed") != NULL) {
      removed = FALSE; 
    }
  }
  fclose(general);

  unlink(out_file);
  unlink(status_file);
  unlink(secret_key_ring);
  return removed ? KM_OK : KM_NO_KEY;
}

/*
  Verifies if a key exists.

  key_id - the key_id.

  return - TRUE if exists, FALSE if not.
 */
gboolean km_pgp_key_exists(gchar* key_id) {
  FILE* general;
  gboolean found;
  gchar* key_str;
#define LINE_LENGTH 400
  gchar txt[LINE_LENGTH];
  GString* gpg_line;
  gchar status_file[LINE_LENGTH];
  gchar tmp[LINE_LENGTH];
  gchar* char_file;

  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }
  sprintf(status_file,"%s/km_status_%d", tmp, getpid());

  //awful
  gpg_line = g_string_new(gpg_path);
  g_string_append(gpg_line, " --no-default-keyring --no-secmem-warning ");
  if (key_ring) {
    g_string_append(gpg_line, "--keyring ");
    g_string_append(gpg_line, on_transaction ? trs_key_ring : key_ring);
  }
  g_string_append(gpg_line, " --list-keys ");
  g_string_append(gpg_line, key_id);
  g_string_append(gpg_line, " > ");
  g_string_append(gpg_line, status_file);
  g_string_append(gpg_line, " 2> ");
  g_string_append(gpg_line, status_file);
  LG_log(ctx, LG_DEBUG, "%s\n", gpg_line->str);
  system(gpg_line->str);
  g_string_free(gpg_line, TRUE);

  /* Parsing gpg output */
  general = fopen(status_file, "r");
  found = FALSE;
  while (fgets (txt, LINE_LENGTH - 1, general) != NULL) {
    LG_log(ctx, LG_DEBUG, "%s\n", txt);
    if (strstr(txt, key_id) != NULL) {
      found = TRUE; 
    }
  }
  fclose(general);

  unlink(status_file);
  return found;
}

/*
  Adds a key to a variable key ring.

  key      - the string representation of a key.
  key_ring - the file name (with path) of the key ring.

  return - A key_return with the result from the operation.

  The variable key ring is used for testing, ie, doing an operation
  on a safe key ring before doing it on the real one.
 */
KM_key_return_t* km_pgp_key_add_internal(gchar* key, gchar *key_ring) {
  km_key_return_t* key_ret;
  FILE* general;
  gboolean valid;
  gchar* key_str;
  gchar key_id[100];
#define LINE_LENGTH 400
  gchar txt[LINE_LENGTH];
  GString* gpg_line;
  gchar out_file[LINE_LENGTH];
  gchar secret_key_ring[LINE_LENGTH];
  gchar status_file[LINE_LENGTH];
  gchar in_file[LINE_LENGTH];
  gchar tmp[LINE_LENGTH];
  gchar* char_file;
  int imported;
  int unchanged;
  int secret;

  imported = 0;
  unchanged = 0;
  secret = 0;
  valid = FALSE;
  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }
  sprintf(secret_key_ring,"%s/km_sec_%d.gpg", tmp, getpid());
  sprintf(out_file,"%s/km_out_%d", tmp, getpid());
  sprintf(status_file,"%s/km_status_%d", tmp, getpid());
  sprintf(in_file,"%s/km_in_%d", tmp, getpid());

  general = fopen(in_file,"w");
  fprintf(general, "%s", key);
  fclose(general);
  //awful
  gpg_line = g_string_new(gpg_path);
  g_string_append(gpg_line, " --no-default-keyring --no-secmem-warning ");
  g_string_append(gpg_line, " --secret-keyring ");
  g_string_append(gpg_line, secret_key_ring);
  g_string_append(gpg_line, " --keyring ");
  g_string_append(gpg_line, key_ring);
  g_string_append(gpg_line, " --import ");
  g_string_append(gpg_line, in_file);
  g_string_append(gpg_line, " > ");
  g_string_append(gpg_line, status_file);
  g_string_append(gpg_line, " 2> ");
  g_string_append(gpg_line, status_file);
  LG_log(ctx, LG_DEBUG, "%s\n", gpg_line->str);
  system(gpg_line->str);
  g_string_free(gpg_line, TRUE);
  /* Parsing gpg output */

  general = fopen(status_file, "r");
  while (fgets (txt, LINE_LENGTH - 1, general) != NULL) {
    LG_log(ctx, LG_DEBUG, "%s", txt);
    if (strstr(txt, "public key") != NULL) {
      if ((strstr(txt, "unchanged") != NULL) || 
          (strstr(txt, "not changed") != NULL)) {
        sprintf(key_id, "%8s\n", txt + 9); 
        key_id[8] = 0;
        unchanged++;
      }
      if(strstr(txt, "imported") != NULL) {
        sprintf(key_id, "%8s\n", txt + 9); 
        key_id[8] = 0;
        imported++;
      }
    } 
    if (strstr(txt, "secret key") != NULL ||
        strstr(txt, "failed to create temporary file") != NULL ) {
        //this falied stuff has to do with using /dev/null its OK
      secret++;
    }
  }
  fclose(general);

  unlink(in_file);
  unlink(out_file);
  unlink(status_file);
  unlink(secret_key_ring);

  //If there is more than 1 key...
  if (secret) {
    key_ret = km_key_return_new(key_id, FALSE, NULL, KM_SECRET_KEY);
  }
  else if (unchanged) {
    key_ret = km_key_return_new(key_id, FALSE, NULL, KM_KEY_EXISTS);
  }
  else if ((unchanged+imported+secret) > 1) {
    key_ret = km_key_return_new(NULL, FALSE, NULL, KM_MULTIPLE_KEYS);
  }
  else if (imported) {
    key_ret = km_key_return_new(key_id, FALSE, NULL, KM_OK);
  }
  else {
    key_ret = km_key_return_new(NULL, FALSE, NULL, KM_NO_KEY);
  }

  return key_ret;
}


void km_pgp_key_get_fingerprint(km_key_return_t* kr, gchar *key_ring) {
  km_key_return_t* key_ret;
  FILE* general;
  gboolean valid;
  gchar* key_str;
  gchar key_id[100];
#define LINE_LENGTH 400
  gchar txt[LINE_LENGTH];
  GString* gpg_line;
  gchar out_file[LINE_LENGTH];
  gchar status_file[LINE_LENGTH];
  gchar tmp[LINE_LENGTH];
  gchar finger_print[LINE_LENGTH];
  gchar key_owner[LINE_LENGTH];
  gchar* char_file;
  int imported;
  int unchanged;
  int secret;

  imported = 0;
  unchanged = 0;
  secret = 0;
  finger_print[0] = 0;
  key_owner[0] = 0;
  valid = FALSE;
  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }
  sprintf(out_file,"%s/km_out_%d", tmp, getpid());
  sprintf(status_file,"%s/km_status_%d", tmp, getpid());

  //awful
  gpg_line = g_string_new(gpg_path);
  g_string_append(gpg_line, " --no-default-keyring --no-secmem-warning ");
  g_string_append(gpg_line, "--keyring ");
  g_string_append(gpg_line, key_ring);
  g_string_append(gpg_line, " --fingerprint ");
  g_string_append(gpg_line, KM_key_return_get_key_id(kr) );
  g_string_append(gpg_line, " > ");
  g_string_append(gpg_line, status_file);
  g_string_append(gpg_line, " 2> ");
  g_string_append(gpg_line, status_file);

  //printf("%s\n", gpg_line->str);
  system(gpg_line->str);
  g_string_free(gpg_line, TRUE);
  /* Parsing gpg output */

  general = fopen(status_file, "r");
  while (fgets (txt, LINE_LENGTH - 1, general) != NULL) {
    //printf("%s\n", txt);
    if ((key_str = strstr(txt, "pub "/*"Key fingerprint ="*/)) == txt /*!= NULL*/) {
      strcpy(key_owner, key_str + 30);
      key_owner[strlen(key_owner)-1] = 0;
    }
    if ((key_str = strstr(txt, "Key fingerprint =")) !=NULL) {
      strcpy(finger_print, key_str + 18);
      finger_print[strlen(finger_print)-1] = 0;
    }
  }
  fclose(general);

  unlink(out_file);
  unlink(status_file);

  if (finger_print[0]) {
    km_key_return_add(kr, "finger_print", finger_print);
  } 
  if (key_owner[0]) {
    km_key_return_add(kr, "key_owner", key_owner);
  } 
}

KM_key_return_t* km_pgp_add_check(gchar* key, gboolean get_information) {
#define LINE_LENGTH 400
  gchar tmp[LINE_LENGTH];
  gchar scratch[LINE_LENGTH];
  gchar out_dir[LINE_LENGTH];
  gchar out_ring[LINE_LENGTH];
  KM_key_return_t* key_return;

  if (temporary_directory) {
    strcpy(tmp, temporary_directory);
  }
  else {
    strcpy(tmp, "/tmp");
  }

  sprintf(out_dir,"%s/km_dir_%d", tmp, getpid());
  mkdir(out_dir, 0700);
  //sprintf(out_ring,"%s/km_dir_%d/pubring.gpg", tmp, getpid());
  sprintf(out_ring, "%s/pubring.gpg", out_dir);
  key_return = km_pgp_key_add_internal(key, out_ring);
  if (get_information && KM_key_return_get_key_id(key_return)) {
    km_pgp_key_get_fingerprint(key_return, out_ring);
  }
  unlink(out_ring);
  sprintf(out_ring, "%s/pubring.gpg~", out_dir);
  unlink(out_ring);
  rmdir(out_dir);
  //system(scratch);

  return key_return;
}

/*
  Reports on a key.

  See KM_key_add for parameters.
 */
KM_key_return_t* km_pgp_get_info(gchar* key) {
#define LINE_LENGTH 400
  KM_key_return_t* key_return;

  key_return = km_pgp_add_check(key, TRUE);

  return key_return;
}

/*
  Adds a key.

  See KM_key_add for parameters.
 */
KM_status_t km_pgp_key_add(gchar* key) {
#define LINE_LENGTH 400
  KM_key_return_t* key_return;
  KM_status_t temp_status;


  LG_log(ctx, LG_DEBUG, "Entering km_pgp_key_add");
  km_pgp_trans_prepare();
  key_return = km_pgp_add_check(key, FALSE);
  temp_status = KM_key_return_get_status(key_return);

  if (temp_status == KM_OK) {
    LG_log (ctx, LG_DEBUG, "add_check OK");
    if (km_pgp_key_exists(((km_key_return_t*)key_return)->key_id)) {
      LG_log (ctx, LG_ERROR, "Key already exists");
      ((km_key_return_t*)key_return)->status = KM_KEY_EXISTS;
    }
    else {
      KM_key_return_free(key_return);
      key_return = km_pgp_key_add_internal(key, trs_key_ring);
    }
  }

  temp_status = KM_key_return_get_status(key_return);
  KM_key_return_free(key_return);
  return temp_status;
}

/*
  Updates a key.

  See KM_key_update for parameters.
 */
KM_status_t km_pgp_key_update(gchar* key_id, gchar* key) {
  KM_status_t ret;
  KM_key_return_t* kr_ret;
  gchar* real_key_id;

  km_pgp_trans_prepare();
  kr_ret = km_pgp_add_check(key, FALSE);
  ret = KM_key_return_get_status(kr_ret);
  real_key_id = g_strdup(KM_key_return_get_key_id(kr_ret));
  KM_key_return_free(kr_ret);
  if (strcmp(real_key_id, key_id) != 0) {
    g_free(real_key_id);
    return KM_IDS_DONT_MATCH;
  }
  g_free(real_key_id);
  if (ret != KM_OK) {
    LG_log(ctx, LG_ERROR, "KM_pgp_key_return_get_status returned: %s",
           KM_return_string(ret));
    return ret;
  }
  if (km_pgp_key_exists(key_id)) {
    ret = km_pgp_key_remove(key_id);
    if (ret != KM_OK) {
      LG_log(ctx, LG_ERROR, "km_pgp_key_remove returned: %s",
             KM_return_string(ret));

      return ret;
    }
    ret = km_pgp_key_add(key);
    if (ret != KM_OK) {
      LG_log(ctx, LG_ERROR, "km_pgp_key_add returned: %s",
             KM_return_string(ret));
    }

    return ret;
  }
  else {
    return KM_UNEXISTENT_KEY;
  }
}

KM_status_t move(gchar* orig, gchar* dest) {
  int o;
  int d;
  int rd;
  gchar  buffer[10000];
  gchar* dest_rename;

  o = open(orig, O_RDONLY);
  if (o<0) {
    return KM_INTERNAL;
  }
  dest_rename = g_malloc(strlen(dest)+4+1);
  sprintf(dest_rename, "%s.ren", dest);
  d = open(dest_rename, O_CREAT|O_TRUNC|O_WRONLY, 00600);
  if (d<0) {
    close(o);
    g_free(dest_rename);
    return KM_INTERNAL;
  }
  while (rd=read(o, buffer, sizeof(buffer))) {
    if (rd<0 || (rd != write(d, buffer, rd)) ) {
      close(o);
      close(d);
      g_free(dest_rename);
      return KM_INTERNAL;
    }
  }
  close(o);
  close(d);
  if (rename(dest_rename, dest)==0) {
    unlink(orig);
    g_free(dest_rename);
    return KM_OK;
  }
  else {
    g_free(dest_rename);
    return KM_INTERNAL;
  }
}

/*
  Commits a set of operations.

  See KM_commit for parameters.
 */
KM_status_t km_pgp_commit() {
  int rename_status;

  LG_log(ctx, LG_DEBUG, "Calling pgp commit for %s %s", trs_key_ring, key_ring);
  rename_status = KM_OK;
  if (on_transaction) {
    rename_status = move(trs_key_ring, key_ring);

    lockf(lock_file, F_ULOCK, 0);
    close(lock_file);
    g_free(trs_key_ring);
  }
  on_transaction = FALSE;
  return rename_status;
}

/*
  Rolls back a set of operations.

  See KM_rollback for parameters.
 */
KM_status_t km_pgp_rollback() {
  if (on_transaction) {
    unlink(trs_key_ring);

    lockf(lock_file, F_ULOCK, 0);
    close(lock_file);
    g_free(trs_key_ring);
  }
  on_transaction = FALSE;
  return KM_OK;
}

/*
  Registers the PGP driver.

  return - A structure with PKI function pointers.
*/
PKI km_pgp_register() {
  PKI pki;

  pki.init = &km_pgp_init;
  pki.end = &km_pgp_end;
  pki.signature_verify = &km_pgp_signature_verify;
  pki.key_remove = &km_pgp_key_remove;
  pki.key_get_info = &km_pgp_get_info;
  pki.key_add = &km_pgp_key_add;
  pki.key_update = &km_pgp_key_update;
  pki.commit = &km_pgp_commit;
  pki.rollback = &km_pgp_rollback;

  return pki;
}
