tmp
[apps/madmutt.git] / lib-mx / hcache.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 2004 Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
4  * Copyright (C) 2004 Tobias Werth <sitowert@stud.uni-erlangen.de>
5  * Copyright (C) 2004 Brian Fundakowski Feldman <green@FreeBSD.org>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 #include <lib-lib/lib-lib.h>
13
14 #ifdef USE_HCACHE
15
16 #if defined(HAVE_TOKYOCABINET)
17 #include <tcutil.h>
18 #include <tchdb.h>
19 #elif defined(HAVE_GDBM)
20 #include <gdbm.h>
21 #else
22 #error no supported DB library found ?
23 #endif
24
25 #include "charset.h"
26 #include "mutt.h"
27 #include "hcache.h"
28
29 struct hcache_t {
30 #if defined(HAVE_TOKYOCABINET)
31     TCHDB *db;
32 #elif defined(HAVE_GDBM)
33     GDBM_FILE db;
34 #endif
35     char *folder;
36     unsigned int crc;
37 };
38
39 static unsigned int crc32(unsigned int crc, const void *src, ssize_t len)
40 {
41     int i;
42     const unsigned char *p = src;
43
44     while (len--) {
45         crc ^= *p++;
46         for (i = 0; i < 8; i++)
47             crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
48     }
49     return crc;
50 }
51
52 static int generate_crc32(void)
53 {
54     int crc = 0;
55
56     crc = crc32(crc, "madmutt.2007.05.13", m_strlen("madmutt.2007.05.13"));
57 #ifdef HAVE_LANGINFO_H
58     crc = crc32(crc, mod_cset.charset, m_strlen(mod_cset.charset));
59 #endif
60     crc = crc32(crc, "USE_POP",   m_strlen("USE_POP"));
61     crc = crc32(crc, "USE_IMAP",  m_strlen("USE_IMAP"));
62     return crc;
63 }
64
65 static const char *
66 mutt_hcache_per_folder(const char *path, const char *folder)
67 {
68     static char buf[_POSIX_PATH_MAX];
69     struct stat st;
70     int pos;
71
72     if (stat(path, &st) < 0 || !S_ISDIR(st.st_mode)) {
73         return path;
74     }
75
76     pos  = m_strcpy(buf, sizeof(buf), path);
77     pos += m_strputc(buf + pos, sizeof(buf) - pos, '/');
78
79     while (*folder) {
80         if (isalnum((unsigned char)*folder) || *folder == '.') {
81             pos += m_strputc(buf + pos, sizeof(buf) - pos, *folder);
82         } else {
83             pos += m_strputc(buf + pos, sizeof(buf) - pos, '_');
84         }
85         folder++;
86     }
87     pos += m_strcpy(buf + pos, sizeof(buf) - pos, ".hdb");
88     return buf;
89 }
90
91 /* store and restore things {{{ */
92
93 static void dump_int(buffer_t *buf, int i)
94 {
95     buffer_add(buf, &i, sizeof(i));
96 }
97
98 static const void *restore_int(const char *d, int *i)
99 {
100     memcpy(i, d, sizeof(*i));
101     return d + sizeof(*i);
102 }
103
104 static void dump_cstr(buffer_t *buf, const char *s)
105 {
106     int size = 0;
107
108     if (m_strisempty(s)) {
109         dump_int(buf, size);
110         return;
111     }
112
113     size = strlen(s) + 1;
114     dump_int(buf, size);
115     buffer_add(buf, s, size);
116 }
117
118 static const void *restore_cstr(const char *d, char **c)
119 {
120     int size;
121
122     d  = restore_int(d, &size);
123     *c = p_dup(d, size);
124     return d + size;
125 }
126
127 static void dump_address(buffer_t *buf, address_t *a)
128 {
129     int counter = 0, pos = buf->len;
130
131     dump_int(buf, counter);
132
133     for (; a; a = a->next, counter++) {
134         dump_cstr(buf, a->personal);
135         dump_cstr(buf, a->mailbox);
136         dump_int(buf, a->group);
137     }
138
139     memcpy(buf->data + pos, &counter, sizeof(counter));
140 }
141
142 static const void *restore_address(const char *d, address_t **a)
143 {
144     int counter;
145
146     d = restore_int(d, &counter);
147
148     for (; counter > 0; counter--) {
149         *a = address_new();
150         d = restore_cstr(d, &(*a)->personal);
151         d = restore_cstr(d, &(*a)->mailbox);
152         d = restore_int(d, &(*a)->group);
153         a = &(*a)->next;
154     }
155
156     *a = NULL;
157     return d;
158 }
159
160 static void dump_list(buffer_t *buf, string_list_t *l)
161 {
162     int pos = buf->len;
163     int counter = 0;
164
165     dump_int(buf, counter);
166
167     for (; l; l = l->next, counter++) {
168         dump_cstr(buf, l->data);
169     }
170
171     memcpy(buf->data + pos, &counter, sizeof(counter));
172 }
173
174 static const void *restore_list(const char *d, string_list_t **l)
175 {
176     int counter;
177
178     d = restore_int(d, &counter);
179
180     for (; counter > 0; counter--) {
181         *l = string_item_new();
182         d = restore_cstr(d, &(*l)->data);
183         l = &(*l)->next;
184     }
185
186     *l = NULL;
187     return d;
188 }
189
190 static void dump_parameter(buffer_t *buf, parameters_t *p)
191 {
192 #if 0
193     int pos = buf->len, counter = 0;
194
195     dump_int(buf, counter);
196
197     for (; p; p = p->next, counter++) {
198         dump_cstr(buf, p->attribute);
199         dump_cstr(buf, p->value);
200     }
201
202     memcpy(buf->data + pos, &counter, sizeof(counter));
203 #endif
204 }
205
206 static const void *restore_parameter(const char *d, parameters_t *p)
207 {
208     int counter;
209
210     d = restore_int(d, &counter);
211
212     for (; counter > 0; counter--) {
213         char *k, *v;
214         d  = restore_cstr(d, &k);
215         d  = restore_cstr(d, &v);
216         parameter_setval(p, k, v);
217         p_delete(&k);
218         p_delete(&v);
219     }
220
221     return d;
222 }
223
224 static void dump_body(buffer_t *buf, BODY *b)
225 {
226     buffer_add(buf, b, sizeof(*b));
227     dump_cstr(buf, b->xtype);
228     dump_cstr(buf, b->subtype);
229
230     dump_parameter(buf, b->parameter);
231
232     dump_cstr(buf, b->description);
233     dump_cstr(buf, b->form_name);
234     dump_cstr(buf, b->filename);
235     dump_cstr(buf, b->d_filename);
236 }
237
238 static const void *restore_body(const char *d, BODY *c)
239 {
240     memcpy(c, d, sizeof(*c));
241     d += sizeof(*c);
242
243     d = restore_cstr(d, &c->xtype);
244     d = restore_cstr(d, &c->subtype);
245
246     d = restore_parameter(d, c->parameter);
247
248     d = restore_cstr(d, &c->description);
249     d = restore_cstr(d, &c->form_name);
250     d = restore_cstr(d, &c->filename);
251     d = restore_cstr(d, &c->d_filename);
252     return d;
253 }
254
255 static void dump_envelope(buffer_t *buf, ENVELOPE * e)
256 {
257     int n;
258
259     dump_address(buf, e->return_path);
260     dump_address(buf, e->from);
261     dump_address(buf, e->to);
262     dump_address(buf, e->cc);
263     dump_address(buf, e->bcc);
264     dump_address(buf, e->sender);
265     dump_address(buf, e->reply_to);
266     dump_address(buf, e->mail_followup_to);
267
268     dump_cstr(buf, e->subject);
269     n = e->real_subj ? e->real_subj - e->subject : -1;
270     dump_int(buf, n);
271
272     dump_cstr(buf, e->message_id);
273     dump_cstr(buf, e->supersedes);
274     dump_cstr(buf, e->date);
275     dump_cstr(buf, e->x_label);
276     dump_cstr(buf, e->list_post);
277
278     dump_list(buf, e->references);
279     dump_list(buf, e->in_reply_to);
280     dump_list(buf, e->userhdrs);
281 }
282
283 static const void *restore_envelope(const char *d, ENVELOPE *e)
284 {
285     int real_subj_off;
286
287     d = restore_address(d, &e->return_path);
288     d = restore_address(d, &e->from);
289     d = restore_address(d, &e->to);
290     d = restore_address(d, &e->cc);
291     d = restore_address(d, &e->bcc);
292     d = restore_address(d, &e->sender);
293     d = restore_address(d, &e->reply_to);
294     d = restore_address(d, &e->mail_followup_to);
295
296     d = restore_cstr(d, &e->subject);
297     d = restore_int(d, &real_subj_off);
298     if (real_subj_off >= 0) {
299         e->real_subj = e->subject + real_subj_off;
300     } else {
301         e->real_subj = NULL;
302     }
303     d = restore_cstr(d, &e->message_id);
304     d = restore_cstr(d, &e->supersedes);
305     d = restore_cstr(d, &e->date);
306     d = restore_cstr(d, &e->x_label);
307     d = restore_cstr(d, &e->list_post);
308
309     d = restore_list(d, &e->references);
310     d = restore_list(d, &e->in_reply_to);
311     d = restore_list(d, &e->userhdrs);
312     return d;
313 }
314
315 static buffer_t *mutt_hcache_dump(hcache_t *db, HEADER *h, long uid_validity)
316 {
317     buffer_t *res = buffer_new();
318
319     if (!uid_validity) {
320         uid_validity = time(NULL);
321     }
322     buffer_add(res, &uid_validity, sizeof(uid_validity));
323     dump_int(res, db->crc);
324     buffer_add(res, h, sizeof(*h));
325
326     dump_envelope(res, h->env);
327     dump_body(res, h->content);
328     dump_cstr(res, h->maildir_flags);
329     return res;
330 }
331
332 HEADER *mutt_hcache_restore(const void *_d, HEADER **oh)
333 {
334     const char *d = _d;
335     HEADER *h = header_new();
336
337     /* skip uid_validity + crc */
338     d += sizeof(long) + sizeof(int);
339
340     memcpy(h, d, sizeof(*h));
341     d += sizeof(*h);
342
343     h->env = envelope_new();
344     d = restore_envelope(d, h->env);
345
346     h->content = body_new();
347     h->content->parameter = parameter_new();
348     d = restore_body(d, h->content);
349
350     d = restore_cstr(d, &h->maildir_flags);
351
352     /* this is needed for maildir style mailboxes */
353     if (oh) {
354         h->old = (*oh)->old;
355         h->path = m_strdup((*oh)->path);
356         header_delete(oh);
357     }
358
359     return h;
360 }
361
362 /* }}} */
363
364 hcache_t *mutt_hcache_open(const char *folder)
365 {
366     const char *path;
367     hcache_t *h;
368
369     if (m_strisempty(mod_core.cachedir)) {
370         return NULL;
371     }
372
373     h = p_new(hcache_t, 1);
374     h->folder = m_strdup(folder);
375     h->crc    = generate_crc32();
376
377     path = mutt_hcache_per_folder(mod_core.cachedir, folder);
378
379     {
380 #if defined(HAVE_TOKYOCABINET)
381         h->db = tchdbnew();
382         if (!tchdbopen(h->db, path, HDBOWRITER | HDBOCREAT)) {
383             tchdbdel(h->db);
384             h->db = NULL;
385         }
386 #elif defined(HAVE_GDBM)
387         h->db = gdbm_open((char *) path, 16384, GDBM_WRCREAT, 00600, NULL);
388 #endif
389     }
390
391     if (!h->db) {
392         p_delete(&h->folder);
393         p_delete(&h);
394     }
395     return h;
396 }
397
398 void mutt_hcache_close(hcache_t **db)
399 {
400     if (!*db)
401         return;
402
403 #if defined(HAVE_TOKYOCABINET)
404     tchdbdel((*db)->db);
405     (*db)->db = NULL;
406 #elif defined(HAVE_GDBM)
407     gdbm_close((*db)->db);
408 #endif
409
410     p_delete(&(*db)->folder);
411     p_delete(db);
412 }
413
414 void *mutt_hcache_fetch(hcache_t *db, const char *filename,
415                         ssize_t (*keylen)(const char *fn))
416 {
417     char path[_POSIX_PATH_MAX];
418     char *data = NULL;
419
420     if (!db)
421         return NULL;
422
423     snprintf(path, sizeof(path), "%s%s", db->folder, filename);
424
425     {
426         int ksize = keylen(path);
427 #if defined(HAVE_TOKYOCABINET)
428         int unused;
429         data  = tchdbget(db->db, path, ksize, &unused);
430 #elif defined(HAVE_GDBM)
431         datum k = { .dptr = path, .dsize = ksize };
432
433         data = gdbm_fetch(db->db, k).dtpr;
434 #endif
435     }
436
437     if (data) {
438         unsigned crc = 0;
439
440         restore_int(data + sizeof(long), (int *)&crc);
441         if (crc != db->crc)
442             p_delete(&data);
443     }
444
445     return data;
446 }
447
448 int mutt_hcache_store(hcache_t *db, const char *filename, HEADER *header,
449                       long uid_validity, ssize_t (*keylen)(const char *fn))
450 {
451     char path[_POSIX_PATH_MAX];
452     buffer_t *data;
453     int ret;
454
455     if (!db)
456         return -1;
457
458     snprintf(path, sizeof(path), "%s%s", db->folder, filename);
459     data = mutt_hcache_dump(db, header, uid_validity);
460
461     {
462         int ksize = keylen(path);
463 #if defined(HAVE_TOKYOCABINET)
464         ret = !!tchdbput(db->db, path, ksize, data->data, data->len) - 1;
465 #elif defined(HAVE_GDBM)
466         datum k = { .dptr = path, .dsize = ksize };
467         datum v = { .dptr = data->data, .dsize = data->len };
468
469         ret = gdbm_store(db->db, k, v, GDBM_REPLACE);
470 #endif
471     }
472
473     buffer_delete(&data);
474     return ret;
475 }
476
477 void mutt_hcache_delete(hcache_t *db, const char *filename,
478                        ssize_t (*keylen)(const char *fn))
479 {
480     char path[_POSIX_PATH_MAX];
481
482     if (!db)
483         return;
484
485     snprintf(path, sizeof(path), "%s%s", db->folder, filename);
486
487     {
488         int ksize = keylen(path);
489 #if defined(HAVE_TOKYOCABINET)
490         tchdbout(db->db, path, ksize);
491 #elif defined(HAVE_GDBM)
492         datum k = { .dptr = path, .dsize = ksize };
493         gdbm_delete(db->db, k);
494 #endif
495     }
496 }
497
498 #endif /* USE_HCACHE */