/*
 * Copyright (C) 2019 Kevin J. McCarthy <kevin@8t8.us>
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "mutt_idna.h"
#include "autocrypt.h"
#include "autocrypt_private.h"

/* Prepared statements */
static sqlite3_stmt *AccountGetStmt;
static sqlite3_stmt *AccountInsertStmt;
static sqlite3_stmt *AccountUpdateStmt;
static sqlite3_stmt *AccountDeleteStmt;
static sqlite3_stmt *PeerGetStmt;
static sqlite3_stmt *PeerInsertStmt;
static sqlite3_stmt *PeerUpdateStmt;
static sqlite3_stmt *PeerHistoryInsertStmt;
static sqlite3_stmt *GossipHistoryInsertStmt;

static int autocrypt_db_create (const char *db_path)
{
  if (sqlite3_open_v2 (db_path,
                       &AutocryptDB,
                       SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,
                       NULL) != SQLITE_OK)
  {
    /* L10N:
       %s is the path to the database.  For some reason sqlite3 failed
       to open that database file.
    */
    mutt_error (_("Unable to open autocrypt database %s"), db_path);
    mutt_sleep (0);
    return -1;
  }
  return mutt_autocrypt_schema_init ();
}

int mutt_autocrypt_db_init (int can_create)
{
  int rv = -1;
  BUFFER *db_path = NULL;
  struct stat sb;

  if (AutocryptDB)
    return 0;

  if (!option (OPTAUTOCRYPT) || !AutocryptDir)
    return -1;

  db_path = mutt_buffer_pool_get ();
  mutt_buffer_concat_path (db_path, AutocryptDir, "autocrypt.db");

  if (stat (mutt_b2s (db_path), &sb))
  {
    if (!can_create)
      goto cleanup;
    if (autocrypt_db_create (mutt_b2s (db_path)))
      goto cleanup;
    /* Don't abort the whole init process because account creation failed */
    mutt_autocrypt_account_init (1);
    mutt_autocrypt_scan_mailboxes ();
  }
  else
  {
    if (sqlite3_open_v2 (mutt_b2s (db_path),
                         &AutocryptDB,
                         SQLITE_OPEN_READWRITE,
                         NULL) != SQLITE_OK)
    {
      /* L10N:
         Error message if autocrypt couldn't open the sqlite database
         for some reason.  The %s is the full path of the database file.
      */
      mutt_error (_("Unable to open autocrypt database %s"), mutt_b2s (db_path));
      mutt_sleep (0);
      goto cleanup;
    }

    if (mutt_autocrypt_schema_update ())
      goto cleanup;
  }

  rv = 0;

cleanup:
  mutt_buffer_pool_release (&db_path);
  return rv;
}

void mutt_autocrypt_db_close (void)
{
  if (!AutocryptDB)
    return;

  sqlite3_finalize (AccountGetStmt);
  AccountGetStmt = NULL;
  sqlite3_finalize (AccountInsertStmt);
  AccountInsertStmt = NULL;
  sqlite3_finalize (AccountUpdateStmt);
  AccountUpdateStmt = NULL;
  sqlite3_finalize (AccountDeleteStmt);
  AccountDeleteStmt = NULL;

  sqlite3_finalize (PeerGetStmt);
  PeerGetStmt = NULL;
  sqlite3_finalize (PeerInsertStmt);
  PeerInsertStmt = NULL;
  sqlite3_finalize (PeerUpdateStmt);
  PeerUpdateStmt = NULL;

  sqlite3_finalize (PeerHistoryInsertStmt);
  PeerHistoryInsertStmt = NULL;

  sqlite3_finalize (GossipHistoryInsertStmt);
  GossipHistoryInsertStmt = NULL;

  sqlite3_close_v2 (AutocryptDB);
  AutocryptDB = NULL;
}

