Updated for string-utils namespace changes.
[platform/upstream/evolution-data-server.git] / camel / camel-store-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003 Ximian Inc.
4  *
5  * Authors: Michael Zucchi <notzed@ximian.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU General Public
9  * License as published by the Free Software Foundation.
10  *
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 GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <stdlib.h>
34
35 #include "camel-store-summary.h"
36
37 #include "camel-file-utils.h"
38
39 #include "e-util/md5-utils.h"
40 #include "e-util/e-memory.h"
41
42 #include "camel-private.h"
43 #include "camel-url.h"
44
45 #define d(x)
46 #define io(x)                   /* io debug */
47
48 /* possible versions, for versioning changes */
49 #define CAMEL_STORE_SUMMARY_VERSION_0 (1)
50
51 /* current version */
52 #define CAMEL_STORE_SUMMARY_VERSION (1)
53
54 #define _PRIVATE(o) (((CamelStoreSummary *)(o))->priv)
55
56 static int summary_header_load(CamelStoreSummary *, FILE *);
57 static int summary_header_save(CamelStoreSummary *, FILE *);
58
59 static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);
60 static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
61 static int               store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
62 static void              store_info_free(CamelStoreSummary *, CamelStoreInfo *);
63
64 static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int);
65 static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *);
66
67 static void camel_store_summary_class_init (CamelStoreSummaryClass *klass);
68 static void camel_store_summary_init       (CamelStoreSummary *obj);
69 static void camel_store_summary_finalise   (CamelObject *obj);
70
71 static CamelObjectClass *camel_store_summary_parent;
72
73 static void
74 camel_store_summary_class_init (CamelStoreSummaryClass *klass)
75 {
76         camel_store_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
77
78         klass->summary_header_load = summary_header_load;
79         klass->summary_header_save = summary_header_save;
80
81         klass->store_info_new  = store_info_new;
82         klass->store_info_load = store_info_load;
83         klass->store_info_save = store_info_save;
84         klass->store_info_free = store_info_free;
85
86         klass->store_info_string = store_info_string;
87         klass->store_info_set_string = store_info_set_string;
88 }
89
90 static void
91 camel_store_summary_init (CamelStoreSummary *s)
92 {
93         struct _CamelStoreSummaryPrivate *p;
94
95         p = _PRIVATE(s) = g_malloc0(sizeof(*p));
96
97         s->store_info_size = sizeof(CamelStoreInfo);
98
99         s->store_info_chunks = NULL;
100
101         s->version = CAMEL_STORE_SUMMARY_VERSION;
102         s->flags = 0;
103         s->count = 0;
104         s->time = 0;
105
106         s->folders = g_ptr_array_new();
107         s->folders_path = g_hash_table_new(g_str_hash, g_str_equal);
108
109 #ifdef ENABLE_THREADS
110         p->summary_lock = g_mutex_new();
111         p->io_lock = g_mutex_new();
112         p->alloc_lock = g_mutex_new();
113         p->ref_lock = g_mutex_new();
114 #endif
115 }
116
117 static void
118 camel_store_summary_finalise (CamelObject *obj)
119 {
120         struct _CamelStoreSummaryPrivate *p;
121         CamelStoreSummary *s = (CamelStoreSummary *)obj;
122
123         p = _PRIVATE(obj);
124
125         camel_store_summary_clear(s);
126         g_ptr_array_free(s->folders, TRUE);
127         g_hash_table_destroy(s->folders_path);
128
129         g_free(s->summary_path);
130
131         if (s->store_info_chunks)
132                 e_memchunk_destroy(s->store_info_chunks);
133
134 #ifdef ENABLE_THREADS
135         g_mutex_free(p->summary_lock);
136         g_mutex_free(p->io_lock);
137         g_mutex_free(p->alloc_lock);
138         g_mutex_free(p->ref_lock);
139 #endif
140
141         g_free(p);
142 }
143
144 CamelType
145 camel_store_summary_get_type (void)
146 {
147         static CamelType type = CAMEL_INVALID_TYPE;
148         
149         if (type == CAMEL_INVALID_TYPE) {
150                 type = camel_type_register (camel_object_get_type (), "CamelStoreSummary",
151                                             sizeof (CamelStoreSummary),
152                                             sizeof (CamelStoreSummaryClass),
153                                             (CamelObjectClassInitFunc) camel_store_summary_class_init,
154                                             NULL,
155                                             (CamelObjectInitFunc) camel_store_summary_init,
156                                             (CamelObjectFinalizeFunc) camel_store_summary_finalise);
157         }
158         
159         return type;
160 }
161
162 /**
163  * camel_store_summary_new:
164  *
165  * Create a new CamelStoreSummary object.
166  * 
167  * Return value: A new CamelStoreSummary widget.
168  **/
169 CamelStoreSummary *
170 camel_store_summary_new (void)
171 {
172         CamelStoreSummary *new = CAMEL_STORE_SUMMARY ( camel_object_new (camel_store_summary_get_type ()));     return new;
173 }
174
175 /**
176  * camel_store_summary_set_filename:
177  * @s: 
178  * @name: 
179  * 
180  * Set the filename where the summary will be loaded to/saved from.
181  **/
182 void camel_store_summary_set_filename(CamelStoreSummary *s, const char *name)
183 {
184         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
185
186         g_free(s->summary_path);
187         s->summary_path = g_strdup(name);
188
189         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
190 }
191
192 void camel_store_summary_set_uri_base(CamelStoreSummary *s, CamelURL *base)
193 {
194         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
195
196         if (s->uri_base)
197                 camel_url_free(s->uri_base);
198         s->uri_base = camel_url_new_with_base(base, "");
199
200         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
201 }
202
203 /**
204  * camel_store_summary_count:
205  * @s: 
206  * 
207  * Get the number of summary items stored in this summary.
208  * 
209  * Return value: The number of items int he summary.
210  **/
211 int
212 camel_store_summary_count(CamelStoreSummary *s)
213 {
214         return s->folders->len;
215 }
216
217 /**
218  * camel_store_summary_index:
219  * @s: 
220  * @i: 
221  * 
222  * Retrieve a summary item by index number.
223  *
224  * A referenced to the summary item is returned, which may be
225  * ref'd or free'd as appropriate.
226  * 
227  * Return value: The summary item, or NULL if the index @i is out
228  * of range.
229  * It must be freed using camel_store_summary_info_free().
230  **/
231 CamelStoreInfo *
232 camel_store_summary_index(CamelStoreSummary *s, int i)
233 {
234         CamelStoreInfo *info = NULL;
235
236         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
237         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
238
239         if (i<s->folders->len)
240                 info = g_ptr_array_index(s->folders, i);
241
242         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
243
244         if (info)
245                 info->refcount++;
246
247         CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
248
249         return info;
250 }
251
252 /**
253  * camel_store_summary_index:
254  * @s: 
255  * @i: 
256  * 
257  * Obtain a copy of the summary array.  This is done atomically,
258  * so cannot contain empty entries.
259  *
260  * It must be freed using camel_store_summary_array_free().
261  **/
262 GPtrArray *
263 camel_store_summary_array(CamelStoreSummary *s)
264 {
265         CamelStoreInfo *info;
266         GPtrArray *res = g_ptr_array_new();
267         int i;
268         
269         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
270         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
271
272         g_ptr_array_set_size(res, s->folders->len);
273         for (i=0;i<s->folders->len;i++) {
274                 info = res->pdata[i] = g_ptr_array_index(s->folders, i);
275                 info->refcount++;
276         }
277
278         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
279         CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
280
281         return res;
282 }
283
284 /**
285  * camel_store_summary_array_free:
286  * @s: 
287  * @array: 
288  * 
289  * Free the folder summary array.
290  **/
291 void
292 camel_store_summary_array_free(CamelStoreSummary *s, GPtrArray *array)
293 {
294         int i;
295
296         for (i=0;i<array->len;i++)
297                 camel_store_summary_info_free(s, array->pdata[i]);
298
299         g_ptr_array_free(array, TRUE);
300 }
301
302 /**
303  * camel_store_summary_path:
304  * @s: 
305  * @path: 
306  * 
307  * Retrieve a summary item by path name.
308  *
309  * A referenced to the summary item is returned, which may be
310  * ref'd or free'd as appropriate.
311  * 
312  * Return value: The summary item, or NULL if the @path name
313  * is not available.
314  * It must be freed using camel_store_summary_info_free().
315  **/
316 CamelStoreInfo *
317 camel_store_summary_path(CamelStoreSummary *s, const char *path)
318 {
319         CamelStoreInfo *info;
320
321         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
322         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
323
324         info = g_hash_table_lookup(s->folders_path, path);
325
326         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
327
328         if (info)
329                 info->refcount++;
330
331         CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
332
333         return info;
334 }
335
336 int
337 camel_store_summary_load(CamelStoreSummary *s)
338 {
339         FILE *in;
340         int i;
341         CamelStoreInfo *mi;
342
343         g_assert(s->summary_path);
344
345         in = fopen(s->summary_path, "r");
346         if (in == NULL)
347                 return -1;
348
349         CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
350         if ( ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
351                 goto error;
352
353         /* now read in each message ... */
354         for (i=0;i<s->count;i++) {
355                 mi = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_load(s, in);
356
357                 if (mi == NULL)
358                         goto error;
359
360                 camel_store_summary_add(s, mi);
361         }
362
363         CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
364         
365         if (fclose (in) != 0)
366                 return -1;
367
368         s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
369
370         return 0;
371
372 error:
373         i = ferror (in);
374         g_warning ("Cannot load summary file: %s", strerror (ferror (in)));
375         CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
376         fclose (in);
377         s->flags |= ~CAMEL_STORE_SUMMARY_DIRTY;
378         errno = i;
379         
380         return -1;
381 }
382
383 /**
384  * camel_store_summary_save:
385  * @s: 
386  * 
387  * Writes the summary to disk.  The summary is only written if changes
388  * have occured.
389  * 
390  * Return value: Returns -1 on error.
391  **/
392 int
393 camel_store_summary_save(CamelStoreSummary *s)
394 {
395         FILE *out;
396         int fd;
397         int i;
398         guint32 count;
399         CamelStoreInfo *mi;
400
401         g_assert(s->summary_path);
402
403         io(printf("** saving summary\n"));
404
405         if ((s->flags & CAMEL_STORE_SUMMARY_DIRTY) == 0) {
406                 io(printf("**  summary clean no save\n"));
407                 return 0;
408         }
409
410         fd = open(s->summary_path, O_RDWR|O_CREAT|O_TRUNC, 0600);
411         if (fd == -1) {
412                 io(printf("**  open error: %s\n", strerror (errno)));
413                 return -1;
414         }
415         out = fdopen(fd, "w");
416         if ( out == NULL ) {
417                 i = errno;
418                 printf("**  fdopen error: %s\n", strerror (errno));
419                 close(fd);
420                 errno = i;
421                 return -1;
422         }
423
424         io(printf("saving header\n"));
425
426         CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
427
428         if ( ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1) {
429                 i = errno;
430                 fclose(out);
431                 CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
432                 errno = i;
433                 return -1;
434         }
435
436         /* now write out each message ... */
437
438         /* FIXME: Locking? */
439
440         count = s->folders->len;
441         for (i=0;i<count;i++) {
442                 mi = s->folders->pdata[i];
443                 ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_save(s, out, mi);
444         }
445
446         CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
447         
448         if (fflush (out) != 0 || fsync (fileno (out)) == -1) {
449                 i = errno;
450                 fclose (out);
451                 errno = i;
452                 return -1;
453         }
454         
455         if (fclose (out) != 0)
456                 return -1;
457
458         s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
459         return 0;
460 }
461
462 /**
463  * camel_store_summary_header_load:
464  * @s: Summary object.
465  * 
466  * Only load the header information from the summary,
467  * keep the rest on disk.  This should only be done on
468  * a fresh summary object.
469  * 
470  * Return value: -1 on error.
471  **/
472 int camel_store_summary_header_load(CamelStoreSummary *s)
473 {
474         FILE *in;
475         int ret;
476
477         g_assert(s->summary_path);
478
479         in = fopen(s->summary_path, "r");
480         if (in == NULL)
481                 return -1;
482
483         CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
484         ret = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in);
485         CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
486         
487         fclose(in);
488         s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
489         return ret;
490 }
491
492 /**
493  * camel_store_summary_add:
494  * @s: 
495  * @info: 
496  * 
497  * Adds a new @info record to the summary.  If @info->uid is NULL, then a new
498  * uid is automatically re-assigned by calling :next_uid_string().
499  *
500  * The @info record should have been generated by calling one of the
501  * info_new_*() functions, as it will be free'd based on the summary
502  * class.  And MUST NOT be allocated directly using malloc.
503  **/
504 void camel_store_summary_add(CamelStoreSummary *s, CamelStoreInfo *info)
505 {
506         if (info == NULL)
507                 return;
508
509         if (camel_store_info_path(s, info) == NULL) {
510                 g_warning("Trying to add a folder info with missing required path name\n");
511                 return;
512         }
513
514         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
515
516         g_ptr_array_add(s->folders, info);
517         g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, info), info);
518         s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
519
520         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
521 }
522
523 /**
524  * camel_store_summary_add_from_path:
525  * @s: 
526  * @h: 
527  * 
528  * Build a new info record based on the name, and add it to the summary.
529  *
530  * Return value: The newly added record.
531  **/
532 CamelStoreInfo *camel_store_summary_add_from_path(CamelStoreSummary *s, const char *path)
533 {
534         CamelStoreInfo *info;
535
536         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
537
538         info = g_hash_table_lookup(s->folders_path, path);
539         if (info != NULL) {
540                 g_warning("Trying to add folder '%s' to summary that already has it", path);
541                 info = NULL;
542         } else {
543                 info = camel_store_summary_info_new_from_path(s, path);
544                 g_ptr_array_add(s->folders, info);
545                 g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, info), info);
546                 s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
547         }
548
549         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
550
551         return info;
552 }
553
554 /**
555  * camel_store_summary_info_new_from_path:
556  * @s: 
557  * @h: 
558  * 
559  * Create a new info record from a name.
560  * 
561  * Return value: Guess?  This info record MUST be freed using
562  * camel_store_summary_info_free(), camel_store_info_free() will not work.
563  **/
564 CamelStoreInfo *camel_store_summary_info_new_from_path(CamelStoreSummary *s, const char *f)
565 {
566         return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> store_info_new(s, f);
567 }
568
569 /**
570  * camel_store_summary_info_free:
571  * @s: 
572  * @mi: 
573  * 
574  * Unref and potentially free the message info @mi, and all associated memory.
575  **/
576 void camel_store_summary_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
577 {
578         g_assert(mi);
579         g_assert(s);
580
581         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
582
583         g_assert(mi->refcount >= 1);
584
585         mi->refcount--;
586         if (mi->refcount > 0) {
587                 CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
588                 return;
589         }
590
591         CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
592
593         ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_free(s, mi);                
594 }
595
596 /**
597  * camel_store_summary_info_ref:
598  * @s: 
599  * @mi: 
600  * 
601  * Add an extra reference to @mi.
602  **/
603 void camel_store_summary_info_ref(CamelStoreSummary *s, CamelStoreInfo *mi)
604 {
605         g_assert(mi);
606         g_assert(s);
607
608         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
609         g_assert(mi->refcount >= 1);
610         mi->refcount++;
611         CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
612 }
613
614 /**
615  * camel_store_summary_touch:
616  * @s: 
617  * 
618  * Mark the summary as changed, so that a save will save it.
619  **/
620 void
621 camel_store_summary_touch(CamelStoreSummary *s)
622 {
623         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
624         s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
625         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
626 }
627
628 /**
629  * camel_store_summary_clear:
630  * @s: 
631  * 
632  * Empty the summary contents.
633  **/
634 void
635 camel_store_summary_clear(CamelStoreSummary *s)
636 {
637         int i;
638
639         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
640         if (camel_store_summary_count(s) == 0) {
641                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
642                 return;
643         }
644
645         for (i=0;i<s->folders->len;i++)
646                 camel_store_summary_info_free(s, s->folders->pdata[i]);
647
648         g_ptr_array_set_size(s->folders, 0);
649         g_hash_table_destroy(s->folders_path);
650         s->folders_path = g_hash_table_new(g_str_hash, g_str_equal);
651         s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
652         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
653 }
654
655 /**
656  * camel_store_summary_remove:
657  * @s: 
658  * @info: 
659  * 
660  * Remove a specific @info record from the summary.
661  **/
662 void camel_store_summary_remove(CamelStoreSummary *s, CamelStoreInfo *info)
663 {
664         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
665         g_hash_table_remove(s->folders_path, camel_store_info_path(s, info));
666         g_ptr_array_remove(s->folders, info);
667         s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
668         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
669
670         camel_store_summary_info_free(s, info);
671 }
672
673 /**
674  * camel_store_summary_remove_uid:
675  * @s: 
676  * @path: 
677  * 
678  * Remove a specific info record from the summary, by @path.
679  **/
680 void camel_store_summary_remove_path(CamelStoreSummary *s, const char *path)
681 {
682         CamelStoreInfo *oldinfo;
683         char *oldpath;
684
685         CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
686         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
687         if (g_hash_table_lookup_extended(s->folders_path, path, (void *)&oldpath, (void *)&oldinfo)) {
688                 /* make sure it doesn't vanish while we're removing it */
689                 oldinfo->refcount++;
690                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
691                 CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
692                 camel_store_summary_remove(s, oldinfo);
693                 camel_store_summary_info_free(s, oldinfo);
694         } else {
695                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
696                 CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
697         }
698 }
699
700 /**
701  * camel_store_summary_remove_index:
702  * @s: 
703  * @index: 
704  * 
705  * Remove a specific info record from the summary, by index.
706  **/
707 void camel_store_summary_remove_index(CamelStoreSummary *s, int index)
708 {
709         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
710         if (index < s->folders->len) {
711                 CamelStoreInfo *info = s->folders->pdata[index];
712
713                 g_hash_table_remove(s->folders_path, camel_store_info_path(s, info));
714                 g_ptr_array_remove_index(s->folders, index);
715                 s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
716
717                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
718                 camel_store_summary_info_free(s, info);
719         } else {
720                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
721         }
722 }
723
724 static int
725 summary_header_load(CamelStoreSummary *s, FILE *in)
726 {
727         gint32 version, flags, count;
728         time_t time;
729
730         fseek(in, 0, SEEK_SET);
731
732         io(printf("Loading header\n"));
733
734         if (camel_file_util_decode_fixed_int32(in, &version) == -1
735             || camel_file_util_decode_fixed_int32(in, &flags) == -1
736             || camel_file_util_decode_time_t(in, &time) == -1
737             || camel_file_util_decode_fixed_int32(in, &count) == -1) {
738                 return -1;
739         }
740
741         s->flags = flags;
742         s->time = time;
743         s->count = count;
744         s->version = version;
745
746         if (version < CAMEL_STORE_SUMMARY_VERSION_0) {
747                 g_warning("Store summary header version too low");
748                 return -1;
749         }
750
751         return 0;
752 }
753
754 static int
755 summary_header_save(CamelStoreSummary *s, FILE *out)
756 {
757         fseek(out, 0, SEEK_SET);
758
759         io(printf("Savining header\n"));
760
761         /* always write latest version */
762         camel_file_util_encode_fixed_int32(out, CAMEL_STORE_SUMMARY_VERSION);
763         camel_file_util_encode_fixed_int32(out, s->flags);
764         camel_file_util_encode_time_t(out, s->time);
765         return camel_file_util_encode_fixed_int32(out, camel_store_summary_count(s));
766 }
767
768 /**
769  * camel_store_summary_info_new:
770  * @s: 
771  * 
772  * Allocate a new camel message info, suitable for adding
773  * to this summary.
774  * 
775  * Return value: 
776  **/
777 CamelStoreInfo *
778 camel_store_summary_info_new(CamelStoreSummary *s)
779 {
780         CamelStoreInfo *mi;
781
782         CAMEL_STORE_SUMMARY_LOCK(s, alloc_lock);
783         if (s->store_info_chunks == NULL)
784                 s->store_info_chunks = e_memchunk_new(32, s->store_info_size);
785         mi = e_memchunk_alloc0(s->store_info_chunks);
786         CAMEL_STORE_SUMMARY_UNLOCK(s, alloc_lock);
787         mi->refcount = 1;
788         return mi;
789 }
790
791 const char *camel_store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
792 {
793         return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_string(s, mi, type);
794 }
795
796 void camel_store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *value)
797 {
798         ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_set_string(s, mi, type, value);
799 }
800
801 static CamelStoreInfo *
802 store_info_new(CamelStoreSummary *s, const char *f)
803 {
804         CamelStoreInfo *mi;
805
806         mi = camel_store_summary_info_new(s);
807
808         mi->path = g_strdup(f);
809         mi->unread = CAMEL_STORE_INFO_FOLDER_UNKNOWN;
810         mi->total = CAMEL_STORE_INFO_FOLDER_UNKNOWN;
811
812         return mi;
813 }
814
815 static CamelStoreInfo *
816 store_info_load(CamelStoreSummary *s, FILE *in)
817 {
818         CamelStoreInfo *mi;
819
820         mi = camel_store_summary_info_new(s);
821
822         io(printf("Loading folder info\n"));
823
824         camel_file_util_decode_string(in, &mi->path);
825         camel_file_util_decode_uint32(in, &mi->flags);
826         camel_file_util_decode_uint32(in, &mi->unread);
827         camel_file_util_decode_uint32(in, &mi->total);
828
829         if (!ferror(in))
830                 return mi;
831
832         camel_store_summary_info_free(s, mi);
833
834         return NULL;
835 }
836
837 static int
838 store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
839 {
840         io(printf("Saving folder info\n"));
841
842         camel_file_util_encode_string(out, camel_store_info_path(s, mi));
843         camel_file_util_encode_uint32(out, mi->flags);
844         camel_file_util_encode_uint32(out, mi->unread);
845         camel_file_util_encode_uint32(out, mi->total);
846
847         return ferror(out);
848 }
849
850 static void
851 store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
852 {
853         g_free(mi->path);
854         g_free(mi->uri);
855         e_memchunk_free(s->store_info_chunks, mi);
856 }
857
858 static const char *
859 store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
860 {
861         const char *p;
862
863         /* FIXME: Locks? */
864
865         g_assert (mi != NULL);
866
867         switch (type) {
868         case CAMEL_STORE_INFO_PATH:
869                 return mi->path;
870         case CAMEL_STORE_INFO_NAME:
871                 p = strrchr(mi->path, '/');
872                 if (p)
873                         return p+1;
874                 else
875                         return mi->path;
876         case CAMEL_STORE_INFO_URI:
877                 if (mi->uri == NULL) {
878                         CamelURL *uri;
879
880                         uri = camel_url_new_with_base(s->uri_base, mi->path);
881                         ((CamelStoreInfo *)mi)->uri = camel_url_to_string(uri, 0);
882                         camel_url_free(uri);
883                 }
884                 return mi->uri;
885         }
886
887         return "";
888 }
889
890 static void
891 store_info_set_string (CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
892 {
893         const char *p;
894         char *v;
895         int len;
896
897         g_assert (mi != NULL);
898
899         switch(type) {
900         case CAMEL_STORE_INFO_PATH:
901                 CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
902                 g_hash_table_remove(s->folders_path, (char *)camel_store_info_path(s, mi));
903                 g_free(mi->path);
904                 g_free(mi->uri);
905                 mi->path = g_strdup(str);
906                 g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, mi), mi);
907                 s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
908                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
909                 break;
910         case CAMEL_STORE_INFO_NAME:
911                 CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
912                 g_hash_table_remove(s->folders_path, (char *)camel_store_info_path(s, mi));
913                 p = strrchr(mi->path, '/');
914                 if (p) {
915                         len = p-mi->path+1;
916                         v = g_malloc(len+strlen(str)+1);
917                         memcpy(v, mi->path, len);
918                         strcpy(v+len, str);
919                 } else {
920                         v = g_strdup(str);
921                 }
922                 g_free(mi->path);
923                 mi->path = v;
924                 g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, mi), mi);
925                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
926                 break;
927         case CAMEL_STORE_INFO_URI:
928                 g_warning("Cannot set store info uri, aborting");
929                 abort();
930                 break;
931         }
932 }