/* -*- 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); }