void mutt_autocrypt_db_normalize_addrlist (ADDRESS *addrlist)
{
  ADDRESS *addr;

  mutt_addrlist_to_local (addrlist);
  for (addr = addrlist; addr; addr = addr->next)
    ascii_strlower (addr->mailbox);
  mutt_addrlist_to_intl (addrlist, NULL);
}

/* The autocrypt spec says email addresses should be
 * normalized to lower case and stored in idna form.
 *
 * In order to avoid visible changes to addresses in the index,
 * we make a copy of the address before lowercasing it.
 *
 * The return value must be freed.
 */
static ADDRESS *copy_normalize_addr (ADDRESS *addr)
{
  ADDRESS *norm_addr;

  /* NOTE: the db functions expect a single address, so in
   * this function we copy only the address passed in.
   *
   * The normalize_addrlist above is extended to work on a list
   * because of requirements in autocrypt.c
   */

  norm_addr = rfc822_new_address ();
  norm_addr->mailbox = safe_strdup (addr->mailbox);
  norm_addr->is_intl = addr->is_intl;
  norm_addr->intl_checked = addr->intl_checked;

  mutt_autocrypt_db_normalize_addrlist (norm_addr);
  return norm_addr;
}

/* Helper that converts to char * and safe_strdups the result */
static char *strdup_column_text (sqlite3_stmt *stmt, int index)
{
  const char *val = (const char *)sqlite3_column_text (stmt, index);
  return safe_strdup (val);
}

AUTOCRYPT_ACCOUNT *mutt_autocrypt_db_account_new (void)
{
  return safe_calloc (1, sizeof(AUTOCRYPT_ACCOUNT));
}

void mutt_autocrypt_db_account_free (AUTOCRYPT_ACCOUNT **account)
{
  if (!account || !*account)
    return;
  FREE (&(*account)->email_addr);
  FREE (&(*account)->keyid);
  FREE (&(*account)->keydata);
  FREE (account);      /* __FREE_CHECKED__ */
}

