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