Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-folder-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Copyright (C) 2000-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 <pthread.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35
36 #include <glib.h>
37 #include <glib/gstdio.h>
38
39 #include <libedataserver/e-iconv.h>
40 #include <libedataserver/e-memory.h>
41 #include <libedataserver/md5-utils.h>
42
43 #include "camel-file-utils.h"
44 #include "camel-folder-summary.h"
45 #include "camel-folder.h"
46 #include "camel-mime-filter-basic.h"
47 #include "camel-mime-filter-charset.h"
48 #include "camel-mime-filter-html.h"
49 #include "camel-mime-filter-index.h"
50 #include "camel-mime-filter.h"
51 #include "camel-mime-message.h"
52 #include "camel-multipart.h"
53 #include "camel-private.h"
54 #include "camel-stream-filter.h"
55 #include "camel-stream-mem.h"
56 #include "camel-stream-null.h"
57 #include "camel-string-utils.h"
58
59 static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER;
60
61 /* this lock is ONLY for the standalone messageinfo stuff */
62 #define GLOBAL_INFO_LOCK(i) pthread_mutex_lock(&info_lock)
63 #define GLOBAL_INFO_UNLOCK(i) pthread_mutex_unlock(&info_lock)
64
65
66 /* this should probably be conditional on it existing */
67 #define USE_BSEARCH
68
69 #define d(x)
70 #define io(x)                   /* io debug */
71 #define w(x)
72
73 #if 0
74 extern int strdup_count, malloc_count, free_count;
75 #endif
76
77 #define CAMEL_FOLDER_SUMMARY_VERSION (13)
78
79 #define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv)
80
81 #define META_SUMMARY_SUFFIX_LEN 5 /* strlen("-meta") */
82
83 /* trivial lists, just because ... */
84 struct _node {
85         struct _node *next;
86 };
87
88 static struct _node *my_list_append(struct _node **list, struct _node *n);
89 static int my_list_size(struct _node **list);
90
91 static int summary_header_load(CamelFolderSummary *, FILE *);
92 static int summary_header_save(CamelFolderSummary *, FILE *);
93 static int summary_meta_header_load(CamelFolderSummary *, FILE *);
94 static int summary_meta_header_save(CamelFolderSummary *, FILE *);
95
96 static CamelMessageInfo * message_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
97 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
98 static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg);
99 static CamelMessageInfo * message_info_load(CamelFolderSummary *, FILE *);
100 static int                message_info_save(CamelFolderSummary *, FILE *, CamelMessageInfo *);
101 static int                meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *info);
102 static void               message_info_free(CamelFolderSummary *, CamelMessageInfo *);
103
104 static CamelMessageContentInfo * content_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
105 static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
106 static CamelMessageContentInfo * content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp);
107 static CamelMessageContentInfo * content_info_load(CamelFolderSummary *, FILE *);
108 static int                       content_info_save(CamelFolderSummary *, FILE *, CamelMessageContentInfo *);
109 static void                      content_info_free(CamelFolderSummary *, CamelMessageContentInfo *);
110
111 static char *next_uid_string(CamelFolderSummary *s);
112
113 static CamelMessageContentInfo * summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp);
114 static CamelMessageContentInfo * summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object);
115
116 static void camel_folder_summary_class_init (CamelFolderSummaryClass *klass);
117 static void camel_folder_summary_init       (CamelFolderSummary *obj);
118 static void camel_folder_summary_finalize   (CamelObject *obj);
119
120 static CamelObjectClass *camel_folder_summary_parent;
121
122 static void
123 camel_folder_summary_init (CamelFolderSummary *s)
124 {
125         struct _CamelFolderSummaryPrivate *p;
126
127         p = _PRIVATE(s) = g_malloc0(sizeof(*p));
128
129         p->filter_charset = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
130
131         s->message_info_size = sizeof(CamelMessageInfoBase);
132         s->content_info_size = sizeof(CamelMessageContentInfo);
133
134         s->message_info_chunks = NULL;
135         s->content_info_chunks = NULL;
136
137 #if defined (DOESTRV) || defined (DOEPOOLV)
138         s->message_info_strings = CAMEL_MESSAGE_INFO_LAST;
139 #endif
140
141         s->version = CAMEL_FOLDER_SUMMARY_VERSION;
142         s->flags = 0;
143         s->time = 0;
144         s->nextuid = 1;
145
146         s->messages = g_ptr_array_new();
147         s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
148         
149         p->summary_lock = g_mutex_new();
150         p->io_lock = g_mutex_new();
151         p->filter_lock = g_mutex_new();
152         p->alloc_lock = g_mutex_new();
153         p->ref_lock = g_mutex_new();
154
155         s->meta_summary = g_malloc0(sizeof(CamelFolderMetaSummary));
156
157         /* Default is 20, any implementor having UIDs that has length
158            exceeding 20, has to override this value 
159         */
160         s->meta_summary->uid_len = 20;
161 }
162
163 static void free_o_name(void *key, void *value, void *data)
164 {
165         camel_object_unref((CamelObject *)value);
166         g_free(key);
167 }
168
169 static void
170 camel_folder_summary_finalize (CamelObject *obj)
171 {
172         struct _CamelFolderSummaryPrivate *p;
173         CamelFolderSummary *s = (CamelFolderSummary *)obj;
174
175         p = _PRIVATE(obj);
176
177         camel_folder_summary_clear(s);
178         g_ptr_array_free(s->messages, TRUE);
179         g_hash_table_destroy(s->messages_uid);
180
181         g_hash_table_foreach(p->filter_charset, free_o_name, 0);
182         g_hash_table_destroy(p->filter_charset);
183
184         g_free(s->summary_path);
185
186         if (s->message_info_chunks)
187                 e_memchunk_destroy(s->message_info_chunks);
188         if (s->content_info_chunks)
189                 e_memchunk_destroy(s->content_info_chunks);
190
191         if (p->filter_index)
192                 camel_object_unref((CamelObject *)p->filter_index);
193         if (p->filter_64)
194                 camel_object_unref((CamelObject *)p->filter_64);
195         if (p->filter_qp)
196                 camel_object_unref((CamelObject *)p->filter_qp);
197         if (p->filter_uu)
198                 camel_object_unref((CamelObject *)p->filter_uu);
199         if (p->filter_save)
200                 camel_object_unref((CamelObject *)p->filter_save);
201         if (p->filter_html)
202                 camel_object_unref((CamelObject *)p->filter_html);
203
204         if (p->filter_stream)
205                 camel_object_unref((CamelObject *)p->filter_stream);
206         if (p->index)
207                 camel_object_unref((CamelObject *)p->index);
208
209         /* Freeing memory occupied by meta-summary-header */
210         g_free(s->meta_summary->path);
211         g_free(s->meta_summary);
212         
213         g_mutex_free(p->summary_lock);
214         g_mutex_free(p->io_lock);
215         g_mutex_free(p->filter_lock);
216         g_mutex_free(p->alloc_lock);
217         g_mutex_free(p->ref_lock);
218         
219         g_free(p);
220 }
221
222 CamelType
223 camel_folder_summary_get_type (void)
224 {
225         static CamelType type = CAMEL_INVALID_TYPE;
226         
227         if (type == CAMEL_INVALID_TYPE) {
228                 type = camel_type_register (camel_object_get_type (), "CamelFolderSummary",
229                                             sizeof (CamelFolderSummary),
230                                             sizeof (CamelFolderSummaryClass),
231                                             (CamelObjectClassInitFunc) camel_folder_summary_class_init,
232                                             NULL,
233                                             (CamelObjectInitFunc) camel_folder_summary_init,
234                                             (CamelObjectFinalizeFunc) camel_folder_summary_finalize);
235         }
236         
237         return type;
238 }
239
240
241 /**
242  * camel_folder_summary_new:
243  * @folder: parent #CamelFolder object
244  *
245  * Create a new #CamelFolderSummary object.
246  * 
247  * Returns a new #CamelFolderSummary object
248  **/
249 CamelFolderSummary *
250 camel_folder_summary_new (struct _CamelFolder *folder)
251 {
252         CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ()));
253
254         new->folder = folder;
255
256         return new;
257 }
258
259
260 /**
261  * camel_folder_summary_set_filename:
262  * @summary: a #CamelFolderSummary object
263  * @filename: a filename
264  * 
265  * Set the filename where the summary will be loaded to/saved from.
266  **/
267 void
268 camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name)
269 {
270         CAMEL_SUMMARY_LOCK(s, summary_lock);
271
272         g_free(s->summary_path);
273         s->summary_path = g_strdup(name);
274
275         g_free(s->meta_summary->path);
276         s->meta_summary->path = g_strconcat(name, "-meta", NULL);
277
278         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
279 }
280
281
282 /**
283  * camel_folder_summary_set_index:
284  * @summary: a #CamelFolderSummary object
285  * @index: a #CamelIndex
286  * 
287  * Set the index used to index body content.  If the index is %NULL, or
288  * not set (the default), no indexing of body content will take place.
289  *
290  * Unlike earlier behaviour, build_content need not be set to perform indexing.
291  **/
292 void
293 camel_folder_summary_set_index(CamelFolderSummary *s, CamelIndex *index)
294 {
295         struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
296
297         if (p->index)
298                 camel_object_unref((CamelObject *)p->index);
299
300         p->index = index;
301         if (index)
302                 camel_object_ref((CamelObject *)index);
303 }
304
305
306 /**
307  * camel_folder_summary_set_build_content:
308  * @summary: a #CamelFolderSummary object
309  * @state: to build or not to build the content
310  * 
311  * Set a flag to tell the summary to build the content info summary
312  * (#CamelMessageInfo.content).  The default is not to build content
313  * info summaries.
314  **/
315 void
316 camel_folder_summary_set_build_content(CamelFolderSummary *s, gboolean state)
317 {
318         s->build_content = state;
319 }
320
321
322 /**
323  * camel_folder_summary_count:
324  * @summary: a #CamelFolderSummary object
325  * 
326  * Get the number of summary items stored in this summary.
327  * 
328  * Returns the number of items in the summary
329  **/
330 int
331 camel_folder_summary_count(CamelFolderSummary *s)
332 {
333         return s->messages->len;
334 }
335
336
337 /**
338  * camel_folder_summary_index:
339  * @summary: a #CamelFolderSummary object
340  * @index: item index
341  * 
342  * Retrieve a summary item by index number.
343  *
344  * A referenced to the summary item is returned, which may be
345  * ref'd or free'd as appropriate.
346  * 
347  * Returns the summary item, or %NULL if @index is out of range
348  **/
349 CamelMessageInfo *
350 camel_folder_summary_index(CamelFolderSummary *s, int i)
351 {
352         CamelMessageInfo *info = NULL;
353
354         CAMEL_SUMMARY_LOCK(s, summary_lock);
355         CAMEL_SUMMARY_LOCK(s, ref_lock);
356
357         if (i<s->messages->len)
358                 info = g_ptr_array_index(s->messages, i);
359
360         if (info)
361                 info->refcount++;
362
363         CAMEL_SUMMARY_UNLOCK(s, ref_lock);
364         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
365
366         return info;
367 }
368
369
370 /**
371  * camel_folder_summary_array:
372  * @summary: a #CamelFolderSummary object
373  * 
374  * Obtain a copy of the summary array.  This is done atomically,
375  * so cannot contain empty entries.
376  *
377  * It must be freed using #camel_folder_summary_array_free.
378  *
379  * Returns a #GPtrArray of #CamelMessageInfo items
380  **/
381 GPtrArray *
382 camel_folder_summary_array(CamelFolderSummary *s)
383 {
384         CamelMessageInfo *info;
385         GPtrArray *res = g_ptr_array_new();
386         int i;
387         
388         CAMEL_SUMMARY_LOCK(s, summary_lock);
389         CAMEL_SUMMARY_LOCK(s, ref_lock);
390
391         g_ptr_array_set_size(res, s->messages->len);
392         for (i=0;i<s->messages->len;i++) {
393                 info = res->pdata[i] = g_ptr_array_index(s->messages, i);
394                 info->refcount++;
395         }
396
397         CAMEL_SUMMARY_UNLOCK(s, ref_lock);
398         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
399
400         return res;
401 }
402
403
404 /**
405  * camel_folder_summary_array_free:
406  * @summary: a #CamelFolderSummary object
407  * @array: array of #CamelMessageInfo items as returned from #camel_folder_summary_array
408  * 
409  * Free the folder summary array.
410  **/
411 void
412 camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array)
413 {
414         int i;
415
416         /* FIXME: do the locking around the whole lot to make it faster */
417         for (i=0;i<array->len;i++)
418                 camel_message_info_free(array->pdata[i]);
419
420         g_ptr_array_free(array, TRUE);
421 }
422
423
424 /**
425  * camel_folder_summary_uid:
426  * @summary: a #CamelFolderSummary object
427  * @uid: a uid
428  * 
429  * Retrieve a summary item by uid.
430  *
431  * A referenced to the summary item is returned, which may be
432  * ref'd or free'd as appropriate.
433  * 
434  * Returns the summary item, or %NULL if the uid @uid is not available
435  **/
436 CamelMessageInfo *
437 camel_folder_summary_uid(CamelFolderSummary *s, const char *uid)
438 {
439         CamelMessageInfo *info;
440
441         CAMEL_SUMMARY_LOCK(s, summary_lock);
442         CAMEL_SUMMARY_LOCK(s, ref_lock);
443
444         info = g_hash_table_lookup(s->messages_uid, uid);
445
446         if (info)
447                 info->refcount++;
448
449         CAMEL_SUMMARY_UNLOCK(s, ref_lock);
450         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
451
452         return info;
453 }
454
455
456 /**
457  * camel_folder_summary_next_uid:
458  * @summary: a #CamelFolderSummary object
459  * 
460  * Generate a new unique uid value as an integer.  This
461  * may be used to create a unique sequence of numbers.
462  * 
463  * Returns the next unique uid value
464  **/
465 guint32
466 camel_folder_summary_next_uid(CamelFolderSummary *s)
467 {
468         guint32 uid;
469
470
471         CAMEL_SUMMARY_LOCK(s, summary_lock);
472
473         uid = s->nextuid++;
474
475         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
476
477         /* FIXME: sync this to disk */
478 /*      summary_header_save(s);*/
479         return uid;
480 }
481
482
483 /**
484  * camel_folder_summary_set_uid:
485  * @summary: a #CamelFolderSummary object
486  * @uid: The next minimum uid to assign.  To avoid clashing
487  * uid's, set this to the uid of a given messages + 1.
488  * 
489  * Set the next minimum uid available.  This can be used to
490  * ensure new uid's do not clash with existing uid's.
491  **/
492 void
493 camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid)
494 {
495         /* TODO: sync to disk? */
496         CAMEL_SUMMARY_LOCK(s, summary_lock);
497
498         s->nextuid = MAX(s->nextuid, uid);
499
500         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
501 }
502
503
504 /**
505  * camel_folder_summary_next_uid_string:
506  * @summary: a #CamelFolderSummary object
507  *
508  * Retrieve the next uid, but as a formatted string.
509  *
510  * Returns the next uid as an unsigned integer string.
511  * This string must be freed by the caller.
512  **/
513 char *
514 camel_folder_summary_next_uid_string(CamelFolderSummary *s)
515 {
516         return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->next_uid_string(s);
517 }
518
519 /* loads the content descriptions, recursively */
520 static CamelMessageContentInfo *
521 perform_content_info_load(CamelFolderSummary *s, FILE *in)
522 {
523         int i;
524         guint32 count;
525         CamelMessageContentInfo *ci, *part;
526
527         ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_load(s, in);
528         if (ci == NULL)
529                 return NULL;
530
531         if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500) {
532                 camel_folder_summary_content_info_free(s, ci);
533                 return NULL;
534         }
535
536         for (i=0;i<count;i++) {
537                 part = perform_content_info_load(s, in);
538                 if (part) {
539                         my_list_append((struct _node **)&ci->childs, (struct _node *)part);
540                         part->parent = ci;
541                 } else {
542                         d(fprintf (stderr, "Summary file format messed up?"));
543                         camel_folder_summary_content_info_free(s, ci);
544                         return NULL;
545                 }
546         }
547         return ci;
548 }
549
550
551 /**
552  * camel_folder_summary_load:
553  * @summary: a #CamelFolderSummary object
554  *
555  * Load the summary from disk.
556  *
557  * Returns %0 on success or %-1 on fail
558  **/
559 int
560 camel_folder_summary_load(CamelFolderSummary *s)
561 {
562         FILE *in;
563         int i;
564         CamelMessageInfo *mi;
565
566         if (s->summary_path == NULL ||
567             s->meta_summary->path == NULL)
568                 return 0;
569
570         in = g_fopen(s->summary_path, "rb");
571         if (in == NULL)
572                 return -1;
573
574         CAMEL_SUMMARY_LOCK(s, io_lock);
575         if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
576                 goto error;
577
578         /* now read in each message ... */
579         for (i=0;i<s->saved_count;i++) {
580                 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_load(s, in);
581
582                 if (mi == NULL)
583                         goto error;
584
585                 /* FIXME: this should be done differently, how i don't know */
586                 if (s->build_content) {
587                         ((CamelMessageInfoBase *)mi)->content = perform_content_info_load(s, in);
588                         if (((CamelMessageInfoBase *)mi)->content == NULL) {
589                                 camel_message_info_free(mi);
590                                 goto error;
591                         }
592                 }
593
594                 camel_folder_summary_add(s, mi);
595         }
596
597         CAMEL_SUMMARY_UNLOCK(s, io_lock);
598         
599         if (fclose (in) != 0)
600                 return -1;
601
602         s->flags &= ~CAMEL_SUMMARY_DIRTY;
603
604         return 0;
605
606 error:
607         if (errno != EINVAL)
608                 g_warning ("Cannot load summary file: `%s': %s", s->summary_path, g_strerror (errno));
609         
610         CAMEL_SUMMARY_UNLOCK(s, io_lock);
611         fclose (in);
612         s->flags |= ~CAMEL_SUMMARY_DIRTY;
613
614         return -1;
615 }
616
617 /* saves the content descriptions, recursively */
618 static int
619 perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
620 {
621         CamelMessageContentInfo *part;
622
623         if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->content_info_save (s, out, ci) == -1)
624                 return -1;
625         
626         if (camel_file_util_encode_uint32 (out, my_list_size ((struct _node **)&ci->childs)) == -1)
627                 return -1;
628         
629         part = ci->childs;
630         while (part) {
631                 if (perform_content_info_save (s, out, part) == -1)
632                         return -1;
633                 part = part->next;
634         }
635         
636         return 0;
637 }
638
639
640 /**
641  * camel_folder_summary_save:
642  * @summary: a #CamelFolderSummary object
643  * 
644  * Writes the summary to disk.  The summary is only written if changes
645  * have occured.
646  * 
647  * Returns %0 on success or %-1 on fail
648  **/
649 int
650 camel_folder_summary_save(CamelFolderSummary *s)
651 {
652         FILE *out;
653         FILE *out_meta;
654         int fd, i, fd_meta;
655         guint32 count;
656         CamelMessageInfo *mi;
657         char *path;
658         char *path_meta;
659
660         g_assert(s->message_info_size >= sizeof(CamelMessageInfoBase));
661
662         if (s->summary_path == NULL 
663             || s->meta_summary->path == NULL
664             || (s->flags & CAMEL_SUMMARY_DIRTY) == 0)
665                 return 0;
666
667         path = alloca(strlen(s->summary_path)+4);
668         sprintf(path, "%s~", s->summary_path);
669         fd = g_open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
670         if (fd == -1)
671                 return -1;
672         out = fdopen(fd, "wb");
673         if (out == NULL) {
674                 i = errno;
675                 g_unlink(path);
676                 close(fd);
677                 errno = i;
678                 return -1;
679         }
680
681         /* Meta summary code */
682         /* This meta summary will be used by beagle in order to 
683            quickly pass through the actual summary file, which 
684            is quite time consuming otherwise.
685         */
686         /* FIXME: Merge meta-summary and summary */
687         path_meta = alloca(strlen(s->meta_summary->path)+4);
688         sprintf(path_meta, "%s~", s->meta_summary->path);
689         fd_meta = g_open(path_meta, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
690         if (fd_meta == -1) {
691                 fclose(out);
692                 return -1;
693         }
694         out_meta = fdopen(fd_meta, "wb");
695         if (out_meta == NULL) {
696                 i = errno;
697                 g_unlink(path);
698                 g_unlink(path_meta);
699                 fclose(out);
700                 close(fd_meta);
701                 errno = i;
702                 return -1;
703         }
704
705         io(printf("saving header\n"));
706
707         CAMEL_SUMMARY_LOCK(s, io_lock);
708
709         if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1)
710                 goto exception;
711         
712         if (summary_meta_header_save(s, out_meta) == -1)
713                 goto exception;
714
715         /* now write out each message ... */
716         /* we check ferorr when done for i/o errors */
717         count = s->messages->len;
718         for (i = 0; i < count; i++) {
719                 mi = s->messages->pdata[i];
720                 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->meta_message_info_save (s, out_meta, out, mi) == -1)
721                         goto exception;
722                 
723                 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->message_info_save (s, out, mi) == -1)
724                         goto exception;
725
726                 if (s->build_content) {
727                         if (perform_content_info_save (s, out, ((CamelMessageInfoBase *)mi)->content) == -1)
728                                 goto exception;
729                 }
730         }
731         
732         /* FIXME: Can't we use the above "fd" variables, instead of fileno()? */
733         if (fflush (out) != 0 || fsync (fileno (out)) == -1)
734                 goto exception;
735         
736         if (fflush (out_meta) != 0 || fsync (fileno (out_meta)) == -1)
737                 goto exception;
738
739         fclose (out);
740         fclose (out_meta);
741
742         CAMEL_SUMMARY_UNLOCK(s, io_lock);
743         
744 #ifdef G_OS_WIN32
745         g_unlink(s->summary_path);
746 #endif
747         if (g_rename(path, s->summary_path) == -1) {
748                 i = errno;
749                 g_unlink(path);
750                 errno = i;
751                 return -1;
752         }
753
754         if (g_rename(path_meta, s->meta_summary->path) == -1) {
755                 i = errno;
756                 g_unlink(path_meta);
757                 errno = i;
758                 return -1;
759         }
760         
761         s->flags &= ~CAMEL_SUMMARY_DIRTY;
762         return 0;
763         
764  exception:
765         
766         i = errno;
767         
768         fclose (out);
769         fclose (out_meta);
770         
771         CAMEL_SUMMARY_UNLOCK(s, io_lock);
772         
773         g_unlink (path);
774         g_unlink (path_meta);
775         errno = i;
776         
777         return -1;
778 }
779
780
781 /**
782  * camel_folder_summary_header_load:
783  * @summary: a #CamelFolderSummary object
784  * 
785  * Only load the header information from the summary,
786  * keep the rest on disk.  This should only be done on
787  * a fresh summary object.
788  * 
789  * Returns %0 on success or %-1 on fail
790  **/
791 int
792 camel_folder_summary_header_load(CamelFolderSummary *s)
793 {
794         FILE *in;
795         FILE *in_meta;
796         int ret;
797
798         if (s->summary_path == NULL ||
799             s->meta_summary->path == NULL)
800                 return 0;
801
802         in = g_fopen(s->summary_path, "rb");
803         if (in == NULL)
804                 return -1;
805
806         in_meta = g_fopen(s->meta_summary->path, "rb");
807         if (in_meta == NULL) {
808                 fclose(in);
809                 return -1;
810         }
811
812         CAMEL_SUMMARY_LOCK(s, io_lock);
813         ret = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in);
814         ret = summary_meta_header_load(s, in_meta);
815         CAMEL_SUMMARY_UNLOCK(s, io_lock);
816         
817         fclose(in);
818         fclose(in_meta);
819         s->flags &= ~CAMEL_SUMMARY_DIRTY;
820         return ret;
821 }
822
823 static int
824 summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info)
825 {
826         const char *uid;
827         CamelMessageInfo *mi;
828
829         uid = camel_message_info_uid(info);
830         if (uid == NULL || uid[0] == 0) {
831                 g_free(info->uid);
832                 uid = info->uid = camel_folder_summary_next_uid_string(s);
833         }
834
835         CAMEL_SUMMARY_LOCK(s, summary_lock);
836
837         while ((mi = g_hash_table_lookup(s->messages_uid, uid))) {
838                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
839                 if (mi == info)
840                         return 0;
841                 d(printf ("Trying to insert message with clashing uid (%s).  new uid re-assigned", camel_message_info_uid(info)));
842                 g_free(info->uid);
843                 uid = info->uid = camel_folder_summary_next_uid_string(s);
844                 camel_message_info_set_flags(info, CAMEL_MESSAGE_FOLDER_FLAGGED, CAMEL_MESSAGE_FOLDER_FLAGGED);
845                 CAMEL_SUMMARY_LOCK(s, summary_lock);
846         }
847
848         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
849
850         return 1;
851 }
852
853
854 /**
855  * camel_folder_summary_add:
856  * @summary: a #CamelFolderSummary object
857  * @info: a #CamelMessageInfo
858  * 
859  * Adds a new @info record to the summary.  If @info->uid is %NULL,
860  * then a new uid is automatically re-assigned by calling
861  * #camel_folder_summary_next_uid_string.
862  *
863  * The @info record should have been generated by calling one of the
864  * info_new_*() functions, as it will be free'd based on the summary
865  * class.  And MUST NOT be allocated directly using malloc.
866  **/
867 void
868 camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info)
869 {
870         if (info == NULL)
871                 return;
872
873         if (summary_assign_uid(s, info) == 0)
874                 return;
875
876         CAMEL_SUMMARY_LOCK(s, summary_lock);
877
878 /* unnecessary for pooled vectors */
879 #ifdef DOESTRV
880         /* this is vitally important, and also if this is ever modified, then
881            the hash table needs to be resynced */
882         info->strings = e_strv_pack(info->strings);
883 #endif
884
885         g_ptr_array_add(s->messages, info);
886         g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info);
887         s->flags |= CAMEL_SUMMARY_DIRTY;
888
889         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
890 }
891
892
893 /**
894  * camel_folder_summary_add_from_header:
895  * @summary: a #CamelFolderSummary object
896  * @headers: rfc822 headers
897  * 
898  * Build a new info record based on a set of headers, and add it to
899  * the summary.
900  *
901  * Note that this function should not be used if build_content_info
902  * has been specified for this summary.
903  * 
904  * Returns the newly added record
905  **/
906 CamelMessageInfo *
907 camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
908 {
909         CamelMessageInfo *info = camel_folder_summary_info_new_from_header(s, h);
910
911         camel_folder_summary_add(s, info);
912
913         return info;
914 }
915
916
917 /**
918  * camel_folder_summary_add_from_parser:
919  * @summary: a #CamelFolderSummary object
920  * @parser: a #CamelMimeParser object
921  * 
922  * Build a new info record based on the current position of a #CamelMimeParser.
923  *
924  * The parser should be positioned before the start of the message to summarise.
925  * This function may be used if build_contnet_info or an index has been
926  * specified for the summary.
927  * 
928  * Returns the newly added record
929  **/
930 CamelMessageInfo *
931 camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
932 {
933         CamelMessageInfo *info;
934
935         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (s), NULL);
936         g_return_val_if_fail (CAMEL_IS_MIME_PARSER (mp), NULL);
937
938         info = camel_folder_summary_info_new_from_parser(s, mp);
939
940         camel_folder_summary_add(s, info);
941
942         return info;
943 }
944
945
946 /**
947  * camel_folder_summary_add_from_message:
948  * @summary: a #CamelFolderSummary object
949  * @message: a #CamelMimeMessage object
950  * 
951  * Add a summary item from an existing message.
952  * 
953  * Returns the newly added record
954  **/
955 CamelMessageInfo *
956 camel_folder_summary_add_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
957 {
958         CamelMessageInfo *info = camel_folder_summary_info_new_from_message(s, msg);
959
960         camel_folder_summary_add(s, info);
961
962         return info;
963 }
964
965
966 /**
967  * camel_folder_summary_info_new_from_header:
968  * @summary: a #CamelFolderSummary object
969  * @headers: rfc822 headers
970  * 
971  * Create a new info record from a header.
972  *
973  * Returns the newly allocated record which must be freed with
974  * #camel_message_info_free
975  **/
976 CamelMessageInfo *
977 camel_folder_summary_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
978 {
979         return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, h);
980 }
981
982
983 /**
984  * camel_folder_summary_info_new_from_parser:
985  * @summary: a #CamelFolderSummary object
986  * @parser: a #CamelMimeParser object
987  * 
988  * Create a new info record from a parser.  If the parser cannot
989  * determine a uid, then none will be assigned.
990  *
991  * If indexing is enabled, and the parser cannot determine a new uid, then
992  * one is automatically assigned.
993  *
994  * If indexing is enabled, then the content will be indexed based
995  * on this new uid.  In this case, the message info MUST be
996  * added using :add().
997  *
998  * Once complete, the parser will be positioned at the end of
999  * the message.
1000  *
1001  * Returns the newly allocated record which must be freed with
1002  * #camel_message_info_free
1003  **/
1004 CamelMessageInfo *
1005 camel_folder_summary_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1006 {
1007         CamelMessageInfo *info = NULL;
1008         char *buffer;
1009         size_t len;
1010         struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
1011         off_t start;
1012         CamelIndexName *name = NULL;
1013
1014         /* should this check the parser is in the right state, or assume it is?? */
1015
1016         start = camel_mime_parser_tell(mp);
1017         if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_EOF) {
1018                 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_parser(s, mp);
1019
1020                 camel_mime_parser_unstep(mp);
1021
1022                 /* assign a unique uid, this is slightly 'wrong' as we do not really
1023                  * know if we are going to store this in the summary, but no matter */
1024                 if (p->index)
1025                         summary_assign_uid(s, info);
1026
1027                 CAMEL_SUMMARY_LOCK(s, filter_lock);
1028
1029                 if (p->index) {
1030                         if (p->filter_index == NULL)
1031                                 p->filter_index = camel_mime_filter_index_new_index(p->index);
1032                         camel_index_delete_name(p->index, camel_message_info_uid(info));
1033                         name = camel_index_add_name(p->index, camel_message_info_uid(info));
1034                         camel_mime_filter_index_set_name(p->filter_index, name);
1035                 }
1036
1037                 /* always scan the content info, even if we dont save it */
1038                 ((CamelMessageInfoBase *)info)->content = summary_build_content_info(s, info, mp);
1039
1040                 if (name && p->index) {
1041                         camel_index_write_name(p->index, name);
1042                         camel_object_unref((CamelObject *)name);
1043                         camel_mime_filter_index_set_name(p->filter_index, NULL);
1044                 }
1045
1046                 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1047
1048                 ((CamelMessageInfoBase *)info)->size = camel_mime_parser_tell(mp) - start;
1049         }
1050         return info;
1051 }
1052
1053
1054 /**
1055  * camel_folder_summary_info_new_from_message:
1056  * @summary: a #CamelFodlerSummary object
1057  * @message: a #CamelMimeMessage object
1058  * 
1059  * Create a summary item from a message.
1060  * 
1061  * Returns the newly allocated record which must be freed using
1062  * #camel_message_info_free
1063  **/
1064 CamelMessageInfo *
1065 camel_folder_summary_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1066 {
1067         CamelMessageInfo *info;
1068         struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
1069         CamelIndexName *name = NULL;
1070
1071         info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_message(s, msg);
1072
1073         /* assign a unique uid, this is slightly 'wrong' as we do not really
1074          * know if we are going to store this in the summary, but we need it set for indexing */
1075         if (p->index)
1076                 summary_assign_uid(s, info);
1077
1078         CAMEL_SUMMARY_LOCK(s, filter_lock);
1079
1080         if (p->index) {
1081                 if (p->filter_index == NULL)
1082                         p->filter_index = camel_mime_filter_index_new_index(p->index);
1083                 camel_index_delete_name(p->index, camel_message_info_uid(info));
1084                 name = camel_index_add_name(p->index, camel_message_info_uid(info));
1085                 camel_mime_filter_index_set_name(p->filter_index, name);
1086
1087                 if (p->filter_stream == NULL) {
1088                         CamelStream *null = camel_stream_null_new();
1089
1090                         p->filter_stream = camel_stream_filter_new_with_stream(null);
1091                         camel_object_unref((CamelObject *)null);
1092                 }
1093         }
1094
1095         ((CamelMessageInfoBase *)info)->content = summary_build_content_info_message(s, info, (CamelMimePart *)msg);
1096
1097         if (name) {
1098                 camel_index_write_name(p->index, name);
1099                 camel_object_unref((CamelObject *)name);
1100                 camel_mime_filter_index_set_name(p->filter_index, NULL);
1101         }
1102
1103         CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1104
1105         return info;
1106 }
1107
1108
1109 /**
1110  * camel_folder_summary_content_info_free:
1111  * @summary: a #CamelFolderSummary object
1112  * @ci: a #CamelMessageContentInfo
1113  * 
1114  * Free the content info @ci, and all associated memory.
1115  **/
1116 void
1117 camel_folder_summary_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1118 {
1119         CamelMessageContentInfo *pw, *pn;
1120
1121         pw = ci->childs;
1122         ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_free(s, ci);
1123         while (pw) {
1124                 pn = pw->next;
1125                 camel_folder_summary_content_info_free(s, pw);
1126                 pw = pn;
1127         }
1128 }
1129
1130
1131 /**
1132  * camel_folder_summary_touch:
1133  * @summary: a #CamelFolderSummary object
1134  * 
1135  * Mark the summary as changed, so that a save will force it to be
1136  * written back to disk.
1137  **/
1138 void
1139 camel_folder_summary_touch(CamelFolderSummary *s)
1140 {
1141         CAMEL_SUMMARY_LOCK(s, summary_lock);
1142         s->flags |= CAMEL_SUMMARY_DIRTY;
1143         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1144 }
1145
1146
1147 /**
1148  * camel_folder_summary_clear:
1149  * @summary: a #CamelFolderSummary object
1150  * 
1151  * Empty the summary contents.
1152  **/
1153 void
1154 camel_folder_summary_clear(CamelFolderSummary *s)
1155 {
1156         int i;
1157
1158         CAMEL_SUMMARY_LOCK(s, summary_lock);
1159         if (camel_folder_summary_count(s) == 0) {
1160                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1161                 return;
1162         }
1163
1164         for (i=0;i<s->messages->len;i++)
1165                 camel_message_info_free(s->messages->pdata[i]);
1166
1167         g_ptr_array_set_size(s->messages, 0);
1168         g_hash_table_destroy(s->messages_uid);
1169         s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
1170         s->flags |= CAMEL_SUMMARY_DIRTY;
1171         s->meta_summary->msg_expunged = TRUE;
1172         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1173 }
1174
1175
1176 /**
1177  * camel_folder_summary_remove:
1178  * @summary: a #CamelFolderSummary object
1179  * @info: a #CamelMessageInfo
1180  * 
1181  * Remove a specific @info record from the summary.
1182  **/
1183 void
1184 camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info)
1185 {
1186         CAMEL_SUMMARY_LOCK(s, summary_lock);
1187         g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1188         g_ptr_array_remove(s->messages, info);
1189         s->flags |= CAMEL_SUMMARY_DIRTY;
1190         s->meta_summary->msg_expunged = TRUE;
1191         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1192         
1193         camel_message_info_free(info);
1194 }
1195
1196
1197 /**
1198  * camel_folder_summary_remove_uid:
1199  * @summary: a #CamelFolderSummary object
1200  * @uid: a uid
1201  * 
1202  * Remove a specific info record from the summary, by @uid.
1203  **/
1204 void
1205 camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid)
1206 {
1207         CamelMessageInfo *oldinfo;
1208         char *olduid;
1209
1210         CAMEL_SUMMARY_LOCK(s, summary_lock);
1211         CAMEL_SUMMARY_LOCK(s, ref_lock);
1212         if (g_hash_table_lookup_extended(s->messages_uid, uid, (void *)&olduid, (void *)&oldinfo)) {
1213                 /* make sure it doesn't vanish while we're removing it */
1214                 oldinfo->refcount++;
1215                 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1216                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1217                 camel_folder_summary_remove(s, oldinfo);
1218                 camel_message_info_free(oldinfo);
1219         } else {
1220                 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1221                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1222         }
1223 }
1224
1225
1226 /**
1227  * camel_folder_summary_remove_index:
1228  * @summary: a #CamelFolderSummary object
1229  * @index: record index
1230  * 
1231  * Remove a specific info record from the summary, by index.
1232  **/
1233 void
1234 camel_folder_summary_remove_index(CamelFolderSummary *s, int index)
1235 {
1236         CAMEL_SUMMARY_LOCK(s, summary_lock);
1237         if (index < s->messages->len) {
1238                 CamelMessageInfo *info = s->messages->pdata[index];
1239
1240                 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1241                 g_ptr_array_remove_index(s->messages, index);
1242                 s->flags |= CAMEL_SUMMARY_DIRTY;
1243
1244                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1245                 camel_message_info_free(info);
1246         } else {
1247                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1248         }
1249 }
1250
1251
1252 /**
1253  * camel_folder_summary_remove_range:
1254  * @summary: a #CamelFolderSummary object
1255  * @start: initial index
1256  * @end: last index to remove
1257  * 
1258  * Removes an indexed range of info records.
1259  **/
1260 void
1261 camel_folder_summary_remove_range(CamelFolderSummary *s, int start, int end)
1262 {
1263         if (end < start)
1264                 return;
1265
1266         CAMEL_SUMMARY_LOCK(s, summary_lock);
1267         if (start < s->messages->len) {
1268                 CamelMessageInfo **infos;
1269                 int i;
1270
1271                 end = MIN(end+1, s->messages->len);
1272                 infos = g_malloc((end-start)*sizeof(infos[0]));
1273
1274                 for (i=start;i<end;i++) {
1275                         CamelMessageInfo *info = s->messages->pdata[i];
1276
1277                         infos[i-start] = info;
1278                         g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1279                 }
1280
1281                 memmove(s->messages->pdata+start, s->messages->pdata+end, (s->messages->len-end)*sizeof(s->messages->pdata[0]));
1282                 g_ptr_array_set_size(s->messages, s->messages->len - (end - start));
1283                 s->flags |= CAMEL_SUMMARY_DIRTY;
1284
1285                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1286
1287                 for (i=start;i<end;i++)
1288                         camel_message_info_free(infos[i-start]);
1289                 g_free(infos);
1290         } else {
1291                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1292         }
1293 }
1294
1295 /* should be sorted, for binary search */
1296 /* This is a tokenisation mechanism for strings written to the
1297    summary - to save space.
1298    This list can have at most 31 words. */
1299 static char * tokens[] = {
1300         "7bit",
1301         "8bit",
1302         "alternative",
1303         "application",
1304         "base64",
1305         "boundary",
1306         "charset",
1307         "filename",
1308         "html",
1309         "image",
1310         "iso-8859-1",
1311         "iso-8859-8",
1312         "message",
1313         "mixed",
1314         "multipart",
1315         "name",
1316         "octet-stream",
1317         "parallel",
1318         "plain",
1319         "postscript",
1320         "quoted-printable",
1321         "related",
1322         "rfc822",
1323         "text",
1324         "us-ascii",             /* 25 words */
1325 };
1326
1327 #define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
1328
1329 /* baiscally ...
1330     0 = null
1331     1-tokens_len == tokens[id-1]
1332     >=32 string, length = n-32
1333 */
1334
1335 #ifdef USE_BSEARCH
1336 static int
1337 token_search_cmp(char *key, char **index)
1338 {
1339         d(printf("comparing '%s' to '%s'\n", key, *index));
1340         return strcmp(key, *index);
1341 }
1342 #endif
1343
1344 /**
1345  * camel_folder_summary_encode_token:
1346  * @out: output FILE pointer
1347  * @str: string token to encode
1348  * 
1349  * Encode a string value, but use tokenisation and compression
1350  * to reduce the size taken for common mailer words.  This
1351  * can still be used to encode normal strings as well.
1352  * 
1353  * Returns %0 on success or %-1 on fail
1354  **/
1355 int
1356 camel_folder_summary_encode_token(FILE *out, const char *str)
1357 {
1358         io(printf("Encoding token: '%s'\n", str));
1359
1360         if (str == NULL) {
1361                 return camel_file_util_encode_uint32(out, 0);
1362         } else {
1363                 int len = strlen(str);
1364                 int i, token=-1;
1365
1366                 if (len <= 16) {
1367                         char lower[32];
1368                         char **match;
1369
1370                         for (i=0;i<len;i++)
1371                                 lower[i] = tolower(str[i]);
1372                         lower[i] = 0;
1373 #ifdef USE_BSEARCH
1374                         match = bsearch(lower, tokens, tokens_len, sizeof(char *), (int (*)(const void *, const void *))token_search_cmp);
1375                         if (match)
1376                                 token = match-tokens;
1377 #else
1378                         for (i=0;i<tokens_len;i++) {
1379                                 if (!strcmp(tokens[i], lower)) {
1380                                         token = i;
1381                                         break;
1382                                 }
1383                         }
1384 #endif
1385                 }
1386                 if (token != -1) {
1387                         return camel_file_util_encode_uint32(out, token+1);
1388                 } else {
1389                         if (camel_file_util_encode_uint32(out, len+32) == -1)
1390                                 return -1;
1391                         if (fwrite(str, len, 1, out) != 1)
1392                                 return -1;
1393                 }
1394         }
1395         return 0;
1396 }
1397
1398
1399 /**
1400  * camel_folder_summary_decode_token:
1401  * @in: input FILE pointer
1402  * @str: string pointer to hold the decoded result
1403  * 
1404  * Decode a token value.
1405  * 
1406  * Returns %0 on success or %-1 on fail
1407  **/
1408 int
1409 camel_folder_summary_decode_token(FILE *in, char **str)
1410 {
1411         char *ret;
1412         guint32 len;
1413
1414         io(printf("Decode token ...\n"));
1415         
1416         if (camel_file_util_decode_uint32(in, &len) == -1) {
1417                 io(printf ("Could not decode token from file"));
1418                 *str = NULL;
1419                 return -1;
1420         }
1421
1422         if (len<32) {
1423                 if (len <= 0) {
1424                         ret = NULL;
1425                 } else if (len<= tokens_len) {
1426                         ret = g_strdup(tokens[len-1]);
1427                 } else {
1428                         io(printf ("Invalid token encountered: %d", len));
1429                         *str = NULL;
1430                         return -1;
1431                 }
1432         } else if (len > 10240) {
1433                 io(printf ("Got broken string header length: %d bytes", len));
1434                 *str = NULL;
1435                 return -1;
1436         } else {
1437                 len -= 32;
1438                 ret = g_malloc(len+1);
1439                 if (len > 0 && fread(ret, len, 1, in) != 1) {
1440                         g_free(ret);
1441                         *str = NULL;
1442                         return -1;
1443                 }
1444                 ret[len]=0;
1445         }
1446
1447         io(printf("Token = '%s'\n", ret));
1448
1449         *str = ret;
1450         return 0;
1451 }
1452
1453 static struct _node *
1454 my_list_append(struct _node **list, struct _node *n)
1455 {
1456         struct _node *ln = (struct _node *)list;
1457         while (ln->next)
1458                 ln = ln->next;
1459         n->next = 0;
1460         ln->next = n;
1461         return n;
1462 }
1463
1464 static int
1465 my_list_size(struct _node **list)
1466 {
1467         int len = 0;
1468         struct _node *ln = (struct _node *)list;
1469         while (ln->next) {
1470                 ln = ln->next;
1471                 len++;
1472         }
1473         return len;
1474 }
1475
1476 static int
1477 summary_meta_header_load(CamelFolderSummary *s, FILE *in)
1478 {
1479         if (!s->meta_summary->path)
1480                 return -1;
1481
1482         fseek(in, 0, SEEK_SET);
1483
1484         io(printf("Loading meta-header\n"));
1485
1486         if (camel_file_util_decode_uint32(in, &s->meta_summary->major) == -1
1487             || camel_file_util_decode_uint32(in, &s->meta_summary->minor) == -1
1488             || camel_file_util_decode_uint32(in, &s->meta_summary->uid_len) == -1) {
1489                 return -1;
1490         }
1491         
1492         return 0;
1493 }
1494
1495 static int
1496 summary_header_load(CamelFolderSummary *s, FILE *in)
1497 {
1498         if (!s->summary_path)
1499                 return -1;
1500
1501         fseek(in, 0, SEEK_SET);
1502
1503         io(printf("Loading header\n"));
1504
1505         if (camel_file_util_decode_fixed_int32(in, &s->version) == -1)
1506                 return -1;
1507
1508         /* Legacy version check, before version 12 we have no upgrade knowledge */
1509         if ((s->version > 0xff) && (s->version & 0xff) < 12) {
1510                 io(printf ("Summary header version mismatch"));
1511                 errno = EINVAL;
1512                 return -1;
1513         }
1514
1515         if (!(s->version < 0x100 && s->version >= 13))
1516                 io(printf("Loading legacy summary\n"));
1517         else
1518                 io(printf("loading new-format summary\n"));
1519
1520         /* legacy version */
1521         if (camel_file_util_decode_fixed_int32(in, &s->flags) == -1
1522             || camel_file_util_decode_fixed_int32(in, &s->nextuid) == -1
1523             || camel_file_util_decode_time_t(in, &s->time) == -1
1524             || camel_file_util_decode_fixed_int32(in, &s->saved_count) == -1) {
1525                 return -1;
1526         }
1527
1528         /* version 13 */
1529         if (s->version < 0x100 && s->version >= 13
1530             && (camel_file_util_decode_fixed_int32(in, &s->unread_count) == -1
1531                 || camel_file_util_decode_fixed_int32(in, &s->deleted_count) == -1
1532                 || camel_file_util_decode_fixed_int32(in, &s->junk_count) == -1)) {
1533                 return -1;
1534         }
1535
1536         return 0;
1537 }
1538
1539 static int
1540 summary_header_save(CamelFolderSummary *s, FILE *out)
1541 {
1542         int unread = 0, deleted = 0, junk = 0, count, i;
1543
1544         fseek(out, 0, SEEK_SET);
1545
1546         io(printf("Savining header\n"));
1547
1548         /* we always write out the current version */
1549         camel_file_util_encode_fixed_int32(out, CAMEL_FOLDER_SUMMARY_VERSION);
1550         camel_file_util_encode_fixed_int32(out, s->flags);
1551         camel_file_util_encode_fixed_int32(out, s->nextuid);
1552         camel_file_util_encode_time_t(out, s->time);
1553
1554         count = camel_folder_summary_count(s);
1555         for (i=0; i<count; i++) {
1556                 CamelMessageInfo *info = camel_folder_summary_index(s, i);
1557                 guint32 flags;
1558
1559                 if (info == NULL)
1560                         continue;
1561
1562                 flags = camel_message_info_flags(info);
1563                 if ((flags & CAMEL_MESSAGE_SEEN) == 0)
1564                         unread++;
1565                 if ((flags & CAMEL_MESSAGE_DELETED) != 0)
1566                         deleted++;
1567                 if ((flags & CAMEL_MESSAGE_JUNK) != 0)
1568                         junk++;
1569
1570                 camel_message_info_free(info);
1571         }
1572
1573         camel_file_util_encode_fixed_int32(out, count);
1574         camel_file_util_encode_fixed_int32(out, unread);
1575         camel_file_util_encode_fixed_int32(out, deleted);
1576
1577         return camel_file_util_encode_fixed_int32(out, junk);
1578 }
1579
1580 static int
1581 summary_meta_header_save(CamelFolderSummary *s, FILE *out_meta)
1582 {
1583         fseek(out_meta, 0, SEEK_SET);
1584
1585         /* Save meta-summary header */
1586         if (s->meta_summary->msg_expunged) {
1587                 s->meta_summary->msg_expunged = FALSE;
1588                 camel_file_util_encode_uint32(out_meta, ++s->meta_summary->major);
1589                 camel_file_util_encode_uint32(out_meta, (s->meta_summary->minor=0));
1590         } else {
1591                 camel_file_util_encode_uint32(out_meta, s->meta_summary->major);
1592                 camel_file_util_encode_uint32(out_meta, ++s->meta_summary->minor);
1593         }
1594         camel_file_util_encode_uint32(out_meta, s->meta_summary->uid_len);
1595
1596         return ferror(out_meta);
1597 }
1598
1599 /* are these even useful for anything??? */
1600 static CamelMessageInfo *
1601 message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1602 {
1603         CamelMessageInfo *mi = NULL;
1604         int state;
1605
1606         state = camel_mime_parser_state(mp);
1607         switch (state) {
1608         case CAMEL_MIME_PARSER_STATE_HEADER:
1609         case CAMEL_MIME_PARSER_STATE_MESSAGE:
1610         case CAMEL_MIME_PARSER_STATE_MULTIPART:
1611                 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, camel_mime_parser_headers_raw(mp));
1612                 break;
1613         default:
1614                 g_error("Invalid parser state");
1615         }
1616
1617         return mi;
1618 }
1619
1620 static CamelMessageContentInfo *
1621 content_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1622 {
1623         CamelMessageContentInfo *ci = NULL;
1624
1625         switch (camel_mime_parser_state(mp)) {
1626         case CAMEL_MIME_PARSER_STATE_HEADER:
1627         case CAMEL_MIME_PARSER_STATE_MESSAGE:
1628         case CAMEL_MIME_PARSER_STATE_MULTIPART:
1629                 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_header(s, camel_mime_parser_headers_raw(mp));
1630                 if (ci) {
1631                         ci->type = camel_mime_parser_content_type(mp);
1632                         camel_content_type_ref(ci->type);
1633                 }
1634                 break;
1635         default:
1636                 g_error("Invalid parser state");
1637         }
1638
1639         return ci;
1640 }
1641
1642 static CamelMessageInfo *
1643 message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1644 {
1645         CamelMessageInfo *mi;
1646
1647         mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, ((CamelMimePart *)msg)->headers);
1648
1649         return mi;
1650 }
1651
1652 static CamelMessageContentInfo *
1653 content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp)
1654 {
1655         CamelMessageContentInfo *ci;
1656
1657         ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_header(s, mp->headers);
1658
1659         return ci;
1660 }
1661
1662 static char *
1663 summary_format_address(struct _camel_header_raw *h, const char *name, const char *charset)
1664 {
1665         struct _camel_header_address *addr;
1666         const char *text;
1667         char *ret;
1668
1669         text = camel_header_raw_find (&h, name, NULL);
1670         addr = camel_header_address_decode (text, charset);
1671         if (addr) {
1672                 ret = camel_header_address_list_format (addr);
1673                 camel_header_address_list_clear (&addr);
1674         } else {
1675                 ret = g_strdup (text);
1676         }
1677         
1678         return ret;
1679 }
1680
1681 static char *
1682 summary_format_string (struct _camel_header_raw *h, const char *name, const char *charset)
1683 {
1684         const char *text;
1685         
1686         text = camel_header_raw_find (&h, name, NULL);
1687         if (text) {
1688                 while (isspace ((unsigned) *text))
1689                         text++;
1690                 return camel_header_decode_string (text, charset);
1691         } else {
1692                 return NULL;
1693         }
1694 }
1695
1696
1697 /**
1698  * camel_folder_summary_content_info_new:
1699  * @summary: a #CamelFolderSummary object
1700  * 
1701  * Allocate a new #CamelMessageContentInfo, suitable for adding
1702  * to this summary.
1703  * 
1704  * Returns a newly allocated #CamelMessageContentInfo
1705  **/
1706 CamelMessageContentInfo *
1707 camel_folder_summary_content_info_new(CamelFolderSummary *s)
1708 {
1709         CamelMessageContentInfo *ci;
1710
1711         CAMEL_SUMMARY_LOCK(s, alloc_lock);
1712         if (s->content_info_chunks == NULL)
1713                 s->content_info_chunks = e_memchunk_new(32, s->content_info_size);
1714         ci = e_memchunk_alloc(s->content_info_chunks);
1715         CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
1716
1717         memset(ci, 0, s->content_info_size);
1718         return ci;
1719 }
1720
1721 static CamelMessageInfo *
1722 message_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
1723 {
1724         CamelMessageInfoBase *mi;
1725         const char *received;
1726         guchar digest[16];
1727         struct _camel_header_references *refs, *irt, *scan;
1728         char *msgid;
1729         int count;
1730         char *subject, *from, *to, *cc, *mlist;
1731         CamelContentType *ct = NULL;
1732         const char *content, *charset = NULL;
1733
1734         mi = (CamelMessageInfoBase *)camel_message_info_new(s);
1735
1736         if ((content = camel_header_raw_find(&h, "Content-Type", NULL))
1737              && (ct = camel_content_type_decode(content))
1738              && (charset = camel_content_type_param(ct, "charset"))
1739              && (g_ascii_strcasecmp(charset, "us-ascii") == 0))
1740                 charset = NULL;
1741         
1742         charset = charset ? e_iconv_charset_name (charset) : NULL;
1743         
1744         subject = summary_format_string(h, "subject", charset);
1745         from = summary_format_address(h, "from", charset);
1746         to = summary_format_address(h, "to", charset);
1747         cc = summary_format_address(h, "cc", charset);
1748         mlist = camel_header_raw_check_mailing_list(&h);
1749
1750         if (ct)
1751                 camel_content_type_unref(ct);
1752         
1753         mi->subject = camel_pstring_add (subject, TRUE);
1754         mi->from = camel_pstring_add (from, TRUE);
1755         mi->to = camel_pstring_add (to, TRUE);
1756         mi->cc = camel_pstring_add (cc, TRUE);
1757         mi->mlist = camel_pstring_add (mlist, TRUE);
1758         
1759         mi->user_flags = NULL;
1760         mi->user_tags = NULL;
1761         mi->date_sent = camel_header_decode_date(camel_header_raw_find(&h, "date", NULL), NULL);
1762         received = camel_header_raw_find(&h, "received", NULL);
1763         if (received)
1764                 received = strrchr(received, ';');
1765         if (received)
1766                 mi->date_received = camel_header_decode_date(received + 1, NULL);
1767         else
1768                 mi->date_received = 0;
1769
1770         msgid = camel_header_msgid_decode(camel_header_raw_find(&h, "message-id", NULL));
1771         if (msgid) {
1772                 md5_get_digest(msgid, strlen(msgid), digest);
1773                 memcpy(mi->message_id.id.hash, digest, sizeof(mi->message_id.id.hash));
1774                 g_free(msgid);
1775         }
1776         
1777         /* decode our references and in-reply-to headers */
1778         refs = camel_header_references_decode (camel_header_raw_find (&h, "references", NULL));
1779         irt = camel_header_references_inreplyto_decode (camel_header_raw_find (&h, "in-reply-to", NULL));
1780         if (refs || irt) {
1781                 if (irt) {
1782                         /* The References field is populated from the ``References'' and/or ``In-Reply-To''
1783                            headers. If both headers exist, take the first thing in the In-Reply-To header
1784                            that looks like a Message-ID, and append it to the References header. */
1785                         
1786                         if (refs)
1787                                 irt->next = refs;
1788                         
1789                         refs = irt;
1790                 }
1791                 
1792                 count = camel_header_references_list_size(&refs);
1793                 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1794                 count = 0;
1795                 scan = refs;
1796                 while (scan) {
1797                         md5_get_digest(scan->id, strlen(scan->id), digest);
1798                         memcpy(mi->references->references[count].id.hash, digest, sizeof(mi->message_id.id.hash));
1799                         count++;
1800                         scan = scan->next;
1801                 }
1802                 mi->references->size = count;
1803                 camel_header_references_list_clear(&refs);
1804         }
1805
1806         return (CamelMessageInfo *)mi;
1807 }
1808
1809 static CamelMessageInfo *
1810 message_info_load(CamelFolderSummary *s, FILE *in)
1811 {
1812         CamelMessageInfoBase *mi;
1813         guint count;
1814         int i;
1815         char *subject, *from, *to, *cc, *mlist, *uid;
1816
1817         mi = (CamelMessageInfoBase *)camel_message_info_new(s);
1818
1819         io(printf("Loading message info\n"));
1820
1821         camel_file_util_decode_string(in, &uid);
1822         camel_file_util_decode_uint32(in, &mi->flags);
1823         camel_file_util_decode_uint32(in, &mi->size);
1824         camel_file_util_decode_time_t(in, &mi->date_sent);
1825         camel_file_util_decode_time_t(in, &mi->date_received);
1826         camel_file_util_decode_string(in, &subject);
1827         camel_file_util_decode_string(in, &from);
1828         camel_file_util_decode_string(in, &to);
1829         camel_file_util_decode_string(in, &cc);
1830         camel_file_util_decode_string(in, &mlist);
1831         
1832         mi->uid = uid;
1833         mi->subject = camel_pstring_add (subject, TRUE);
1834         mi->from = camel_pstring_add (from, TRUE);
1835         mi->to = camel_pstring_add (to, TRUE);
1836         mi->cc = camel_pstring_add (cc, TRUE);
1837         mi->mlist = camel_pstring_add (mlist, TRUE);
1838         
1839         mi->content = NULL;
1840
1841         camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.hi);
1842         camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.lo);
1843
1844         if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1845                 goto error;
1846
1847         if (count > 0) {
1848                 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1849                 mi->references->size = count;
1850                 for (i=0;i<count;i++) {
1851                         camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.hi);
1852                         camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.lo);
1853                 }
1854         }
1855
1856         if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1857                 goto error;
1858
1859         for (i=0;i<count;i++) {
1860                 char *name;
1861                 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL)
1862                         goto error;
1863                 camel_flag_set(&mi->user_flags, name, TRUE);
1864                 g_free(name);
1865         }
1866
1867         if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1868                 goto error;
1869
1870         for (i=0;i<count;i++) {
1871                 char *name, *value;
1872                 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL
1873                     || camel_file_util_decode_string(in, &value) == -1)
1874                         goto error;
1875                 camel_tag_set(&mi->user_tags, name, value);
1876                 g_free(name);
1877                 g_free(value);
1878         }
1879
1880         if (!ferror(in))
1881                 return (CamelMessageInfo *)mi;
1882
1883 error:
1884         camel_message_info_free((CamelMessageInfo *)mi);
1885
1886         return NULL;
1887 }
1888
1889 static int
1890 meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *info)
1891 {
1892         time_t timestamp;
1893         off_t offset;
1894         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1895
1896         time (&timestamp);
1897         offset = ftell (out);
1898         /* FIXME: errno check after ftell */
1899         
1900         camel_file_util_encode_time_t(out_meta, timestamp);
1901         camel_file_util_encode_fixed_string(out_meta, camel_message_info_uid(mi), s->meta_summary->uid_len);
1902         camel_file_util_encode_uint32(out_meta, mi->flags);
1903         camel_file_util_encode_off_t(out_meta, offset); 
1904
1905         return ferror(out);
1906 }
1907
1908 static int
1909 message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
1910 {
1911         guint32 count;
1912         CamelFlag *flag;
1913         CamelTag *tag;
1914         int i;
1915         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1916
1917         io(printf("Saving message info\n"));
1918
1919         camel_file_util_encode_string(out, camel_message_info_uid(mi));
1920         camel_file_util_encode_uint32(out, mi->flags);
1921         camel_file_util_encode_uint32(out, mi->size);
1922         camel_file_util_encode_time_t(out, mi->date_sent);
1923         camel_file_util_encode_time_t(out, mi->date_received);
1924         camel_file_util_encode_string(out, camel_message_info_subject(mi));
1925         camel_file_util_encode_string(out, camel_message_info_from(mi));
1926         camel_file_util_encode_string(out, camel_message_info_to(mi));
1927         camel_file_util_encode_string(out, camel_message_info_cc(mi));
1928         camel_file_util_encode_string(out, camel_message_info_mlist(mi));
1929
1930         camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.hi);
1931         camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.lo);
1932
1933         if (mi->references) {
1934                 camel_file_util_encode_uint32(out, mi->references->size);
1935                 for (i=0;i<mi->references->size;i++) {
1936                         camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.hi);
1937                         camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.lo);
1938                 }
1939         } else {
1940                 camel_file_util_encode_uint32(out, 0);
1941         }
1942
1943         count = camel_flag_list_size(&mi->user_flags);
1944         camel_file_util_encode_uint32(out, count);
1945         flag = mi->user_flags;
1946         while (flag) {
1947                 camel_file_util_encode_string(out, flag->name);
1948                 flag = flag->next;
1949         }
1950
1951         count = camel_tag_list_size(&mi->user_tags);
1952         camel_file_util_encode_uint32(out, count);
1953         tag = mi->user_tags;
1954         while (tag) {
1955                 camel_file_util_encode_string(out, tag->name);
1956                 camel_file_util_encode_string(out, tag->value);
1957                 tag = tag->next;
1958         }
1959
1960         return ferror(out);
1961 }
1962
1963 static void
1964 message_info_free(CamelFolderSummary *s, CamelMessageInfo *info)
1965 {
1966         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1967
1968         g_free(mi->uid);
1969         camel_pstring_free(mi->subject);
1970         camel_pstring_free(mi->from);
1971         camel_pstring_free(mi->to);
1972         camel_pstring_free(mi->cc);
1973         camel_pstring_free(mi->mlist);
1974         g_free(mi->references);
1975         camel_flag_list_free(&mi->user_flags);
1976         camel_tag_list_free(&mi->user_tags);
1977         if (s)
1978                 e_memchunk_free(s->message_info_chunks, mi);
1979         else
1980                 g_free(mi);
1981 }
1982
1983 static CamelMessageContentInfo *
1984 content_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
1985 {
1986         CamelMessageContentInfo *ci;
1987         const char *charset;
1988         
1989         ci = camel_folder_summary_content_info_new (s);
1990         
1991         charset = e_iconv_locale_charset ();
1992         ci->id = camel_header_msgid_decode (camel_header_raw_find (&h, "content-id", NULL));
1993         ci->description = camel_header_decode_string (camel_header_raw_find (&h, "content-description", NULL), charset);
1994         ci->encoding = camel_content_transfer_encoding_decode (camel_header_raw_find (&h, "content-transfer-encoding", NULL));
1995         ci->type = camel_content_type_decode(camel_header_raw_find(&h, "content-type", NULL));
1996
1997         return ci;
1998 }
1999
2000 static CamelMessageContentInfo *
2001 content_info_load(CamelFolderSummary *s, FILE *in)
2002 {
2003         CamelMessageContentInfo *ci;
2004         char *type, *subtype;
2005         guint32 count, i;
2006         CamelContentType *ct;
2007
2008         io(printf("Loading content info\n"));
2009
2010         ci = camel_folder_summary_content_info_new(s);
2011         
2012         camel_folder_summary_decode_token(in, &type);
2013         camel_folder_summary_decode_token(in, &subtype);
2014         ct = camel_content_type_new(type, subtype);
2015         g_free(type);           /* can this be removed? */
2016         g_free(subtype);
2017         if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
2018                 goto error;
2019         
2020         for (i=0;i<count;i++) {
2021                 char *name, *value;
2022                 camel_folder_summary_decode_token(in, &name);
2023                 camel_folder_summary_decode_token(in, &value);
2024                 if (!(name && value))
2025                         goto error;
2026                 
2027                 camel_content_type_set_param(ct, name, value);
2028                 /* TODO: do this so we dont have to double alloc/free */
2029                 g_free(name);
2030                 g_free(value);
2031         }
2032         ci->type = ct;
2033
2034         camel_folder_summary_decode_token(in, &ci->id);
2035         camel_folder_summary_decode_token(in, &ci->description);
2036         camel_folder_summary_decode_token(in, &ci->encoding);
2037
2038         camel_file_util_decode_uint32(in, &ci->size);
2039
2040         ci->childs = NULL;
2041
2042         if (!ferror(in))
2043                 return ci;
2044
2045  error:
2046         camel_folder_summary_content_info_free(s, ci);
2047         return NULL;
2048 }
2049
2050 static int
2051 content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
2052 {
2053         CamelContentType *ct;
2054         struct _camel_header_param *hp;
2055
2056         io(printf("Saving content info\n"));
2057
2058         ct = ci->type;
2059         if (ct) {
2060                 camel_folder_summary_encode_token(out, ct->type);
2061                 camel_folder_summary_encode_token(out, ct->subtype);
2062                 camel_file_util_encode_uint32(out, my_list_size((struct _node **)&ct->params));
2063                 hp = ct->params;
2064                 while (hp) {
2065                         camel_folder_summary_encode_token(out, hp->name);
2066                         camel_folder_summary_encode_token(out, hp->value);
2067                         hp = hp->next;
2068                 }
2069         } else {
2070                 camel_folder_summary_encode_token(out, NULL);
2071                 camel_folder_summary_encode_token(out, NULL);
2072                 camel_file_util_encode_uint32(out, 0);
2073         }
2074         camel_folder_summary_encode_token(out, ci->id);
2075         camel_folder_summary_encode_token(out, ci->description);
2076         camel_folder_summary_encode_token(out, ci->encoding);
2077         return camel_file_util_encode_uint32(out, ci->size);
2078 }
2079
2080 static void
2081 content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
2082 {
2083         camel_content_type_unref(ci->type);
2084         g_free(ci->id);
2085         g_free(ci->description);
2086         g_free(ci->encoding);
2087         e_memchunk_free(s->content_info_chunks, ci);
2088 }
2089
2090 static char *
2091 next_uid_string(CamelFolderSummary *s)
2092 {
2093         return g_strdup_printf("%u", camel_folder_summary_next_uid(s));
2094 }
2095
2096 /*
2097   OK
2098   Now this is where all the "smarts" happen, where the content info is built,
2099   and any indexing and what not is performed
2100 */
2101
2102 /* must have filter_lock before calling this function */
2103 static CamelMessageContentInfo *
2104 summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp)
2105 {
2106         int state;
2107         size_t len;
2108         char *buffer;
2109         CamelMessageContentInfo *info = NULL;
2110         CamelContentType *ct;
2111         int enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
2112         struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2113         CamelMimeFilterCharset *mfc;
2114         CamelMessageContentInfo *part;
2115
2116         d(printf("building content info\n"));
2117
2118         /* start of this part */
2119         state = camel_mime_parser_step(mp, &buffer, &len);
2120         
2121         if (s->build_content)
2122                 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_parser(s, mp);
2123
2124         switch(state) {
2125         case CAMEL_MIME_PARSER_STATE_HEADER:
2126                 /* check content type for indexing, then read body */
2127                 ct = camel_mime_parser_content_type(mp);
2128                 /* update attachments flag as we go */
2129                 if (camel_content_type_is(ct, "application", "pgp-signature")
2130 #ifdef ENABLE_SMIME
2131                     || camel_content_type_is(ct, "application", "x-pkcs7-signature")
2132                     || camel_content_type_is(ct, "application", "pkcs7-signature")
2133 #endif
2134                         )
2135                         camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2136
2137                 if (p->index && camel_content_type_is(ct, "text", "*")) {
2138                         char *encoding;
2139                         const char *charset;
2140
2141                         d(printf("generating index:\n"));
2142                         
2143                         encoding = camel_content_transfer_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
2144                         if (encoding) {
2145                                 if (!g_ascii_strcasecmp(encoding, "base64")) {
2146                                         d(printf(" decoding base64\n"));
2147                                         if (p->filter_64 == NULL)
2148                                                 p->filter_64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
2149                                         else
2150                                                 camel_mime_filter_reset((CamelMimeFilter *)p->filter_64);
2151                                         enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_64);
2152                                 } else if (!g_ascii_strcasecmp(encoding, "quoted-printable")) {
2153                                         d(printf(" decoding quoted-printable\n"));
2154                                         if (p->filter_qp == NULL)
2155                                                 p->filter_qp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC);
2156                                         else
2157                                                 camel_mime_filter_reset((CamelMimeFilter *)p->filter_qp);
2158                                         enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_qp);
2159                                 } else if (!g_ascii_strcasecmp (encoding, "x-uuencode")) {
2160                                         d(printf(" decoding x-uuencode\n"));
2161                                         if (p->filter_uu == NULL)
2162                                                 p->filter_uu = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_UU_DEC);
2163                                         else
2164                                                 camel_mime_filter_reset((CamelMimeFilter *)p->filter_uu);
2165                                         enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_uu);
2166                                 } else {
2167                                         d(printf(" ignoring encoding %s\n", encoding));
2168                                 }
2169                                 g_free(encoding);
2170                         }
2171
2172                         charset = camel_content_type_param(ct, "charset");
2173                         if (charset!=NULL
2174                             && !(g_ascii_strcasecmp(charset, "us-ascii")==0
2175                                  || g_ascii_strcasecmp(charset, "utf-8")==0)) {
2176                                 d(printf(" Adding conversion filter from %s to UTF-8\n", charset));
2177                                 mfc = g_hash_table_lookup(p->filter_charset, charset);
2178                                 if (mfc == NULL) {
2179                                         mfc = camel_mime_filter_charset_new_convert(charset, "UTF-8");
2180                                         if (mfc)
2181                                                 g_hash_table_insert(p->filter_charset, g_strdup(charset), mfc);
2182                                 } else {
2183                                         camel_mime_filter_reset((CamelMimeFilter *)mfc);
2184                                 }
2185                                 if (mfc) {
2186                                         chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc);
2187                                 } else {
2188                                         w(g_warning("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset));
2189                                 }
2190                         }
2191
2192                         /* we do charset conversions before this filter, which isn't strictly correct,
2193                            but works in most cases */
2194                         if (camel_content_type_is(ct, "text", "html")) {
2195                                 if (p->filter_html == NULL)
2196                                         p->filter_html = camel_mime_filter_html_new();
2197                                 else
2198                                         camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2199                                 html_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_html);
2200                         }
2201                         
2202                         /* and this filter actually does the indexing */
2203                         idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_index);
2204                 }
2205                 /* and scan/index everything */
2206                 while (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
2207                         ;
2208                 /* and remove the filters */
2209                 camel_mime_parser_filter_remove(mp, enc_id);
2210                 camel_mime_parser_filter_remove(mp, chr_id);
2211                 camel_mime_parser_filter_remove(mp, html_id);
2212                 camel_mime_parser_filter_remove(mp, idx_id);
2213                 break;
2214         case CAMEL_MIME_PARSER_STATE_MULTIPART:
2215                 d(printf("Summarising multipart\n"));
2216                 /* update attachments flag as we go */
2217                 ct = camel_mime_parser_content_type(mp);
2218                 if (camel_content_type_is(ct, "multipart", "mixed"))
2219                         camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2220                 if (camel_content_type_is(ct, "multipart", "signed")
2221                     || camel_content_type_is(ct, "multipart", "encrypted"))
2222                         camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2223
2224                 while (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
2225                         camel_mime_parser_unstep(mp);
2226                         part = summary_build_content_info(s, msginfo, mp);
2227                         if (part) {
2228                                 part->parent = info;
2229                                 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2230                         }
2231                 }
2232                 break;
2233         case CAMEL_MIME_PARSER_STATE_MESSAGE:
2234                 d(printf("Summarising message\n"));
2235                 /* update attachments flag as we go */
2236                 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2237
2238                 part = summary_build_content_info(s, msginfo, mp);
2239                 if (part) {
2240                         part->parent = info;
2241                         my_list_append((struct _node **)&info->childs, (struct _node *)part);
2242                 }
2243                 state = camel_mime_parser_step(mp, &buffer, &len);
2244                 if (state != CAMEL_MIME_PARSER_STATE_MESSAGE_END) {
2245                         g_error("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
2246                         camel_mime_parser_unstep(mp);
2247                 }
2248                 break;
2249         }
2250
2251         d(printf("finished building content info\n"));
2252
2253         return info;
2254 }
2255
2256 /* build the content-info, from a message */
2257 /* this needs the filter lock since it uses filters to perform indexing */
2258 static CamelMessageContentInfo *
2259 summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object)
2260 {
2261         CamelDataWrapper *containee;
2262         int parts, i;
2263         struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2264         CamelMessageContentInfo *info = NULL, *child;
2265         CamelContentType *ct;
2266
2267         if (s->build_content)
2268                 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_message(s, object);
2269         
2270         containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));
2271
2272         if (containee == NULL)
2273                 return info;
2274
2275         /* TODO: I find it odd that get_part and get_content_object do not
2276            add a reference, probably need fixing for multithreading */
2277
2278         /* check for attachments */
2279         ct = ((CamelDataWrapper *)containee)->mime_type;
2280         if (camel_content_type_is(ct, "multipart", "*")) {
2281                 if (camel_content_type_is(ct, "multipart", "mixed"))
2282                         camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2283                 if (camel_content_type_is(ct, "multipart", "signed")
2284                     || camel_content_type_is(ct, "multipart", "encrypted"))
2285                         camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2286         } else if (camel_content_type_is(ct, "application", "pgp-signature")
2287 #ifdef ENABLE_SMIME
2288                     || camel_content_type_is(ct, "application", "x-pkcs7-signature")
2289                     || camel_content_type_is(ct, "application", "pkcs7-signature")
2290 #endif
2291                 ) {
2292                 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2293         }
2294
2295         /* using the object types is more accurate than using the mime/types */
2296         if (CAMEL_IS_MULTIPART(containee)) {
2297                 parts = camel_multipart_get_number(CAMEL_MULTIPART(containee));
2298                 for (i=0;i<parts;i++) {
2299                         CamelMimePart *part = camel_multipart_get_part(CAMEL_MULTIPART(containee), i);
2300                         g_assert(part);
2301                         child = summary_build_content_info_message(s, msginfo, part);
2302                         if (child) {
2303                                 child->parent = info;
2304                                 my_list_append((struct _node **)&info->childs, (struct _node *)child);
2305                         }
2306                 }
2307         } else if (CAMEL_IS_MIME_MESSAGE(containee)) {
2308                 /* for messages we only look at its contents */
2309                 child = summary_build_content_info_message(s, msginfo, (CamelMimePart *)containee);
2310                 if (child) {
2311                         child->parent = info;
2312                         my_list_append((struct _node **)&info->childs, (struct _node *)child);
2313                 }
2314         } else if (p->filter_stream
2315                    && camel_content_type_is(ct, "text", "*")) {
2316                 int html_id = -1, idx_id = -1;
2317
2318                 /* pre-attach html filter if required, otherwise just index filter */
2319                 if (camel_content_type_is(ct, "text", "html")) {
2320                         if (p->filter_html == NULL)
2321                                 p->filter_html = camel_mime_filter_html_new();
2322                         else
2323                                 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2324                         html_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_html);
2325                 }
2326                 idx_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_index);
2327
2328                 camel_data_wrapper_decode_to_stream(containee, (CamelStream *)p->filter_stream);
2329                 camel_stream_flush((CamelStream *)p->filter_stream);
2330
2331                 camel_stream_filter_remove(p->filter_stream, idx_id);
2332                 camel_stream_filter_remove(p->filter_stream, html_id);
2333         }
2334
2335         return info;
2336 }
2337
2338 /**
2339  * camel_flag_get:
2340  * @list: the address of a #CamelFlag list
2341  * @name: name of the flag to get
2342  * 
2343  * Find the state of the flag @name in @list.
2344  * 
2345  * Returns the state of the flag (%TRUE or %FALSE)
2346  **/
2347 gboolean
2348 camel_flag_get(CamelFlag **list, const char *name)
2349 {
2350         CamelFlag *flag;
2351         flag = *list;
2352         while (flag) {
2353                 if (!strcmp(flag->name, name))
2354                         return TRUE;
2355                 flag = flag->next;
2356         }
2357         return FALSE;
2358 }
2359
2360
2361 /**
2362  * camel_flag_set:
2363  * @list: the address of a #CamelFlag list
2364  * @name: name of the flag to set or change
2365  * @value: the value to set on the flag
2366  * 
2367  * Set the state of a flag @name in the list @list to @value.
2368  *
2369  * Returns %TRUE if the value of the flag has been changed or %FALSE
2370  * otherwise
2371  **/
2372 gboolean
2373 camel_flag_set(CamelFlag **list, const char *name, gboolean value)
2374 {
2375         CamelFlag *flag, *tmp;
2376
2377         /* this 'trick' works because flag->next is the first element */
2378         flag = (CamelFlag *)list;
2379         while (flag->next) {
2380                 tmp = flag->next;
2381                 if (!strcmp(flag->next->name, name)) {
2382                         if (!value) {
2383                                 flag->next = tmp->next;
2384                                 g_free(tmp);
2385                         }
2386                         return !value;
2387                 }
2388                 flag = tmp;
2389         }
2390
2391         if (value) {
2392                 tmp = g_malloc(sizeof(*tmp) + strlen(name));
2393                 strcpy(tmp->name, name);
2394                 tmp->next = 0;
2395                 flag->next = tmp;
2396         }
2397         return value;
2398 }
2399
2400
2401 /**
2402  * camel_flag_list_size:
2403  * @list: the address of a #CamelFlag list
2404  * 
2405  * Get the length of the flag list.
2406  * 
2407  * Returns the number of flags in the list
2408  **/
2409 int
2410 camel_flag_list_size(CamelFlag **list)
2411 {
2412         int count=0;
2413         CamelFlag *flag;
2414
2415         flag = *list;
2416         while (flag) {
2417                 count++;
2418                 flag = flag->next;
2419         }
2420         return count;
2421 }
2422
2423
2424 /**
2425  * camel_flag_list_free:
2426  * @list: the address of a #CamelFlag list
2427  * 
2428  * Free the memory associated with the flag list @list.
2429  **/
2430 void
2431 camel_flag_list_free(CamelFlag **list)
2432 {
2433         CamelFlag *flag, *tmp;
2434         flag = *list;
2435         while (flag) {
2436                 tmp = flag->next;
2437                 g_free(flag);
2438                 flag = tmp;
2439         }
2440         *list = NULL;
2441 }
2442
2443
2444 /**
2445  * camel_flag_list_copy:
2446  * @to: the address of the #CamelFlag list to copy to
2447  * @from: the address of the #CamelFlag list to copy from
2448  * 
2449  * Copy a flag list.
2450  * 
2451  * Returns %TRUE if @to is changed or %FALSE otherwise
2452  **/
2453 gboolean
2454 camel_flag_list_copy(CamelFlag **to, CamelFlag **from)
2455 {
2456         CamelFlag *flag, *tmp;
2457         int changed = FALSE;
2458
2459         if (*to == NULL && from == NULL)
2460                 return FALSE;
2461
2462         /* Remove any now-missing flags */
2463         flag = (CamelFlag *)to;
2464         while (flag->next) {
2465                 tmp = flag->next;
2466                 if (!camel_flag_get(from, tmp->name)) {
2467                         flag->next = tmp->next;
2468                         g_free(tmp);
2469                         changed = TRUE;
2470                 } else {
2471                         flag = tmp;
2472                 }
2473         }
2474
2475         /* Add any new flags */
2476         flag = *from;
2477         while (flag) {
2478                 changed |= camel_flag_set(to, flag->name, TRUE);
2479                 flag = flag->next;
2480         }
2481
2482         return changed;
2483 }
2484
2485
2486 /**
2487  * camel_tag_get:
2488  * @list: the address of a #CamelTag list
2489  * @name: name of the tag to get
2490  *
2491  * Find the flag @name in @list and get the value.
2492  * 
2493  * Returns the value of the flag  or %NULL if unset
2494  **/
2495 const char *
2496 camel_tag_get(CamelTag **list, const char *name)
2497 {
2498         CamelTag *tag;
2499
2500         tag = *list;
2501         while (tag) {
2502                 if (!strcmp(tag->name, name))
2503                         return (const char *)tag->value;
2504                 tag = tag->next;
2505         }
2506         return NULL;
2507 }
2508
2509
2510 /**
2511  * camel_tag_set:
2512  * @list: the address of a #CamelTag list
2513  * @name: name of the tag to set
2514  * @value: value to set on the tag
2515  * 
2516  * Set the tag @name in the tag list @list to @value.
2517  *
2518  * Returns %TRUE if the value on the tag changed or %FALSE otherwise
2519  **/
2520 gboolean
2521 camel_tag_set(CamelTag **list, const char *name, const char *value)
2522 {
2523         CamelTag *tag, *tmp;
2524
2525         /* this 'trick' works because tag->next is the first element */
2526         tag = (CamelTag *)list;
2527         while (tag->next) {
2528                 tmp = tag->next;
2529                 if (!strcmp(tmp->name, name)) {
2530                         if (value == NULL) { /* clear it? */
2531                                 tag->next = tmp->next;
2532                                 g_free(tmp->value);
2533                                 g_free(tmp);
2534                                 return TRUE;
2535                         } else if (strcmp(tmp->value, value)) { /* has it changed? */
2536                                 g_free(tmp->value);
2537                                 tmp->value = g_strdup(value);
2538                                 return TRUE;
2539                         }
2540                         return FALSE;
2541                 }
2542                 tag = tmp;
2543         }
2544
2545         if (value) {
2546                 tmp = g_malloc(sizeof(*tmp)+strlen(name));
2547                 strcpy(tmp->name, name);
2548                 tmp->value = g_strdup(value);
2549                 tmp->next = 0;
2550                 tag->next = tmp;
2551                 return TRUE;
2552         }
2553         return FALSE;
2554 }
2555
2556
2557 /**
2558  * camel_tag_list_size:
2559  * @list: the address of a #CamelTag list
2560  * 
2561  * Get the number of tags present in the tag list @list.
2562  * 
2563  * Returns the number of tags
2564  **/
2565 int
2566 camel_tag_list_size(CamelTag **list)
2567 {
2568         int count=0;
2569         CamelTag *tag;
2570
2571         tag = *list;
2572         while (tag) {
2573                 count++;
2574                 tag = tag->next;
2575         }
2576         return count;
2577 }
2578
2579 static void
2580 rem_tag(char *key, char *value, CamelTag **to)
2581 {
2582         camel_tag_set(to, key, NULL);
2583 }
2584
2585
2586 /**
2587  * camel_tag_list_copy:
2588  * @to: the address of the #CamelTag list to copy to
2589  * @from: the address of the #CamelTag list to copy from
2590  * 
2591  * Copy a tag list.
2592  * 
2593  * Returns %TRUE if @to is changed or %FALSE otherwise
2594  **/
2595 gboolean
2596 camel_tag_list_copy(CamelTag **to, CamelTag **from)
2597 {
2598         int changed = FALSE;
2599         CamelTag *tag;
2600         GHashTable *left;
2601
2602         if (*to == NULL && from == NULL)
2603                 return FALSE;
2604
2605         left = g_hash_table_new(g_str_hash, g_str_equal);
2606         tag = *to;
2607         while (tag) {
2608                 g_hash_table_insert(left, tag->name, tag);
2609                 tag = tag->next;
2610         }
2611
2612         tag = *from;
2613         while (tag) {
2614                 changed |= camel_tag_set(to, tag->name, tag->value);
2615                 g_hash_table_remove(left, tag->name);
2616                 tag = tag->next;
2617         }
2618
2619         if (g_hash_table_size(left)>0) {
2620                 g_hash_table_foreach(left, (GHFunc)rem_tag, to);
2621                 changed = TRUE;
2622         }
2623         g_hash_table_destroy(left);
2624
2625         return changed;
2626 }
2627
2628
2629 /**
2630  * camel_tag_list_free:
2631  * @list: the address of a #CamelTag list
2632  * 
2633  * Free the tag list @list.
2634  **/
2635 void
2636 camel_tag_list_free(CamelTag **list)
2637 {
2638         CamelTag *tag, *tmp;
2639         tag = *list;
2640         while (tag) {
2641                 tmp = tag->next;
2642                 g_free(tag->value);
2643                 g_free(tag);
2644                 tag = tmp;
2645         }
2646         *list = NULL;
2647 }
2648
2649 static struct flag_names_t {
2650         char *name;
2651         guint32 value;
2652 } flag_names[] = {
2653         { "answered", CAMEL_MESSAGE_ANSWERED },
2654         { "deleted", CAMEL_MESSAGE_DELETED },
2655         { "draft", CAMEL_MESSAGE_DELETED },
2656         { "flagged", CAMEL_MESSAGE_FLAGGED },
2657         { "seen", CAMEL_MESSAGE_SEEN },
2658         { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
2659         { "junk", CAMEL_MESSAGE_JUNK },
2660         { "secure", CAMEL_MESSAGE_SECURE },
2661         { NULL, 0 }
2662 };
2663
2664
2665 /**
2666  * camel_system_flag:
2667  * @name: name of a system flag
2668  * 
2669  * Returns the integer value of the system flag string
2670  **/
2671 guint32
2672 camel_system_flag (const char *name)
2673 {
2674         struct flag_names_t *flag;
2675         
2676         g_return_val_if_fail (name != NULL, 0);
2677         
2678         for (flag = flag_names; *flag->name; flag++)
2679                 if (!g_ascii_strcasecmp (name, flag->name))
2680                         return flag->value;
2681         
2682         return 0;
2683 }
2684
2685
2686 /**
2687  * camel_system_flag_get:
2688  * @flags: bitwise system flags
2689  * @name: name of the flag to check for
2690  * 
2691  * Find the state of the flag @name in @flags.
2692  * 
2693  * Returns %TRUE if the named flag is set or %FALSE otherwise
2694  **/
2695 gboolean
2696 camel_system_flag_get (guint32 flags, const char *name)
2697 {
2698         g_return_val_if_fail (name != NULL, FALSE);
2699         
2700         return flags & camel_system_flag (name);
2701 }
2702
2703
2704 /**
2705  * camel_message_info_new:
2706  * @summary: a #CamelFolderSummary object or %NULL
2707  *
2708  * Create a new #CamelMessageInfo.
2709  *
2710  * Returns a new #CamelMessageInfo
2711  **/
2712 void *
2713 camel_message_info_new (CamelFolderSummary *s)
2714 {
2715         CamelMessageInfo *info;
2716
2717         if (s) {
2718                 CAMEL_SUMMARY_LOCK(s, alloc_lock);
2719                 if (s->message_info_chunks == NULL)
2720                         s->message_info_chunks = e_memchunk_new(32, s->message_info_size);
2721                 info = e_memchunk_alloc0(s->message_info_chunks);
2722                 CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
2723         } else {
2724                 info = g_malloc0(sizeof(CamelMessageInfoBase));
2725         }
2726
2727         info->refcount = 1;
2728         info->summary = s;
2729
2730         return info;
2731 }
2732
2733
2734 /**
2735  * camel_message_info_ref:
2736  * @info: a #CamelMessageInfo
2737  * 
2738  * Reference an info.
2739  **/
2740 void
2741 camel_message_info_ref(void *o)
2742 {
2743         CamelMessageInfo *mi = o;
2744
2745         if (mi->summary) {
2746                 CAMEL_SUMMARY_LOCK(mi->summary, ref_lock);
2747                 g_assert(mi->refcount >= 1);
2748                 mi->refcount++;
2749                 CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2750         } else {
2751                 GLOBAL_INFO_LOCK(info);
2752                 g_assert(mi->refcount >= 1);
2753                 mi->refcount++;
2754                 GLOBAL_INFO_UNLOCK(info);
2755         }
2756 }
2757
2758
2759 /**
2760  * camel_message_info_new_from_header:
2761  * @summary: a #CamelFolderSummary object or %NULL
2762  * @header: raw header
2763  *
2764  * Create a new #CamelMessageInfo pre-populated with info from
2765  * @header.
2766  *
2767  * Returns a new #CamelMessageInfo
2768  **/
2769 CamelMessageInfo *
2770 camel_message_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *header)
2771 {
2772         if (s)
2773                 return ((CamelFolderSummaryClass *)((CamelObject *)s)->klass)->message_info_new_from_header(s, header);
2774         else
2775                 return message_info_new_from_header(NULL, header);
2776 }
2777
2778
2779 /**
2780  * camel_message_info_free:
2781  * @info: a #CamelMessageInfo
2782  *
2783  * Unref's and potentially frees a #CamelMessageInfo and its contents.
2784  **/
2785 void
2786 camel_message_info_free(void *o)
2787 {
2788         CamelMessageInfo *mi = o;
2789
2790         g_return_if_fail(mi != NULL);
2791
2792         if (mi->summary) {
2793                 CAMEL_SUMMARY_LOCK(mi->summary, ref_lock);
2794
2795                 if (mi->refcount >= 1)
2796                         mi->refcount--;
2797                 if (mi->refcount > 0) {
2798                         CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2799                         return;
2800                 }
2801
2802                 CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2803
2804                 /* FIXME: this is kinda busted, should really be handled by message info free */
2805                 if (mi->summary->build_content
2806                     && ((CamelMessageInfoBase *)mi)->content) {
2807                         camel_folder_summary_content_info_free(mi->summary, ((CamelMessageInfoBase *)mi)->content);
2808                 }
2809
2810                 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(mi->summary)))->message_info_free(mi->summary, mi);
2811         } else {
2812                 GLOBAL_INFO_LOCK(info);
2813                 mi->refcount--;
2814                 if (mi->refcount > 0) {
2815                         GLOBAL_INFO_UNLOCK(info);
2816                         return;
2817                 }
2818                 GLOBAL_INFO_UNLOCK(info);
2819
2820                 message_info_free(NULL, mi);
2821         }
2822 }
2823
2824 static CamelMessageInfo *
2825 message_info_clone(CamelFolderSummary *s, const CamelMessageInfo *mi)
2826 {
2827         CamelMessageInfoBase *to, *from = (CamelMessageInfoBase *)mi;
2828         CamelFlag *flag;
2829         CamelTag *tag;
2830
2831         to = (CamelMessageInfoBase *)camel_message_info_new(s);
2832
2833         to->flags = from->flags;
2834         to->size = from->size;
2835         to->date_sent = from->date_sent;
2836         to->date_received = from->date_received;
2837         to->refcount = 1;
2838
2839         /* NB: We don't clone the uid */
2840
2841         to->subject = camel_pstring_strdup(from->subject);
2842         to->from = camel_pstring_strdup(from->from);
2843         to->to = camel_pstring_strdup(from->to);
2844         to->cc = camel_pstring_strdup(from->cc);
2845         to->mlist = camel_pstring_strdup(from->mlist);
2846         memcpy(&to->message_id, &from->message_id, sizeof(to->message_id));
2847
2848         if (from->references) {
2849                 int len = sizeof(*from->references) + ((from->references->size-1) * sizeof(from->references->references[0]));
2850
2851                 to->references = g_malloc(len);
2852                 memcpy(to->references, from->references, len);
2853         }
2854
2855         flag = from->user_flags;
2856         while (flag) {
2857                 camel_flag_set(&to->user_flags, flag->name, TRUE);
2858                 flag = flag->next;
2859         }
2860
2861         tag = from->user_tags;
2862         while (tag) {
2863                 camel_tag_set(&to->user_tags, tag->name, tag->value);
2864                 tag = tag->next;
2865         }
2866
2867         if (from->content) {
2868                 /* FIXME: copy content-infos */
2869         }
2870
2871         return (CamelMessageInfo *)to;
2872 }
2873
2874
2875 /**
2876  * camel_message_info_clone:
2877  * @info: a #CamelMessageInfo
2878  *
2879  * Duplicate a #CamelMessageInfo.
2880  *
2881  * Returns the duplicated #CamelMessageInfo
2882  **/
2883 void *
2884 camel_message_info_clone(const void *o)
2885 {
2886         const CamelMessageInfo *mi = o;
2887
2888         if (mi->summary)
2889                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->message_info_clone(mi->summary, mi);
2890         else
2891                 return message_info_clone(NULL, mi);
2892 }
2893
2894 static const void *
2895 info_ptr(const CamelMessageInfo *mi, int id)
2896 {
2897         switch (id) {
2898         case CAMEL_MESSAGE_INFO_SUBJECT:
2899                 return ((const CamelMessageInfoBase *)mi)->subject;
2900         case CAMEL_MESSAGE_INFO_FROM:
2901                 return ((const CamelMessageInfoBase *)mi)->from;
2902         case CAMEL_MESSAGE_INFO_TO:
2903                 return ((const CamelMessageInfoBase *)mi)->to;
2904         case CAMEL_MESSAGE_INFO_CC:
2905                 return ((const CamelMessageInfoBase *)mi)->cc;
2906         case CAMEL_MESSAGE_INFO_MLIST:
2907                 return ((const CamelMessageInfoBase *)mi)->mlist;
2908         case CAMEL_MESSAGE_INFO_MESSAGE_ID:
2909                 return &((const CamelMessageInfoBase *)mi)->message_id;
2910         case CAMEL_MESSAGE_INFO_REFERENCES:
2911                 return ((const CamelMessageInfoBase *)mi)->references;
2912         case CAMEL_MESSAGE_INFO_USER_FLAGS:
2913                 return ((const CamelMessageInfoBase *)mi)->user_flags;
2914         case CAMEL_MESSAGE_INFO_USER_TAGS:
2915                 return ((const CamelMessageInfoBase *)mi)->user_tags;
2916         default:
2917                 abort();
2918         }
2919 }
2920
2921 static guint32
2922 info_uint32(const CamelMessageInfo *mi, int id)
2923 {
2924         switch (id) {
2925         case CAMEL_MESSAGE_INFO_FLAGS:
2926                 return ((const CamelMessageInfoBase *)mi)->flags;
2927         case CAMEL_MESSAGE_INFO_SIZE:
2928                 return ((const CamelMessageInfoBase *)mi)->size;
2929         default:
2930                 abort();
2931         }
2932 }
2933
2934 static time_t
2935 info_time(const CamelMessageInfo *mi, int id)
2936 {
2937         switch (id) {
2938         case CAMEL_MESSAGE_INFO_DATE_SENT:
2939                 return ((const CamelMessageInfoBase *)mi)->date_sent;
2940         case CAMEL_MESSAGE_INFO_DATE_RECEIVED:
2941                 return ((const CamelMessageInfoBase *)mi)->date_received;
2942         default:
2943                 abort();
2944         }
2945 }
2946
2947 static gboolean
2948 info_user_flag(const CamelMessageInfo *mi, const char *id)
2949 {
2950         return camel_flag_get(&((CamelMessageInfoBase *)mi)->user_flags, id);
2951 }
2952
2953 static const char *
2954 info_user_tag(const CamelMessageInfo *mi, const char *id)
2955 {
2956         return camel_tag_get(&((CamelMessageInfoBase *)mi)->user_tags, id);
2957 }
2958
2959
2960 /**
2961  * camel_message_info_ptr:
2962  * @mi: a #CamelMessageInfo
2963  * @id: info to get
2964  *
2965  * Generic accessor method for getting pointer data.
2966  *
2967  * Returns the pointer data
2968  **/
2969 const void *
2970 camel_message_info_ptr(const CamelMessageInfo *mi, int id)
2971 {
2972         if (mi->summary)
2973                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_ptr(mi, id);
2974         else
2975                 return info_ptr(mi, id);
2976 }
2977
2978
2979 /**
2980  * camel_message_info_uint32:
2981  * @mi: a #CamelMessageInfo
2982  * @id: info to get
2983  *
2984  * Generic accessor method for getting 32bit int data.
2985  *
2986  * Returns the int data
2987  **/
2988 guint32
2989 camel_message_info_uint32(const CamelMessageInfo *mi, int id)
2990 {
2991         if (mi->summary)
2992                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_uint32(mi, id);
2993         else
2994                 return info_uint32(mi, id);
2995 }
2996
2997
2998 /**
2999  * camel_message_info_time:
3000  * @mi: a #CamelMessageInfo
3001  * @id: info to get
3002  *
3003  * Generic accessor method for getting time_t data.
3004  *
3005  * Returns the time_t data
3006  **/
3007 time_t
3008 camel_message_info_time(const CamelMessageInfo *mi, int id)
3009 {
3010         if (mi->summary)
3011                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_time(mi, id);
3012         else
3013                 return info_time(mi, id);
3014 }
3015
3016
3017 /**
3018  * camel_message_info_user_flag:
3019  * @mi: a #CamelMessageInfo
3020  * @id: user flag to get
3021  *
3022  * Get the state of a user flag named @id.
3023  *
3024  * Returns the state of the user flag
3025  **/
3026 gboolean
3027 camel_message_info_user_flag(const CamelMessageInfo *mi, const char *id)
3028 {
3029         if (mi->summary)
3030                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_user_flag(mi, id);
3031         else
3032                 return info_user_flag(mi, id);
3033 }
3034
3035
3036 /**
3037  * camel_message_info_user_tag:
3038  * @mi: a #CamelMessageInfo
3039  * @id: user tag to get
3040  *
3041  * Get the value of a user tag named @id.
3042  *
3043  * Returns the value of the user tag
3044  **/
3045 const char *
3046 camel_message_info_user_tag(const CamelMessageInfo *mi, const char *id)
3047 {
3048         if (mi->summary)
3049                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_user_tag(mi, id);
3050         else
3051                 return info_user_tag(mi, id);
3052 }
3053
3054 static gboolean
3055 info_set_flags(CamelMessageInfo *info, guint32 flags, guint32 set)
3056 {
3057         guint32 old;
3058         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3059
3060         /* TODO: locking? */
3061
3062         old = mi->flags;
3063         mi->flags = (old & ~flags) | (set & flags);
3064         if (old != mi->flags) {
3065                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3066                 if (mi->summary)
3067                         camel_folder_summary_touch(mi->summary);
3068         }
3069
3070         if (((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK)))
3071                 return FALSE;
3072
3073         if (mi->summary && mi->summary->folder && mi->uid) {
3074                 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3075
3076                 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3077                 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3078                 camel_folder_change_info_free(changes);
3079         }
3080
3081         return TRUE;
3082 }
3083
3084
3085 /**
3086  * camel_message_info_set_flags:
3087  * @mi: a #CamelMessageInfo
3088  * @flags: mask of flags to change
3089  * @set: state the flags should be changed to
3090  *
3091  * Change the state of the system flags on the #CamelMessageInfo
3092  *
3093  * Returns %TRUE if any of the flags changed or %FALSE otherwise
3094  **/
3095 gboolean
3096 camel_message_info_set_flags(CamelMessageInfo *mi, guint32 flags, guint32 set)
3097 {
3098         if (mi->summary)
3099                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_flags(mi, flags, set);
3100         else
3101                 return info_set_flags(mi, flags, set);
3102 }
3103
3104 static gboolean
3105 info_set_user_flag(CamelMessageInfo *info, const char *name, gboolean value)
3106 {
3107         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3108         int res;
3109
3110         res = camel_flag_set(&mi->user_flags, name, value);
3111
3112         /* TODO: check this item is still in the summary first */
3113         if (mi->summary && res && mi->summary->folder && mi->uid) {
3114                 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3115
3116                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3117                 camel_folder_summary_touch(mi->summary);
3118                 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3119                 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3120                 camel_folder_change_info_free(changes);
3121         }
3122
3123         return res;
3124 }
3125
3126
3127 /**
3128  * camel_message_info_set_user_flag:
3129  * @mi: a #CamelMessageInfo
3130  * @id: name of the user flag to set
3131  * @state: state to set the flag to
3132  *
3133  * Set the state of a user flag on a #CamelMessageInfo.
3134  *
3135  * Returns %TRUE if the state changed or %FALSE otherwise
3136  **/
3137 gboolean
3138 camel_message_info_set_user_flag(CamelMessageInfo *mi, const char *id, gboolean state)
3139 {
3140         if (mi->summary)
3141                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_user_flag(mi, id, state);
3142         else
3143                 return info_set_user_flag(mi, id, state);
3144 }
3145
3146 static gboolean
3147 info_set_user_tag(CamelMessageInfo *info, const char *name, const char *value)
3148 {
3149         CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3150         int res;
3151
3152         res = camel_tag_set(&mi->user_tags, name, value);
3153
3154         if (mi->summary && res && mi->summary->folder && mi->uid) {
3155                 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3156
3157                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3158                 camel_folder_summary_touch(mi->summary);
3159                 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3160                 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3161                 camel_folder_change_info_free(changes);
3162         }
3163
3164         return res;
3165 }
3166
3167
3168 /**
3169  * camel_message_info_set_user_tag:
3170  * @mi: a #CamelMessageInfo
3171  * @id: name of the user tag to set
3172  * @val: value to set
3173  *
3174  * Set the value of a user tag on a #CamelMessageInfo.
3175  *
3176  * Returns %TRUE if the value changed or %FALSE otherwise
3177  **/
3178 gboolean
3179 camel_message_info_set_user_tag(CamelMessageInfo *mi, const char *id, const char *val)
3180 {
3181         if (mi->summary)
3182                 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_user_tag(mi, id, val);
3183         else
3184                 return info_set_user_tag(mi, id, val);
3185 }
3186
3187 void
3188 camel_content_info_dump (CamelMessageContentInfo *ci, int depth)
3189 {
3190         char *p;
3191         
3192         p = alloca (depth * 4 + 1);
3193         memset (p, ' ', depth * 4);
3194         p[depth * 4] = 0;
3195         
3196         if (ci == NULL) {
3197                 printf ("%s<empty>\n", p);
3198                 return;
3199         }
3200         
3201         if (ci->type)
3202                 printf ("%scontent-type: %s/%s\n", p, ci->type->type ? ci->type->type : "(null)",
3203                         ci->type->subtype ? ci->type->subtype : "(null)");
3204         else
3205                 printf ("%scontent-type: <unset>\n", p);
3206         printf ("%scontent-transfer-encoding: %s\n", p, ci->encoding ? ci->encoding : "(null)");
3207         printf ("%scontent-description: %s\n", p, ci->description ? ci->description : "(null)");
3208         printf ("%ssize: %lu\n", p, (unsigned long) ci->size);
3209         ci = ci->childs;
3210         while (ci) {
3211                 camel_content_info_dump (ci, depth + 1);
3212                 ci = ci->next;
3213         }
3214 }
3215
3216 void
3217 camel_message_info_dump (CamelMessageInfo *mi)
3218 {
3219         if (mi == NULL) {
3220                 printf("No message?\n");
3221                 return;
3222         }
3223
3224         printf("Subject: %s\n", camel_message_info_subject(mi));
3225         printf("To: %s\n", camel_message_info_to(mi));
3226         printf("Cc: %s\n", camel_message_info_cc(mi));
3227         printf("mailing list: %s\n", camel_message_info_mlist(mi));
3228         printf("From: %s\n", camel_message_info_from(mi));
3229         printf("UID: %s\n", camel_message_info_uid(mi));
3230         printf("Flags: %04x\n", camel_message_info_flags(mi));
3231         /*camel_content_info_dump(mi->content, 0);*/
3232 }
3233
3234
3235 static void
3236 camel_folder_summary_class_init (CamelFolderSummaryClass *klass)
3237 {
3238         camel_folder_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
3239
3240         klass->summary_header_load = summary_header_load;
3241         klass->summary_header_save = summary_header_save;
3242
3243         klass->message_info_new_from_header  = message_info_new_from_header;
3244         klass->message_info_new_from_parser = message_info_new_from_parser;
3245         klass->message_info_new_from_message = message_info_new_from_message;
3246         klass->message_info_load = message_info_load;
3247         klass->message_info_save = message_info_save;
3248         klass->meta_message_info_save = meta_message_info_save;
3249         klass->message_info_free = message_info_free;
3250         klass->message_info_clone = message_info_clone;
3251
3252         klass->content_info_new_from_header  = content_info_new_from_header;
3253         klass->content_info_new_from_parser = content_info_new_from_parser;
3254         klass->content_info_new_from_message = content_info_new_from_message;
3255         klass->content_info_load = content_info_load;
3256         klass->content_info_save = content_info_save;
3257         klass->content_info_free = content_info_free;
3258
3259         klass->next_uid_string = next_uid_string;
3260
3261         klass->info_ptr = info_ptr;
3262         klass->info_uint32 = info_uint32;
3263         klass->info_time = info_time;
3264         klass->info_user_flag = info_user_flag;
3265         klass->info_user_tag = info_user_tag;
3266
3267 #if 0
3268         klass->info_set_string = info_set_string;
3269         klass->info_set_uint32 = info_set_uint32;
3270         klass->info_set_time = info_set_time;
3271         klass->info_set_ptr = info_set_ptr;
3272 #endif
3273         klass->info_set_user_flag = info_set_user_flag;
3274         klass->info_set_user_tag = info_set_user_tag;
3275
3276         klass->info_set_flags = info_set_flags;
3277 }