int mutt_autocrypt_db_account_get (ADDRESS *addr, AUTOCRYPT_ACCOUNT **account)
{
  int rv = -1, result;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);
  *account = NULL;

  if (!AccountGetStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "SELECT "
          "email_addr, "
          "keyid, "
          "keydata, "
          "prefer_encrypt, "
          "enabled "
          "FROM account "
          "WHERE email_addr = ?",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &AccountGetStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (AccountGetStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  result = sqlite3_step (AccountGetStmt);
  if (result != SQLITE_ROW)
  {
    if (result == SQLITE_DONE)
      rv = 0;
    goto cleanup;
  }

  *account = mutt_autocrypt_db_account_new ();
  (*account)->email_addr = strdup_column_text (AccountGetStmt, 0);
  (*account)->keyid = strdup_column_text (AccountGetStmt, 1);
  (*account)->keydata = strdup_column_text (AccountGetStmt, 2);
  (*account)->prefer_encrypt = sqlite3_column_int (AccountGetStmt, 3);
  (*account)->enabled = sqlite3_column_int (AccountGetStmt, 4);

  rv = 1;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (AccountGetStmt);
  return rv;
}

int mutt_autocrypt_db_account_insert (ADDRESS *addr, const char *keyid,
                                      const char *keydata, int prefer_encrypt)
{
  int rv = -1;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);

  if (!AccountInsertStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "INSERT INTO account "
          "(email_addr, "
          "keyid, "
          "keydata, "
          "prefer_encrypt, "
          "enabled) "
          "VALUES (?, ?, ?, ?, ?);",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &AccountInsertStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (AccountInsertStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (AccountInsertStmt,
                         2,
                         keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (AccountInsertStmt,
                         3,
                         keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (AccountInsertStmt,
                        4,
                        prefer_encrypt) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (AccountInsertStmt,
                        5,
                        1) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (AccountInsertStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (AccountInsertStmt);
  return rv;
}

int mutt_autocrypt_db_account_update (AUTOCRYPT_ACCOUNT *acct)
{
  int rv = -1;

  if (!AccountUpdateStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "UPDATE account SET "
          "keyid = ?, "
          "keydata = ?, "
          "prefer_encrypt = ?, "
          "enabled = ? "
          "WHERE email_addr = ?;",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &AccountUpdateStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (AccountUpdateStmt,
                         1,
                         acct->keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (AccountUpdateStmt,
                         2,
                         acct->keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (AccountUpdateStmt,
                        3,
                        acct->prefer_encrypt) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (AccountUpdateStmt,
                        4,
                        acct->enabled) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (AccountUpdateStmt,
                         5,
                         acct->email_addr,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (AccountUpdateStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  sqlite3_reset (AccountUpdateStmt);
  return rv;
}

int mutt_autocrypt_db_account_delete (AUTOCRYPT_ACCOUNT *acct)
{
  int rv = -1;

  if (!AccountDeleteStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "DELETE from account "
          "WHERE email_addr = ?;",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &AccountDeleteStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (AccountDeleteStmt,
                         1,
                         acct->email_addr,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (AccountDeleteStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  sqlite3_reset (AccountDeleteStmt);
  return rv;
}

int mutt_autocrypt_db_account_get_all (AUTOCRYPT_ACCOUNT ***accounts, int *num_accounts)
{
  int rv = -1, result;
  sqlite3_stmt *stmt = NULL;
  AUTOCRYPT_ACCOUNT **results = NULL, *account;
  int results_len = 0, results_count = 0;

  *accounts = NULL;
  *num_accounts = 0;

  /* Note, speed is not of the essence for the account management screen,
   * so we don't bother with a persistent prepared statement */
  if (sqlite3_prepare_v2 (
        AutocryptDB,
        "SELECT "
        "email_addr, "
        "keyid, "
        "keydata, "
        "prefer_encrypt, "
        "enabled "
        "FROM account "
        "ORDER BY email_addr",
        -1,
        &stmt,
        NULL) != SQLITE_OK)
    goto cleanup;

  while ((result = sqlite3_step (stmt)) == SQLITE_ROW)
  {
    if (results_count == results_len)
    {
      results_len += 5;
      safe_realloc (&results, results_len * sizeof(AUTOCRYPT_ACCOUNT *));
    }

    results[results_count++] = account = mutt_autocrypt_db_account_new ();

    account->email_addr = strdup_column_text (stmt, 0);
    account->keyid = strdup_column_text (stmt, 1);
    account->keydata = strdup_column_text (stmt, 2);
    account->prefer_encrypt = sqlite3_column_int (stmt, 3);
    account->enabled = sqlite3_column_int (stmt, 4);
  }

  if (result == SQLITE_DONE)
  {
    *accounts = results;
    rv = *num_accounts = results_count;
  }
  else
  {
    while (results_count > 0)
      mutt_autocrypt_db_account_free (&results[--results_count]);
    FREE (&results);
  }

cleanup:
  sqlite3_finalize (stmt);
  return rv;
}

AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void)
{
  return safe_calloc (1, sizeof(AUTOCRYPT_PEER));
}

void mutt_autocrypt_db_peer_free (AUTOCRYPT_PEER **peer)
{
  if (!peer || !*peer)
    return;
  FREE (&(*peer)->email_addr);
  FREE (&(*peer)->keyid);
  FREE (&(*peer)->keydata);
  FREE (&(*peer)->gossip_keyid);
  FREE (&(*peer)->gossip_keydata);
  FREE (peer);      /* __FREE_CHECKED__ */
}

int mutt_autocrypt_db_peer_get (ADDRESS *addr, AUTOCRYPT_PEER **peer)
{
  int rv = -1, result;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);
  *peer = NULL;

  if (!PeerGetStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "SELECT "
          "email_addr, "
          "last_seen, "
          "autocrypt_timestamp, "
          "keyid, "
          "keydata, "
          "prefer_encrypt, "
          "gossip_timestamp, "
          "gossip_keyid, "
          "gossip_keydata "
          "FROM peer "
          "WHERE email_addr = ?",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &PeerGetStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (PeerGetStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  result = sqlite3_step (PeerGetStmt);
  if (result != SQLITE_ROW)
  {
    if (result == SQLITE_DONE)
      rv = 0;
    goto cleanup;
  }

  *peer = mutt_autocrypt_db_peer_new ();
  (*peer)->email_addr = strdup_column_text (PeerGetStmt, 0);
  (*peer)->last_seen = sqlite3_column_int64 (PeerGetStmt, 1);
  (*peer)->autocrypt_timestamp = sqlite3_column_int64 (PeerGetStmt, 2);
  (*peer)->keyid = strdup_column_text (PeerGetStmt, 3);
  (*peer)->keydata = strdup_column_text (PeerGetStmt, 4);
  (*peer)->prefer_encrypt = sqlite3_column_int (PeerGetStmt, 5);
  (*peer)->gossip_timestamp = sqlite3_column_int64 (PeerGetStmt, 6);
  (*peer)->gossip_keyid = strdup_column_text (PeerGetStmt, 7);
  (*peer)->gossip_keydata = strdup_column_text (PeerGetStmt, 8);

  rv = 1;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (PeerGetStmt);
  return rv;
}

int mutt_autocrypt_db_peer_insert (ADDRESS *addr, AUTOCRYPT_PEER *peer)
{
  int rv = -1;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);

  if (!PeerInsertStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "INSERT INTO peer "
          "(email_addr, "
          "last_seen, "
          "autocrypt_timestamp, "
          "keyid, "
          "keydata, "
          "prefer_encrypt, "
          "gossip_timestamp, "
          "gossip_keyid, "
          "gossip_keydata) "
          "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &PeerInsertStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (PeerInsertStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerInsertStmt,
                          2,
                          peer->last_seen) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerInsertStmt,
                          3,
                          peer->autocrypt_timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerInsertStmt,
                         4,
                         peer->keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerInsertStmt,
                         5,
                         peer->keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (PeerInsertStmt,
                        6,
                        peer->prefer_encrypt) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerInsertStmt,
                          7,
                          peer->gossip_timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerInsertStmt,
                         8,
                         peer->gossip_keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerInsertStmt,
                         9,
                         peer->gossip_keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (PeerInsertStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (PeerInsertStmt);
  return rv;
}

int mutt_autocrypt_db_peer_update (AUTOCRYPT_PEER *peer)
{
  int rv = -1;

  if (!PeerUpdateStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "UPDATE peer SET "
          "last_seen = ?, "
          "autocrypt_timestamp = ?, "
          "keyid = ?, "
          "keydata = ?, "
          "prefer_encrypt = ?, "
          "gossip_timestamp = ?, "
          "gossip_keyid = ?, "
          "gossip_keydata = ? "
          "WHERE email_addr = ?;",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &PeerUpdateStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_int64 (PeerUpdateStmt,
                          1,
                          peer->last_seen) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerUpdateStmt,
                          2,
                          peer->autocrypt_timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerUpdateStmt,
                         3,
                         peer->keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerUpdateStmt,
                         4,
                         peer->keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int (PeerUpdateStmt,
                        5,
                        peer->prefer_encrypt) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerUpdateStmt,
                          6,
                          peer->gossip_timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerUpdateStmt,
                         7,
                         peer->gossip_keyid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerUpdateStmt,
                         8,
                         peer->gossip_keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerUpdateStmt,
                         9,
                         peer->email_addr,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (PeerUpdateStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  sqlite3_reset (PeerUpdateStmt);
  return rv;
}

AUTOCRYPT_PEER_HISTORY *mutt_autocrypt_db_peer_history_new (void)
{
  return safe_calloc (1, sizeof(AUTOCRYPT_PEER_HISTORY));
}

void mutt_autocrypt_db_peer_history_free (AUTOCRYPT_PEER_HISTORY **peerhist)
{
  if (!peerhist || !*peerhist)
    return;
  FREE (&(*peerhist)->peer_email_addr);
  FREE (&(*peerhist)->email_msgid);
  FREE (&(*peerhist)->keydata);
  FREE (peerhist);      /* __FREE_CHECKED__ */
}

int mutt_autocrypt_db_peer_history_insert (ADDRESS *addr, AUTOCRYPT_PEER_HISTORY *peerhist)
{
  int rv = -1;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);

  if (!PeerHistoryInsertStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "INSERT INTO peer_history "
          "(peer_email_addr, "
          "email_msgid, "
          "timestamp, "
          "keydata) "
          "VALUES (?, ?, ?, ?);",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &PeerHistoryInsertStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (PeerHistoryInsertStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerHistoryInsertStmt,
                         2,
                         peerhist->email_msgid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (PeerHistoryInsertStmt,
                          3,
                          peerhist->timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (PeerHistoryInsertStmt,
                         4,
                         peerhist->keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (PeerHistoryInsertStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (PeerHistoryInsertStmt);
  return rv;
}

AUTOCRYPT_GOSSIP_HISTORY *mutt_autocrypt_db_gossip_history_new (void)
{
  return safe_calloc (1, sizeof(AUTOCRYPT_GOSSIP_HISTORY));
}

void mutt_autocrypt_db_gossip_history_free (AUTOCRYPT_GOSSIP_HISTORY **gossip_hist)
{
  if (!gossip_hist || !*gossip_hist)
    return;
  FREE (&(*gossip_hist)->peer_email_addr);
  FREE (&(*gossip_hist)->sender_email_addr);
  FREE (&(*gossip_hist)->email_msgid);
  FREE (&(*gossip_hist)->gossip_keydata);
  FREE (gossip_hist);      /* __FREE_CHECKED__ */
}

int mutt_autocrypt_db_gossip_history_insert (ADDRESS *addr, AUTOCRYPT_GOSSIP_HISTORY *gossip_hist)
{
  int rv = -1;
  ADDRESS *norm_addr = NULL;

  norm_addr = copy_normalize_addr (addr);

  if (!GossipHistoryInsertStmt)
  {
    if (sqlite3_prepare_v3 (
          AutocryptDB,
          "INSERT INTO gossip_history "
          "(peer_email_addr, "
          "sender_email_addr, "
          "email_msgid, "
          "timestamp, "
          "gossip_keydata) "
          "VALUES (?, ?, ?, ?, ?);",
          -1,
          SQLITE_PREPARE_PERSISTENT,
          &GossipHistoryInsertStmt,
          NULL) != SQLITE_OK)
      goto cleanup;
  }

  if (sqlite3_bind_text (GossipHistoryInsertStmt,
                         1,
                         norm_addr->mailbox,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (GossipHistoryInsertStmt,
                         2,
                         gossip_hist->sender_email_addr,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
  if (sqlite3_bind_text (GossipHistoryInsertStmt,
                         3,
                         gossip_hist->email_msgid,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_int64 (GossipHistoryInsertStmt,
                          4,
                          gossip_hist->timestamp) != SQLITE_OK)
    goto cleanup;
  if (sqlite3_bind_text (GossipHistoryInsertStmt,
                         5,
                         gossip_hist->gossip_keydata,
                         -1,
                         SQLITE_STATIC) != SQLITE_OK)
    goto cleanup;

  if (sqlite3_step (GossipHistoryInsertStmt) != SQLITE_DONE)
    goto cleanup;

  rv = 0;

cleanup:
  rfc822_free_address (&norm_addr);
  sqlite3_reset (GossipHistoryInsertStmt);
  return rv;
}
