updated changelog
[platform/upstream/evolution-data-server.git] / libebackend / e-dbhash.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Author:
4  *   JP Rosevear (jpr@ximian.com)
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  */
8
9 /**
10  * SECTION: e-dbhash
11  * @short_description: Simple DB-based hash table for strings
12  *
13  * An #EDbHash is a simple hash table of strings backed by a Berkeley DB
14  * file for permanent storage.
15  **/
16
17 #include <config.h>
18
19 #include "e-dbhash.h"
20
21 #include <string.h>
22 #include <fcntl.h>
23 #include "db.h"
24
25 struct _EDbHashPrivate {
26         DB *db;
27 };
28
29 /**
30  * e_dbhash_new:
31  * @filename: path to a Berkeley DB file
32  *
33  * Creates a new #EDbHash structure and opens the given Berkeley DB file,
34  * creating the DB file if necessary.
35  *
36  * Returns: a new #EDbHash
37  **/
38 EDbHash *
39 e_dbhash_new (const gchar *filename)
40 {
41         EDbHash *edbh;
42         DB *db;
43         gint rv;
44
45         /* Attempt to open the database */
46         rv = db_create (&db, NULL, 0);
47         if (rv != 0) {
48                 return NULL;
49         }
50
51         rv = (*db->open) (db, NULL, filename, NULL, DB_HASH, 0, 0666);
52         if (rv != 0) {
53                 /* Close and re-create the db handle to avoid memory leak */
54                 db->close (db, 0);
55                 rv = db_create (&db, NULL, 0);
56                 if (rv != 0) {
57                         return NULL;
58                 }
59
60                 rv = (*db->open) (
61                         db, NULL, filename, NULL, DB_HASH, DB_CREATE, 0666);
62
63                 if (rv != 0) {
64                         db->close (db, 0);
65                         return NULL;
66                 }
67         }
68
69         edbh = g_new (EDbHash, 1);
70         edbh->priv = g_new (EDbHashPrivate, 1);
71         edbh->priv->db = db;
72
73         return edbh;
74 }
75
76 static void
77 string_to_dbt (const gchar *str,
78                DBT *dbt)
79 {
80         memset (dbt, 0, sizeof (DBT));
81         dbt->data = (gpointer) str;
82         dbt->size = strlen (str) + 1;
83 }
84
85 static void
86 md5_to_dbt (const guint8 str[16],
87             DBT *dbt)
88 {
89         memset (dbt, 0, sizeof (DBT));
90         dbt->data = (gpointer) str;
91         dbt->size = 16;
92 }
93
94 /**
95  * e_dbhash_add:
96  * @edbh: an #EDbHash
97  * @key: a database key
98  * @data: a database object for @key
99  *
100  * Adds a database object for @key.
101  **/
102 void
103 e_dbhash_add (EDbHash *edbh,
104               const gchar *key,
105               const gchar *data)
106 {
107         DB *db;
108         DBT dkey;
109         DBT ddata;
110         GChecksum *checksum;
111         guint8 *digest;
112         gsize length;
113
114         g_return_if_fail (edbh != NULL);
115         g_return_if_fail (edbh->priv != NULL);
116         g_return_if_fail (edbh->priv->db != NULL);
117         g_return_if_fail (key != NULL);
118         g_return_if_fail (data != NULL);
119
120         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
121         digest = g_alloca (length);
122
123         db = edbh->priv->db;
124
125         /* Key dbt */
126         string_to_dbt (key, &dkey);
127
128         /* Compute MD5 checksum */
129         checksum = g_checksum_new (G_CHECKSUM_MD5);
130         g_checksum_update (checksum, (guchar *) data, -1);
131         g_checksum_get_digest (checksum, digest, &length);
132         g_checksum_free (checksum);
133
134         /* Data dbt */
135         md5_to_dbt (digest, &ddata);
136
137         /* Add to database */
138         db->put (db, NULL, &dkey, &ddata, 0);
139 }
140
141 /**
142  * e_dbhash_remove:
143  * @edbh: an #EDbHash
144  * @key: a database key
145  *
146  * Removes the database object corresponding to @key.
147  **/
148 void
149 e_dbhash_remove (EDbHash *edbh,
150                  const gchar *key)
151 {
152         DB *db;
153         DBT dkey;
154
155         g_return_if_fail (edbh != NULL);
156         g_return_if_fail (edbh->priv != NULL);
157         g_return_if_fail (key != NULL);
158
159         db = edbh->priv->db;
160
161         /* Key dbt */
162         string_to_dbt (key, &dkey);
163
164         /* Remove from database */
165         db->del (db, NULL, &dkey, 0);
166 }
167
168 /**
169  * e_dbhash_foreach_key:
170  * @edbh: an #EDbHash
171  * @func: a callback function
172  * @user_data: data to pass to @func
173  *
174  * Calls @func for each database object.
175  **/
176 void
177 e_dbhash_foreach_key (EDbHash *edbh,
178                       EDbHashFunc func,
179                       gpointer user_data)
180 {
181         DB *db;
182         DBT dkey;
183         DBT ddata;
184         DBC *dbc;
185         gint db_error = 0;
186
187         g_return_if_fail (edbh != NULL);
188         g_return_if_fail (edbh->priv != NULL);
189         g_return_if_fail (func != NULL);
190
191         db = edbh->priv->db;
192
193         db_error = db->cursor (db, NULL, &dbc, 0);
194
195         if (db_error != 0) {
196                 return;
197         }
198
199         memset (&dkey, 0, sizeof (DBT));
200         memset (&ddata, 0, sizeof (DBT));
201         db_error = dbc->c_get (dbc, &dkey, &ddata, DB_FIRST);
202
203         while (db_error == 0) {
204                 (*func) ((const gchar *) dkey.data, user_data);
205
206                 db_error = dbc->c_get (dbc, &dkey, &ddata, DB_NEXT);
207         }
208         dbc->c_close (dbc);
209 }
210
211 /**
212  * e_dbhash_compare:
213  * @edbh: an #EDbHash
214  * @key: a database key
215  * @compare_data: data to compare against the database
216  *
217  * Compares @compare_data to the database object corresponding to
218  * @key using an MD5 checksum.  Returns #E_DBHASH_STATUS_SAME if the
219  * checksums match, #E_DBHASH_STATUS_DIFFERENT if the checksums differ,
220  * or #E_DBHASH_STATUS_NOT_FOUND if @key is not present in the database.
221  *
222  * Returns: a checksum comparison status
223  **/
224 EDbHashStatus
225 e_dbhash_compare (EDbHash *edbh,
226                   const gchar *key,
227                   const gchar *compare_data)
228 {
229         DB *db;
230         DBT dkey;
231         DBT ddata;
232         guint8 compare_hash[16];
233         gsize length = sizeof (compare_hash);
234
235         g_return_val_if_fail (edbh != NULL, FALSE);
236         g_return_val_if_fail (edbh->priv != NULL, FALSE);
237         g_return_val_if_fail (key != NULL, FALSE);
238         g_return_val_if_fail (compare_hash != NULL, FALSE);
239
240         db = edbh->priv->db;
241
242         /* Key dbt */
243         string_to_dbt (key, &dkey);
244
245         /* Lookup in database */
246         memset (&ddata, 0, sizeof (DBT));
247         db->get (db, NULL, &dkey, &ddata, 0);
248
249         /* Compare */
250         if (ddata.data) {
251                 GChecksum *checksum;
252
253                 checksum = g_checksum_new (G_CHECKSUM_MD5);
254                 g_checksum_update (checksum, (guchar *) compare_data, -1);
255                 g_checksum_get_digest (checksum, compare_hash, &length);
256                 g_checksum_free (checksum);
257
258                 if (memcmp (ddata.data, compare_hash, sizeof (guchar) * 16))
259                         return E_DBHASH_STATUS_DIFFERENT;
260         } else {
261                 return E_DBHASH_STATUS_NOT_FOUND;
262         }
263
264         return E_DBHASH_STATUS_SAME;
265 }
266
267 /**
268  * e_dbhash_write:
269  * @edbh: an #EDbHash
270  *
271  * Flushes database changes to disk.
272  **/
273 void
274 e_dbhash_write (EDbHash *edbh)
275 {
276         DB *db;
277
278         g_return_if_fail (edbh != NULL);
279         g_return_if_fail (edbh->priv != NULL);
280
281         db = edbh->priv->db;
282
283         /* Flush database to disk */
284         db->sync (db, 0);
285 }
286
287 /**
288  * e_dbhash_destroy:
289  * @edbh: an #EDbHash
290  *
291  * Closes the database file and frees the #EDbHash.
292  **/
293 void
294 e_dbhash_destroy (EDbHash *edbh)
295 {
296         DB *db;
297
298         g_return_if_fail (edbh != NULL);
299         g_return_if_fail (edbh->priv != NULL);
300
301         db = edbh->priv->db;
302
303         /* Close datbase */
304         db->close (db, 0);
305
306         g_free (edbh->priv);
307         g_free (edbh);
308 }