2 * Maildir Plugin -- Maildir++ support for Claws Mail
3 * Copyright (C) 2003-2004 Christoph Hohmann
4 * Copyright (C) 2017-2018 Ricardo Mones and the Claws Mail team
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 static gboolean initialized = FALSE;
40 int flags = DB_INIT_MPOOL | DB_INIT_CDB | DB_CREATE;
42 if ((ret = db_env_create(&dbenv, 0)) != 0) {
43 g_warning("db_env_create: %s", db_strerror(ret));
46 if ((ret = dbenv->open(dbenv, get_tmp_dir(), flags, 0600)) != 0) {
47 if (ret != DB_RUNRECOVERY) {
48 g_warning("dbenv->open: %s", db_strerror(ret));
52 if ((ret = dbenv->open(dbenv, get_tmp_dir(), flags, 0600)) != 0) {
53 g_warning("dbenv->open (recovering): %s", db_strerror(ret));
63 dbenv->close(dbenv, 0);
68 int get_secondary_key(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey)
72 memset(skey, 0, sizeof(DBT));
74 uniq = pdata->data + sizeof(guint32);
76 skey->size = strlen(uniq);
81 UIDDB *uiddb_open(const gchar *dbfile)
87 g_return_val_if_fail(initialized, NULL);
89 /* open uid key based database */
90 if ((ret = db_create(&db_uid, dbenv, 0)) != 0) {
91 debug_print("db_create: %s\n", db_strerror(ret));
94 if ((ret = db_uid->open(db_uid, NULL, dbfile, "uidkey", DB_BTREE, DB_CREATE, 0600)) != 0) {
95 debug_print("DB->open: %s\n", db_strerror(ret));
96 db_uid->close(db_uid, 0);
99 debug_print("UID based database opened\n");
101 /* open uniq key based database */
102 if ((ret = db_create(&db_uniq, dbenv, 0)) != 0) {
103 debug_print("db_create: %s\n", db_strerror(ret));
104 db_uid->close(db_uid, 0);
107 if ((ret = db_uniq->open(db_uniq, NULL, dbfile, "uniqkey", DB_BTREE, DB_CREATE, 0600)) != 0) {
108 debug_print("DB->open: %s\n", db_strerror(ret));
109 db_uniq->close(db_uniq, 0);
110 db_uid->close(db_uid, 0);
113 debug_print("Uniq based database opened\n");
115 if ((ret = db_uid->associate(db_uid, NULL, db_uniq, get_secondary_key, 0)) != 0) {
116 debug_print("DB->associate: %s\n", db_strerror(ret));
117 db_uid->close(db_uid, 0);
118 db_uniq->close(db_uniq, 0);
121 debug_print("Databases associated\n");
123 uiddb = g_new0(UIDDB, 1);
124 uiddb->db_uid = db_uid;
125 uiddb->db_uniq = db_uniq;
131 void uiddb_close(UIDDB *uiddb)
133 g_return_if_fail(uiddb != NULL);
135 if (uiddb->db_uid != NULL)
136 uiddb->db_uid->close(uiddb->db_uid, 0);
137 if (uiddb->db_uniq != NULL)
138 uiddb->db_uniq->close(uiddb->db_uniq, 0);
141 void uiddb_free_msgdata(MessageData *msgdata)
143 g_free(msgdata->uniq);
144 g_free(msgdata->info);
145 g_free(msgdata->dir);
149 static DBT marshal(MessageData *msgdata)
154 memset(&dbt, 0, sizeof(dbt));
155 dbt.size = sizeof(msgdata->uid) + \
156 strlen(msgdata->uniq) + 1 + \
157 strlen(msgdata->info) + 1 + \
158 strlen(msgdata->dir) + 1;
159 dbt.data = g_malloc0(dbt.size);
163 #define ADD_DATA(dataptr, size) \
165 memcpy(ptr, dataptr, size); \
169 ADD_DATA(&msgdata->uid, sizeof(msgdata->uid));
170 ADD_DATA(msgdata->uniq, strlen(msgdata->uniq) + 1);
171 ADD_DATA(msgdata->info, strlen(msgdata->info) + 1);
172 ADD_DATA(msgdata->dir, strlen(msgdata->dir) + 1);
179 static MessageData *unmarshal(DBT dbt)
182 MessageData *msgdata;
185 msgdata = g_new0(MessageData, 1);
187 memcpy(&msgdata->uid, ptr, sizeof(msgdata->uid));
188 ptr += sizeof(msgdata->uid);
189 msgdata->uniq = g_strdup(ptr);
190 ptr += strlen(ptr) + 1;
191 msgdata->info = g_strdup(ptr);
192 ptr += strlen(ptr) + 1;
193 msgdata->dir = g_strdup(ptr);
198 guint32 uiddb_get_new_uid(UIDDB *uiddb)
203 guint32 uid, lastuid = -1;
205 g_return_val_if_fail(uiddb != NULL, 0);
207 lastuid = uiddb->lastuid;
209 if (uiddb->lastuid > 0)
210 return ++uiddb->lastuid;
212 ret = uiddb->db_uid->cursor(uiddb->db_uid, NULL, &cursor, 0);
214 debug_print("DB->cursor: %s\n", db_strerror(ret));
218 memset(&key, 0, sizeof(key));
219 memset(&data, 0, sizeof(data));
220 while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
221 uid = *((guint32 *) key.data);
226 memset(&key, 0, sizeof(key));
227 memset(&data, 0, sizeof(data));
230 cursor->c_close(cursor);
232 uiddb->lastuid = ++lastuid;
236 MessageData *uiddb_get_entry_for_uid(UIDDB *uiddb, guint32 uid)
240 g_return_val_if_fail(uiddb, NULL);
242 memset(&key, 0, sizeof(key));
243 memset(&data, 0, sizeof(data));
245 key.size = sizeof(guint32);
248 if (uiddb->db_uid->get(uiddb->db_uid, NULL, &key, &data, 0) != 0)
251 return unmarshal(data);
254 MessageData *uiddb_get_entry_for_uniq(UIDDB *uiddb, gchar *uniq)
258 g_return_val_if_fail(uiddb, NULL);
260 memset(&key, 0, sizeof(key));
261 memset(&pkey, 0, sizeof(pkey));
262 memset(&data, 0, sizeof(data));
264 key.size = strlen(uniq);
267 if (uiddb->db_uniq->pget(uiddb->db_uniq, NULL, &key, &pkey, &data, 0) != 0)
270 return unmarshal(data);
273 void uiddb_delete_entry(UIDDB *uiddb, guint32 uid)
277 g_return_if_fail(uiddb);
279 memset(&key, 0, sizeof(key));
281 key.size = sizeof(guint32);
284 uiddb->db_uid->del(uiddb->db_uid, NULL, &key, 0);
287 void uiddb_insert_entry(UIDDB *uiddb, MessageData *msgdata)
292 g_return_if_fail(uiddb);
294 memset(&key, 0, sizeof(key));
295 memset(&data, 0, sizeof(data));
297 key.size = sizeof(guint32);
298 key.data = &msgdata->uid;
300 data = marshal(msgdata);
302 ret = uiddb->db_uid->put(uiddb->db_uid, NULL, &key, &data, 0);
304 debug_print("DB->put: %s\n", db_strerror(ret));
309 static int uiddb_uid_compare(const void *a, const void *b)
311 return *(guint32*)a - *(guint32*)b;
314 void uiddb_delete_entries_not_in_list(UIDDB *uiddb, MsgNumberList *list)
321 g_return_if_fail(uiddb);
325 ret = uiddb->db_uid->cursor(uiddb->db_uid, NULL, &cursor, DB_WRITECURSOR);
327 debug_print("DB->cursor: %s\n", db_strerror(ret));
331 uidcnt = g_slist_length(list);
332 uid_sorted = g_new(guint32, uidcnt);
333 for (i = 0; i < uidcnt; i++) {
334 uid_sorted[i] = GPOINTER_TO_INT(list->data);
335 list = g_slist_next(list);
338 memset(&key, 0, sizeof(key));
339 memset(&data, 0, sizeof(data));
340 while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
341 guint32 uid = *((guint32 *) key.data);
343 if (bsearch(&uid, uid_sorted, uidcnt, sizeof(guint32), &uiddb_uid_compare) == NULL)
344 cursor->c_del(cursor, 0);
346 memset(&key, 0, sizeof(key));
347 memset(&data, 0, sizeof(data));
352 cursor->c_close(cursor);