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