/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors: JP Rosevear (jpr@ximian.com)
*/
/**
* SECTION: e-dbhash
* @short_description: Simple DB-based hash table for strings
*
* An #EDbHash is a simple hash table of strings backed by a Berkeley DB
* file for permanent storage.
**/
#include
#include "e-dbhash.h"
#include
#include
#include "db.h"
struct _EDbHashPrivate {
DB *db;
};
/**
* e_dbhash_new:
* @filename: path to a Berkeley DB file
*
* Creates a new #EDbHash structure and opens the given Berkeley DB file,
* creating the DB file if necessary.
*
* Returns: a new #EDbHash
**/
EDbHash *
e_dbhash_new (const gchar *filename)
{
EDbHash *edbh;
DB *db;
gint rv;
/* Attempt to open the database */
rv = db_create (&db, NULL, 0);
if (rv != 0) {
return NULL;
}
rv = (*db->open) (db, NULL, filename, NULL, DB_HASH, 0, 0666);
if (rv != 0) {
/* Close and re-create the db handle to avoid memory leak */
db->close (db, 0);
rv = db_create (&db, NULL, 0);
if (rv != 0) {
return NULL;
}
rv = (*db->open) (
db, NULL, filename, NULL, DB_HASH, DB_CREATE, 0666);
if (rv != 0) {
db->close (db, 0);
return NULL;
}
}
edbh = g_new (EDbHash, 1);
edbh->priv = g_new (EDbHashPrivate, 1);
edbh->priv->db = db;
return edbh;
}
static void
string_to_dbt (const gchar *str,
DBT *dbt)
{
memset (dbt, 0, sizeof (DBT));
dbt->data = (gpointer) str;
dbt->size = strlen (str) + 1;
}
static void
md5_to_dbt (const guint8 str[16],
DBT *dbt)
{
memset (dbt, 0, sizeof (DBT));
dbt->data = (gpointer) str;
dbt->size = 16;
}
/**
* e_dbhash_add:
* @edbh: an #EDbHash
* @key: a database key
* @data: a database object for @key
*
* Adds a database object for @key.
**/
void
e_dbhash_add (EDbHash *edbh,
const gchar *key,
const gchar *data)
{
DB *db;
DBT dkey;
DBT ddata;
GChecksum *checksum;
guint8 *digest;
gsize length;
g_return_if_fail (edbh != NULL);
g_return_if_fail (edbh->priv != NULL);
g_return_if_fail (edbh->priv->db != NULL);
g_return_if_fail (key != NULL);
g_return_if_fail (data != NULL);
length = g_checksum_type_get_length (G_CHECKSUM_MD5);
digest = g_alloca (length);
db = edbh->priv->db;
/* Key dbt */
string_to_dbt (key, &dkey);
/* Compute MD5 checksum */
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (guchar *) data, -1);
g_checksum_get_digest (checksum, digest, &length);
g_checksum_free (checksum);
/* Data dbt */
md5_to_dbt (digest, &ddata);
/* Add to database */
db->put (db, NULL, &dkey, &ddata, 0);
}
/**
* e_dbhash_remove:
* @edbh: an #EDbHash
* @key: a database key
*
* Removes the database object corresponding to @key.
**/
void
e_dbhash_remove (EDbHash *edbh,
const gchar *key)
{
DB *db;
DBT dkey;
g_return_if_fail (edbh != NULL);
g_return_if_fail (edbh->priv != NULL);
g_return_if_fail (key != NULL);
db = edbh->priv->db;
/* Key dbt */
string_to_dbt (key, &dkey);
/* Remove from database */
db->del (db, NULL, &dkey, 0);
}
/**
* e_dbhash_foreach_key:
* @edbh: an #EDbHash
* @func: a callback function
* @user_data: data to pass to @func
*
* Calls @func for each database object.
**/
void
e_dbhash_foreach_key (EDbHash *edbh,
EDbHashFunc func,
gpointer user_data)
{
DB *db;
DBT dkey;
DBT ddata;
DBC *dbc;
gint db_error = 0;
g_return_if_fail (edbh != NULL);
g_return_if_fail (edbh->priv != NULL);
g_return_if_fail (func != NULL);
db = edbh->priv->db;
db_error = db->cursor (db, NULL, &dbc, 0);
if (db_error != 0) {
return;
}
memset (&dkey, 0, sizeof (DBT));
memset (&ddata, 0, sizeof (DBT));
db_error = dbc->c_get (dbc, &dkey, &ddata, DB_FIRST);
while (db_error == 0) {
(*func) ((const gchar *) dkey.data, user_data);
db_error = dbc->c_get (dbc, &dkey, &ddata, DB_NEXT);
}
dbc->c_close (dbc);
}
/**
* e_dbhash_compare:
* @edbh: an #EDbHash
* @key: a database key
* @compare_data: data to compare against the database
*
* Compares @compare_data to the database object corresponding to
* @key using an MD5 checksum. Returns #E_DBHASH_STATUS_SAME if the
* checksums match, #E_DBHASH_STATUS_DIFFERENT if the checksums differ,
* or #E_DBHASH_STATUS_NOT_FOUND if @key is not present in the database.
*
* Returns: a checksum comparison status
**/
EDbHashStatus
e_dbhash_compare (EDbHash *edbh,
const gchar *key,
const gchar *compare_data)
{
DB *db;
DBT dkey;
DBT ddata;
guint8 compare_hash[16];
gsize length = sizeof (compare_hash);
g_return_val_if_fail (edbh != NULL, FALSE);
g_return_val_if_fail (edbh->priv != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (compare_hash != NULL, FALSE);
db = edbh->priv->db;
/* Key dbt */
string_to_dbt (key, &dkey);
/* Lookup in database */
memset (&ddata, 0, sizeof (DBT));
db->get (db, NULL, &dkey, &ddata, 0);
/* Compare */
if (ddata.data) {
GChecksum *checksum;
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (guchar *) compare_data, -1);
g_checksum_get_digest (checksum, compare_hash, &length);
g_checksum_free (checksum);
if (memcmp (ddata.data, compare_hash, sizeof (guchar) * 16))
return E_DBHASH_STATUS_DIFFERENT;
} else {
return E_DBHASH_STATUS_NOT_FOUND;
}
return E_DBHASH_STATUS_SAME;
}
/**
* e_dbhash_write:
* @edbh: an #EDbHash
*
* Flushes database changes to disk.
**/
void
e_dbhash_write (EDbHash *edbh)
{
DB *db;
g_return_if_fail (edbh != NULL);
g_return_if_fail (edbh->priv != NULL);
db = edbh->priv->db;
/* Flush database to disk */
db->sync (db, 0);
}
/**
* e_dbhash_destroy:
* @edbh: an #EDbHash
*
* Closes the database file and frees the #EDbHash.
**/
void
e_dbhash_destroy (EDbHash *edbh)
{
DB *db;
g_return_if_fail (edbh != NULL);
g_return_if_fail (edbh->priv != NULL);
db = edbh->priv->db;
/* Close datbase */
db->close (db, 0);
g_free (edbh->priv);
g_free (edbh);
}