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