Add missing title to foldersel_folder_sel calls
[claws-mail-maildir-plugin.git] / src / uiddb.c
1 /*
2  * Maildir Plugin -- Maildir++ support for Sylpheed
3  * Copyright (C) 2003-2004 Christoph Hohmann
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <glib.h>
21 #include <db.h>
22 #include <stdlib.h>
23
24 #include "utils.h"
25 #include "uiddb.h"
26
27 struct _UIDDB
28 {
29         DB      *db_uid;
30         DB      *db_uniq;
31         guint32  lastuid;
32 };
33
34 static gboolean initialized = FALSE;
35 static DB_ENV *dbenv;
36
37 void uiddb_init()
38 {
39         db_env_create(&dbenv, 0);
40         dbenv->open(dbenv, get_tmp_dir(), DB_INIT_MPOOL | DB_INIT_CDB | DB_CREATE, 0600);
41
42         initialized = TRUE;
43 }
44
45 void uiddb_done()
46 {
47         dbenv->close(dbenv, 0);
48
49         initialized = FALSE;
50 }
51
52 int get_secondary_key(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey)
53 {
54         gchar *uniq;
55
56         memset(skey, 0, sizeof(DBT));
57
58         uniq = pdata->data + sizeof(guint32);
59         skey->data = uniq;
60         skey->size = strlen(uniq);
61
62         return 0;
63 }
64
65 UIDDB *uiddb_open(const gchar *dbfile)
66 {
67         gint     ret;
68         DB      *db_uid, *db_uniq;
69         UIDDB   *uiddb;
70
71         g_return_val_if_fail(initialized, NULL);
72
73         /* open uid key based database */
74         if ((ret = db_create(&db_uid, dbenv, 0)) != 0) {
75                 debug_print("db_create: %s\n", db_strerror(ret));
76                 return NULL;
77         }
78         if ((ret = db_uid->open(db_uid, NULL, dbfile, "uidkey", DB_BTREE, DB_CREATE, 0600)) != 0) {
79                 debug_print("DB->open: %s\n", db_strerror(ret));
80                 db_uid->close(db_uid, 0);
81                 return NULL;
82         }
83         debug_print("UID based database opened\n");
84
85         /* open uniq key based database */
86         if ((ret = db_create(&db_uniq, dbenv, 0)) != 0) {
87                 debug_print("db_create: %s\n", db_strerror(ret));
88                 db_uid->close(db_uid, 0);
89                 return NULL;
90         }
91         if ((ret = db_uniq->open(db_uniq, NULL, dbfile, "uniqkey", DB_BTREE, DB_CREATE, 0600)) != 0) {
92                 debug_print("DB->open: %s\n", db_strerror(ret));
93                 db_uniq->close(db_uniq, 0);
94                 db_uid->close(db_uid, 0);
95                 return NULL;
96         }
97         debug_print("Uniq based database opened\n");
98
99         if ((ret = db_uid->associate(db_uid, NULL, db_uniq, get_secondary_key, 0)) != 0) {
100                 debug_print("DB->associate: %s\n", db_strerror(ret));
101                 db_uid->close(db_uid, 0);
102                 db_uniq->close(db_uniq, 0);
103                 return NULL;
104         }
105         debug_print("Databases associated\n");
106
107         uiddb = g_new0(UIDDB, 1);
108         uiddb->db_uid = db_uid;
109         uiddb->db_uniq = db_uniq;
110         uiddb->lastuid = 0;
111
112         return uiddb;
113 }
114
115 void uiddb_close(UIDDB *uiddb)
116 {
117         g_return_if_fail(uiddb != NULL);
118
119         if (uiddb->db_uid != NULL)
120                 uiddb->db_uid->close(uiddb->db_uid, 0);
121         if (uiddb->db_uniq != NULL)
122                 uiddb->db_uniq->close(uiddb->db_uniq, 0);
123 }
124
125 void uiddb_free_msgdata(MessageData *msgdata)
126 {
127         g_free(msgdata->uniq);
128         g_free(msgdata->info);
129         g_free(msgdata->dir);
130         g_free(msgdata);
131 }
132
133 static DBT marshal(MessageData *msgdata)
134 {
135         DBT dbt;
136         gpointer ptr;
137
138         memset(&dbt, 0, sizeof(dbt));
139         dbt.size = sizeof(msgdata->uid) + \
140                    strlen(msgdata->uniq) + 1 + \
141                    strlen(msgdata->info) + 1 + \
142                    strlen(msgdata->dir) + 1;
143         dbt.data = g_malloc0(dbt.size);
144
145         ptr = dbt.data;
146
147 #define ADD_DATA(dataptr, size) \
148 { \
149         memcpy(ptr, dataptr, size); \
150         ptr += size; \
151 }
152
153         ADD_DATA(&msgdata->uid, sizeof(msgdata->uid));
154         ADD_DATA(msgdata->uniq, strlen(msgdata->uniq) + 1);
155         ADD_DATA(msgdata->info, strlen(msgdata->info) + 1);
156         ADD_DATA(msgdata->dir, strlen(msgdata->dir) + 1);
157
158 #undef ADD_DATA 
159
160         return dbt;
161 }
162
163 static MessageData *unmarshal(DBT dbt)
164 {
165         gpointer ptr;
166         MessageData *msgdata;
167
168         ptr = dbt.data;
169         msgdata = g_new0(MessageData, 1);
170
171         memcpy(&msgdata->uid, ptr, sizeof(msgdata->uid));
172         ptr += sizeof(msgdata->uid);
173         msgdata->uniq = g_strdup(ptr);
174         ptr += strlen(ptr) + 1;
175         msgdata->info = g_strdup(ptr);
176         ptr += strlen(ptr) + 1;
177         msgdata->dir = g_strdup(ptr);
178
179         return msgdata;
180 }
181
182 guint32 uiddb_get_new_uid(UIDDB *uiddb)
183 {
184         DBC *cursor;
185         DBT key, data;
186         gint ret;
187         guint32 uid, lastuid = -1;
188
189         g_return_val_if_fail(uiddb != NULL, 0);
190
191         lastuid = uiddb->lastuid;
192
193         if (uiddb->lastuid > 0)
194                 return ++uiddb->lastuid;
195
196         ret = uiddb->db_uid->cursor(uiddb->db_uid, NULL, &cursor, 0);
197         if (ret != 0) {
198                 debug_print("DB->cursor: %s\n", db_strerror(ret));
199                 return -1;
200         }
201
202         memset(&key, 0, sizeof(key));
203         memset(&data, 0, sizeof(data));
204         while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
205                 uid = *((guint32 *) key.data);
206
207                 if (uid > lastuid)
208                         lastuid = uid;          
209
210                 memset(&key, 0, sizeof(key));
211                 memset(&data, 0, sizeof(data));
212         }
213
214         cursor->c_close(cursor);
215
216         uiddb->lastuid = ++lastuid;
217         return lastuid;
218 }
219
220 MessageData *uiddb_get_entry_for_uid(UIDDB *uiddb, guint32 uid)
221 {
222         DBT key, data;
223
224         g_return_val_if_fail(uiddb, NULL);
225
226         memset(&key, 0, sizeof(key));
227         memset(&data, 0, sizeof(data));
228
229         key.size = sizeof(guint32);
230         key.data = &uid;
231
232         if (uiddb->db_uid->get(uiddb->db_uid, NULL, &key, &data, 0) != 0)
233                 return NULL;
234
235         return unmarshal(data);
236 }
237
238 MessageData *uiddb_get_entry_for_uniq(UIDDB *uiddb, gchar *uniq)
239 {
240         DBT key, pkey, data;
241
242         g_return_val_if_fail(uiddb, NULL);
243
244         memset(&key, 0, sizeof(key));
245         memset(&pkey, 0, sizeof(pkey));
246         memset(&data, 0, sizeof(data));
247
248         key.size = strlen(uniq);
249         key.data = uniq;
250
251         if (uiddb->db_uniq->pget(uiddb->db_uniq, NULL, &key, &pkey, &data, 0) != 0)
252                 return NULL;
253
254         return unmarshal(data);
255 }
256
257 void uiddb_delete_entry(UIDDB *uiddb, guint32 uid)
258 {
259         DBT key;
260
261         g_return_if_fail(uiddb);
262
263         memset(&key, 0, sizeof(key));
264
265         key.size = sizeof(guint32);
266         key.data = &uid;
267
268         uiddb->db_uid->del(uiddb->db_uid, NULL, &key, 0);
269 }
270
271 void uiddb_insert_entry(UIDDB *uiddb, MessageData *msgdata)
272 {
273         DBT key, data;
274         gint ret;
275
276         g_return_if_fail(uiddb);
277
278         memset(&key, 0, sizeof(key));
279         memset(&data, 0, sizeof(data));
280
281         key.size = sizeof(guint32);
282         key.data = &msgdata->uid;
283
284         data = marshal(msgdata);
285
286         ret = uiddb->db_uid->put(uiddb->db_uid, NULL, &key, &data, 0);
287         if (ret != 0)
288                 debug_print("DB->put: %s\n", db_strerror(ret));
289
290         g_free(data.data);
291 }
292
293 static int uiddb_uid_compare(const void *a, const void *b)
294 {
295     return *(guint32*)a - *(guint32*)b;
296 }
297
298 void uiddb_delete_entries_not_in_list(UIDDB *uiddb, MsgNumberList *list)
299 {
300         DBC *cursor;
301         DBT key, data;
302         gint i, uidcnt, ret;
303         guint32 *uid_sorted;
304
305         g_return_if_fail(uiddb);
306         if (list == NULL)
307                 return;
308
309         ret = uiddb->db_uid->cursor(uiddb->db_uid, NULL, &cursor, DB_WRITECURSOR);
310         if (ret != 0) {
311                 debug_print("DB->cursor: %s\n", db_strerror(ret));
312                 return;
313         }
314
315         uidcnt = g_slist_length(list);
316         uid_sorted = g_new(guint32, uidcnt);
317         for (i = 0; i < uidcnt; i++) {
318             uid_sorted[i] = GPOINTER_TO_INT(list->data);
319             list = g_slist_next(list);
320         }
321         
322         memset(&key, 0, sizeof(key));
323         memset(&data, 0, sizeof(data));
324         while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
325                 guint32 uid = *((guint32 *) key.data);
326
327                 if (bsearch(&uid, uid_sorted, uidcnt, sizeof(guint32), &uiddb_uid_compare) == NULL)
328                         cursor->c_del(cursor, 0);
329
330                 memset(&key, 0, sizeof(key));
331                 memset(&data, 0, sizeof(data));
332         }
333
334         g_free(uid_sorted);
335
336         cursor->c_close(cursor);
337 }