1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2003 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
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.
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.
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.
26 #include <sys/types.h>
36 #include <gal/util/e-iconv.h>
38 #include "camel-folder-summary.h"
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>
50 #include <camel/camel-stream-null.h>
51 #include <camel/camel-stream-filter.h>
53 #include <camel/camel-string-utils.h>
55 #include "e-util/md5-utils.h"
56 #include "e-util/e-memory.h"
58 #include "camel-private.h"
63 static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER;
65 /* this lock is ONLY for the standalone messageinfo stuff */
66 #define GLOBAL_INFO_LOCK(i) pthread_mutex_lock(&info_lock)
67 #define GLOBAL_INFO_UNLOCK(i) pthread_mutex_unlock(&info_lock)
69 #define GLOBAL_INFO_LOCK(i)
70 #define GLOBAL_INFO_UNLOCK(i)
73 /* this should probably be conditional on it existing */
77 #define io(x) /* io debug */
80 extern int strdup_count, malloc_count, free_count;
83 #define CAMEL_FOLDER_SUMMARY_VERSION (12)
85 #define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv)
87 /* trivial lists, just because ... */
92 static struct _node *my_list_append(struct _node **list, struct _node *n);
93 static int my_list_size(struct _node **list);
95 static int summary_header_load(CamelFolderSummary *, FILE *);
96 static int summary_header_save(CamelFolderSummary *, FILE *);
98 static CamelMessageInfo * message_info_new(CamelFolderSummary *, struct _header_raw *);
99 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
100 static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg);
101 static CamelMessageInfo * message_info_load(CamelFolderSummary *, FILE *);
102 static int message_info_save(CamelFolderSummary *, FILE *, CamelMessageInfo *);
103 static void message_info_free(CamelFolderSummary *, CamelMessageInfo *);
105 static CamelMessageContentInfo * content_info_new(CamelFolderSummary *, struct _header_raw *);
106 static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
107 static CamelMessageContentInfo * content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp);
108 static CamelMessageContentInfo * content_info_load(CamelFolderSummary *, FILE *);
109 static int content_info_save(CamelFolderSummary *, FILE *, CamelMessageContentInfo *);
110 static void content_info_free(CamelFolderSummary *, CamelMessageContentInfo *);
112 static char *next_uid_string(CamelFolderSummary *s);
114 static CamelMessageContentInfo * summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp);
115 static CamelMessageContentInfo * summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object);
117 static void camel_folder_summary_class_init (CamelFolderSummaryClass *klass);
118 static void camel_folder_summary_init (CamelFolderSummary *obj);
119 static void camel_folder_summary_finalize (CamelObject *obj);
121 static CamelObjectClass *camel_folder_summary_parent;
124 camel_folder_summary_class_init (CamelFolderSummaryClass *klass)
126 camel_folder_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
128 klass->summary_header_load = summary_header_load;
129 klass->summary_header_save = summary_header_save;
131 klass->message_info_new = message_info_new;
132 klass->message_info_new_from_parser = message_info_new_from_parser;
133 klass->message_info_new_from_message = message_info_new_from_message;
134 klass->message_info_load = message_info_load;
135 klass->message_info_save = message_info_save;
136 klass->message_info_free = message_info_free;
138 klass->content_info_new = content_info_new;
139 klass->content_info_new_from_parser = content_info_new_from_parser;
140 klass->content_info_new_from_message = content_info_new_from_message;
141 klass->content_info_load = content_info_load;
142 klass->content_info_save = content_info_save;
143 klass->content_info_free = content_info_free;
145 klass->next_uid_string = next_uid_string;
149 camel_folder_summary_init (CamelFolderSummary *s)
151 struct _CamelFolderSummaryPrivate *p;
153 p = _PRIVATE(s) = g_malloc0(sizeof(*p));
155 p->filter_charset = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
157 s->message_info_size = sizeof(CamelMessageInfo);
158 s->content_info_size = sizeof(CamelMessageContentInfo);
160 s->message_info_chunks = NULL;
161 s->content_info_chunks = NULL;
163 #if defined (DOESTRV) || defined (DOEPOOLV)
164 s->message_info_strings = CAMEL_MESSAGE_INFO_LAST;
167 s->version = CAMEL_FOLDER_SUMMARY_VERSION;
172 s->messages = g_ptr_array_new();
173 s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
175 #ifdef ENABLE_THREADS
176 p->summary_lock = g_mutex_new();
177 p->io_lock = g_mutex_new();
178 p->filter_lock = g_mutex_new();
179 p->alloc_lock = g_mutex_new();
180 p->ref_lock = g_mutex_new();
184 static void free_o_name(void *key, void *value, void *data)
186 camel_object_unref((CamelObject *)value);
191 camel_folder_summary_finalize (CamelObject *obj)
193 struct _CamelFolderSummaryPrivate *p;
194 CamelFolderSummary *s = (CamelFolderSummary *)obj;
198 camel_folder_summary_clear(s);
199 g_ptr_array_free(s->messages, TRUE);
200 g_hash_table_destroy(s->messages_uid);
202 g_hash_table_foreach(p->filter_charset, free_o_name, 0);
203 g_hash_table_destroy(p->filter_charset);
205 g_free(s->summary_path);
207 if (s->message_info_chunks)
208 e_memchunk_destroy(s->message_info_chunks);
209 if (s->content_info_chunks)
210 e_memchunk_destroy(s->content_info_chunks);
213 camel_object_unref((CamelObject *)p->filter_index);
215 camel_object_unref((CamelObject *)p->filter_64);
217 camel_object_unref((CamelObject *)p->filter_qp);
219 camel_object_unref((CamelObject *)p->filter_uu);
221 camel_object_unref((CamelObject *)p->filter_save);
223 camel_object_unref((CamelObject *)p->filter_html);
225 if (p->filter_stream)
226 camel_object_unref((CamelObject *)p->filter_stream);
228 camel_object_unref((CamelObject *)p->index);
230 #ifdef ENABLE_THREADS
231 g_mutex_free(p->summary_lock);
232 g_mutex_free(p->io_lock);
233 g_mutex_free(p->filter_lock);
234 g_mutex_free(p->alloc_lock);
235 g_mutex_free(p->ref_lock);
242 camel_folder_summary_get_type (void)
244 static CamelType type = CAMEL_INVALID_TYPE;
246 if (type == CAMEL_INVALID_TYPE) {
247 type = camel_type_register (camel_object_get_type (), "CamelFolderSummary",
248 sizeof (CamelFolderSummary),
249 sizeof (CamelFolderSummaryClass),
250 (CamelObjectClassInitFunc) camel_folder_summary_class_init,
252 (CamelObjectInitFunc) camel_folder_summary_init,
253 (CamelObjectFinalizeFunc) camel_folder_summary_finalize);
260 * camel_folder_summary_new:
262 * Create a new CamelFolderSummary object.
264 * Return value: A new CamelFolderSummary widget.
267 camel_folder_summary_new (void)
269 CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ())); return new;
274 * camel_folder_summary_set_filename:
278 * Set the filename where the summary will be loaded to/saved from.
280 void camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name)
282 CAMEL_SUMMARY_LOCK(s, summary_lock);
284 g_free(s->summary_path);
285 s->summary_path = g_strdup(name);
287 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
291 * camel_folder_summary_set_index:
295 * Set the index used to index body content. If the index is NULL, or
296 * not set (the default), no indexing of body content will take place.
298 * Unlike earlier behaviour, build_content need not be set to perform indexing.
300 void camel_folder_summary_set_index(CamelFolderSummary *s, CamelIndex *index)
302 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
305 camel_object_unref((CamelObject *)p->index);
309 camel_object_ref((CamelObject *)index);
313 * camel_folder_summary_set_build_content:
317 * Set a flag to tell the summary to build the content info summary
318 * (CamelMessageInfo.content). The default is not to build content info
321 void camel_folder_summary_set_build_content(CamelFolderSummary *s, gboolean state)
323 s->build_content = state;
327 * camel_folder_summary_count:
330 * Get the number of summary items stored in this summary.
332 * Return value: The number of items int he summary.
335 camel_folder_summary_count(CamelFolderSummary *s)
337 return s->messages->len;
341 * camel_folder_summary_index:
345 * Retrieve a summary item by index number.
347 * A referenced to the summary item is returned, which may be
348 * ref'd or free'd as appropriate.
350 * Return value: The summary item, or NULL if the index @i is out
352 * It must be freed using camel_folder_summary_info_free().
355 camel_folder_summary_index(CamelFolderSummary *s, int i)
357 CamelMessageInfo *info = NULL;
359 CAMEL_SUMMARY_LOCK(s, summary_lock);
360 CAMEL_SUMMARY_LOCK(s, ref_lock);
362 if (i<s->messages->len)
363 info = g_ptr_array_index(s->messages, i);
368 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
369 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
375 * camel_folder_summary_index:
379 * Obtain a copy of the summary array. This is done atomically,
380 * so cannot contain empty entries.
382 * It must be freed using camel_folder_summary_array_free().
385 camel_folder_summary_array(CamelFolderSummary *s)
387 CamelMessageInfo *info;
388 GPtrArray *res = g_ptr_array_new();
391 CAMEL_SUMMARY_LOCK(s, summary_lock);
392 CAMEL_SUMMARY_LOCK(s, ref_lock);
394 g_ptr_array_set_size(res, s->messages->len);
395 for (i=0;i<s->messages->len;i++) {
396 info = res->pdata[i] = g_ptr_array_index(s->messages, i);
400 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
401 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
407 * camel_folder_summary_array_free:
411 * Free the folder summary array.
414 camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array)
418 for (i=0;i<array->len;i++)
419 camel_folder_summary_info_free(s, array->pdata[i]);
421 g_ptr_array_free(array, TRUE);
425 * camel_folder_summary_uid:
429 * Retrieve a summary item by uid.
431 * A referenced to the summary item is returned, which may be
432 * ref'd or free'd as appropriate.
434 * Return value: The summary item, or NULL if the uid @uid
436 * It must be freed using camel_folder_summary_info_free().
439 camel_folder_summary_uid(CamelFolderSummary *s, const char *uid)
441 CamelMessageInfo *info;
443 CAMEL_SUMMARY_LOCK(s, summary_lock);
444 CAMEL_SUMMARY_LOCK(s, ref_lock);
446 info = g_hash_table_lookup(s->messages_uid, uid);
451 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
452 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
458 * camel_folder_summary_next_uid:
461 * Generate a new unique uid value as an integer. This
462 * may be used to create a unique sequence of numbers.
464 * Return value: The next unique uid value.
466 guint32 camel_folder_summary_next_uid(CamelFolderSummary *s)
471 CAMEL_SUMMARY_LOCK(s, summary_lock);
475 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
477 /* FIXME: sync this to disk */
478 /* summary_header_save(s);*/
483 * camel_folder_summary_set_uid:
485 * @uid: The next minimum uid to assign. To avoid clashing
486 * uid's, set this to the uid of a given messages + 1.
488 * Set the next minimum uid available. This can be used to
489 * ensure new uid's do not clash with existing uid's.
491 void camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid)
493 /* TODO: sync to disk? */
494 CAMEL_SUMMARY_LOCK(s, summary_lock);
496 s->nextuid = MAX(s->nextuid, uid);
498 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
502 * camel_folder_summary_next_uid_string:
505 * Retrieve the next uid, but as a formatted string.
507 * Return value: The next uid as an unsigned integer string.
508 * This string must be freed by the caller.
511 camel_folder_summary_next_uid_string(CamelFolderSummary *s)
513 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->next_uid_string(s);
516 /* loads the content descriptions, recursively */
517 static CamelMessageContentInfo *
518 perform_content_info_load(CamelFolderSummary *s, FILE *in)
522 CamelMessageContentInfo *ci, *part;
524 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_load(s, in);
528 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500) {
529 camel_folder_summary_content_info_free(s, ci);
533 for (i=0;i<count;i++) {
534 part = perform_content_info_load(s, in);
536 my_list_append((struct _node **)&ci->childs, (struct _node *)part);
539 d(fprintf (stderr, "Summary file format messed up?"));
540 camel_folder_summary_content_info_free(s, ci);
548 camel_folder_summary_load(CamelFolderSummary *s)
552 CamelMessageInfo *mi;
554 if (s->summary_path == NULL)
557 in = fopen(s->summary_path, "r");
561 CAMEL_SUMMARY_LOCK(s, io_lock);
562 if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
565 /* now read in each message ... */
566 for (i=0;i<s->saved_count;i++) {
567 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_load(s, in);
572 if (s->build_content) {
573 mi->content = perform_content_info_load(s, in);
574 if (mi->content == NULL) {
575 camel_folder_summary_info_free(s, mi);
580 camel_folder_summary_add(s, mi);
583 CAMEL_SUMMARY_UNLOCK(s, io_lock);
585 if (fclose (in) != 0)
588 s->flags &= ~CAMEL_SUMMARY_DIRTY;
594 g_warning ("Cannot load summary file: `%s': %s", s->summary_path, g_strerror (errno));
596 CAMEL_SUMMARY_UNLOCK(s, io_lock);
598 s->flags |= ~CAMEL_SUMMARY_DIRTY;
603 /* saves the content descriptions, recursively */
605 perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
607 CamelMessageContentInfo *part;
609 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->content_info_save (s, out, ci) == -1)
612 if (camel_file_util_encode_uint32 (out, my_list_size ((struct _node **)&ci->childs)) == -1)
617 if (perform_content_info_save (s, out, part) == -1)
626 * camel_folder_summary_save:
629 * Writes the summary to disk. The summary is only written if changes
632 * Return value: Returns -1 on error.
635 camel_folder_summary_save(CamelFolderSummary *s)
640 CamelMessageInfo *mi;
643 if (s->summary_path == NULL
644 || (s->flags & CAMEL_SUMMARY_DIRTY) == 0)
647 path = alloca(strlen(s->summary_path)+4);
648 sprintf(path, "%s~", s->summary_path);
649 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600);
652 out = fdopen(fd, "w");
661 io(printf("saving header\n"));
663 CAMEL_SUMMARY_LOCK(s, io_lock);
665 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1)
668 /* now write out each message ... */
669 /* we check ferorr when done for i/o errors */
670 count = s->messages->len;
671 for (i = 0; i < count; i++) {
672 mi = s->messages->pdata[i];
673 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->message_info_save (s, out, mi) == -1)
676 if (s->build_content) {
677 if (perform_content_info_save (s, out, mi->content) == -1)
682 if (fflush (out) != 0 || fsync (fileno (out)) == -1)
687 CAMEL_SUMMARY_UNLOCK(s, io_lock);
689 if (rename(path, s->summary_path) == -1) {
696 s->flags &= ~CAMEL_SUMMARY_DIRTY;
705 CAMEL_SUMMARY_UNLOCK(s, io_lock);
714 * camel_folder_summary_header_load:
715 * @s: Summary object.
717 * Only load the header information from the summary,
718 * keep the rest on disk. This should only be done on
719 * a fresh summary object.
721 * Return value: -1 on error.
723 int camel_folder_summary_header_load(CamelFolderSummary *s)
728 if (s->summary_path == NULL)
731 in = fopen(s->summary_path, "r");
735 CAMEL_SUMMARY_LOCK(s, io_lock);
736 ret = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in);
737 CAMEL_SUMMARY_UNLOCK(s, io_lock);
740 s->flags &= ~CAMEL_SUMMARY_DIRTY;
745 summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info)
748 CamelMessageInfo *mi;
750 uid = camel_message_info_uid(info);
751 if (uid == NULL || uid[0] == 0) {
752 camel_message_info_set_uid(info, camel_folder_summary_next_uid_string(s));
753 uid = camel_message_info_uid(info);
756 CAMEL_SUMMARY_LOCK(s, summary_lock);
758 while ((mi = g_hash_table_lookup(s->messages_uid, uid))) {
759 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
762 d(printf ("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid(info)));
763 camel_message_info_set_uid(info, camel_folder_summary_next_uid_string(s));
764 uid = camel_message_info_uid(info);
765 info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
766 CAMEL_SUMMARY_LOCK(s, summary_lock);
769 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
774 * camel_folder_summary_add:
778 * Adds a new @info record to the summary. If @info->uid is NULL, then a new
779 * uid is automatically re-assigned by calling :next_uid_string().
781 * The @info record should have been generated by calling one of the
782 * info_new_*() functions, as it will be free'd based on the summary
783 * class. And MUST NOT be allocated directly using malloc.
785 void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info)
790 if (summary_assign_uid(s, info) == 0)
793 CAMEL_SUMMARY_LOCK(s, summary_lock);
795 /* unnecessary for pooled vectors */
797 /* this is vitally important, and also if this is ever modified, then
798 the hash table needs to be resynced */
799 info->strings = e_strv_pack(info->strings);
802 g_ptr_array_add(s->messages, info);
803 g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info);
804 s->flags |= CAMEL_SUMMARY_DIRTY;
806 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
810 * camel_folder_summary_add_from_header:
814 * Build a new info record based on a set of headers, and add it to the
817 * Note that this function should not be used if build_content_info has
818 * been specified for this summary.
820 * Return value: The newly added record.
822 CamelMessageInfo *camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _header_raw *h)
824 CamelMessageInfo *info = camel_folder_summary_info_new_from_header(s, h);
826 camel_folder_summary_add(s, info);
832 * camel_folder_summary_add_from_parser:
836 * Build a new info record based on the current position of a CamelMimeParser.
838 * The parser should be positioned before the start of the message to summarise.
839 * This function may be used if build_contnet_info or an index has been
840 * specified for the summary.
842 * Return value: The newly added record.
844 CamelMessageInfo *camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
846 CamelMessageInfo *info = camel_folder_summary_info_new_from_parser(s, mp);
848 camel_folder_summary_add(s, info);
854 * camel_folder_summary_add_from_message:
858 * Add a summary item from an existing message.
862 CamelMessageInfo *camel_folder_summary_add_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
864 CamelMessageInfo *info = camel_folder_summary_info_new_from_message(s, msg);
866 camel_folder_summary_add(s, info);
872 * camel_folder_summary_info_new_from_header:
876 * Create a new info record from a header.
878 * Return value: Guess? This info record MUST be freed using
879 * camel_folder_summary_info_free(), camel_message_info_free() will not work.
881 CamelMessageInfo *camel_folder_summary_info_new_from_header(CamelFolderSummary *s, struct _header_raw *h)
883 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> message_info_new(s, h);
887 * camel_folder_summary_info_new_from_parser:
891 * Create a new info record from a parser. If the parser cannot
892 * determine a uid, then none will be assigned.
894 * If indexing is enabled, and the parser cannot determine a new uid, then
895 * one is automatically assigned.
897 * If indexing is enabled, then the content will be indexed based
898 * on this new uid. In this case, the message info MUST be
899 * added using :add().
901 * Once complete, the parser will be positioned at the end of
904 * Return value: Guess? This info record MUST be freed using
905 * camel_folder_summary_info_free(), camel_message_info_free() will not work.
907 CamelMessageInfo *camel_folder_summary_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
909 CamelMessageInfo *info = NULL;
912 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
914 CamelIndexName *name = NULL;
916 /* should this check the parser is in the right state, or assume it is?? */
918 start = camel_mime_parser_tell(mp);
919 if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_EOF) {
920 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_parser(s, mp);
922 camel_mime_parser_unstep(mp);
924 /* assign a unique uid, this is slightly 'wrong' as we do not really
925 * know if we are going to store this in the summary, but no matter */
927 summary_assign_uid(s, info);
929 CAMEL_SUMMARY_LOCK(s, filter_lock);
932 if (p->filter_index == NULL)
933 p->filter_index = camel_mime_filter_index_new_index(p->index);
934 camel_index_delete_name(p->index, camel_message_info_uid(info));
935 name = camel_index_add_name(p->index, camel_message_info_uid(info));
936 camel_mime_filter_index_set_name(p->filter_index, name);
939 /* always scan the content info, even if we dont save it */
940 info->content = summary_build_content_info(s, info, mp);
943 camel_index_write_name(p->index, name);
944 camel_object_unref((CamelObject *)name);
945 camel_mime_filter_index_set_name(p->filter_index, NULL);
948 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
950 info->size = camel_mime_parser_tell(mp) - start;
956 * camel_folder_summary_info_new_from_message:
960 * Create a summary item from a message.
964 CamelMessageInfo *camel_folder_summary_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
966 CamelMessageInfo *info;
967 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
968 CamelIndexName *name = NULL;
970 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_message(s, msg);
972 /* assign a unique uid, this is slightly 'wrong' as we do not really
973 * know if we are going to store this in the summary, but we need it set for indexing */
975 summary_assign_uid(s, info);
977 CAMEL_SUMMARY_LOCK(s, filter_lock);
980 if (p->filter_index == NULL)
981 p->filter_index = camel_mime_filter_index_new_index(p->index);
982 camel_index_delete_name(p->index, camel_message_info_uid(info));
983 name = camel_index_add_name(p->index, camel_message_info_uid(info));
984 camel_mime_filter_index_set_name(p->filter_index, name);
986 if (p->filter_stream == NULL) {
987 CamelStream *null = camel_stream_null_new();
989 p->filter_stream = camel_stream_filter_new_with_stream(null);
990 camel_object_unref((CamelObject *)null);
994 info->content = summary_build_content_info_message(s, info, (CamelMimePart *)msg);
997 camel_index_write_name(p->index, name);
998 camel_object_unref((CamelObject *)name);
999 camel_mime_filter_index_set_name(p->filter_index, NULL);
1002 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1008 * camel_folder_summary_content_info_free:
1012 * Free the content info @ci, and all associated memory.
1015 camel_folder_summary_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1017 CamelMessageContentInfo *pw, *pn;
1020 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_free(s, ci);
1023 camel_folder_summary_content_info_free(s, pw);
1029 * camel_folder_summary_info_free:
1033 * Unref and potentially free the message info @mi, and all associated memory.
1035 void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
1037 CamelMessageContentInfo *ci;
1042 CAMEL_SUMMARY_LOCK(s, ref_lock);
1044 g_assert(mi->refcount >= 1);
1047 if (mi->refcount > 0) {
1048 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1052 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1056 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_free(s, mi);
1057 if (s->build_content && ci) {
1058 camel_folder_summary_content_info_free(s, ci);
1063 * camel_folder_summary_info_ref:
1067 * Add an extra reference to @mi.
1069 void camel_folder_summary_info_ref(CamelFolderSummary *s, CamelMessageInfo *mi)
1074 CAMEL_SUMMARY_LOCK(s, ref_lock);
1075 g_assert(mi->refcount >= 1);
1077 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1081 * camel_folder_summary_touch:
1084 * Mark the summary as changed, so that a save will save it.
1087 camel_folder_summary_touch(CamelFolderSummary *s)
1089 CAMEL_SUMMARY_LOCK(s, summary_lock);
1090 s->flags |= CAMEL_SUMMARY_DIRTY;
1091 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1095 * camel_folder_summary_clear:
1098 * Empty the summary contents.
1101 camel_folder_summary_clear(CamelFolderSummary *s)
1105 CAMEL_SUMMARY_LOCK(s, summary_lock);
1106 if (camel_folder_summary_count(s) == 0) {
1107 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1111 for (i=0;i<s->messages->len;i++)
1112 camel_folder_summary_info_free(s, s->messages->pdata[i]);
1114 g_ptr_array_set_size(s->messages, 0);
1115 g_hash_table_destroy(s->messages_uid);
1116 s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
1117 s->flags |= CAMEL_SUMMARY_DIRTY;
1118 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1122 * camel_folder_summary_remove:
1126 * Remove a specific @info record from the summary.
1128 void camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info)
1130 CAMEL_SUMMARY_LOCK(s, summary_lock);
1131 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1132 g_ptr_array_remove(s->messages, info);
1133 s->flags |= CAMEL_SUMMARY_DIRTY;
1134 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1136 camel_folder_summary_info_free(s, info);
1140 * camel_folder_summary_remove_uid:
1144 * Remove a specific info record from the summary, by @uid.
1146 void camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid)
1148 CamelMessageInfo *oldinfo;
1151 CAMEL_SUMMARY_LOCK(s, summary_lock);
1152 CAMEL_SUMMARY_LOCK(s, ref_lock);
1153 if (g_hash_table_lookup_extended(s->messages_uid, uid, (void *)&olduid, (void *)&oldinfo)) {
1154 /* make sure it doesn't vanish while we're removing it */
1155 oldinfo->refcount++;
1156 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1157 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1158 camel_folder_summary_remove(s, oldinfo);
1159 camel_folder_summary_info_free(s, oldinfo);
1161 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1162 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1167 * camel_folder_summary_remove_index:
1171 * Remove a specific info record from the summary, by index.
1173 void camel_folder_summary_remove_index(CamelFolderSummary *s, int index)
1175 CAMEL_SUMMARY_LOCK(s, summary_lock);
1176 if (index < s->messages->len) {
1177 CamelMessageInfo *info = s->messages->pdata[index];
1179 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1180 g_ptr_array_remove_index(s->messages, index);
1181 s->flags |= CAMEL_SUMMARY_DIRTY;
1183 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1184 camel_folder_summary_info_free(s, info);
1186 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1191 * camel_folder_summary_remove_range:
1193 * @start: initial index
1194 * @end: last index to remove
1196 * Removes an indexed range of info records.
1198 void camel_folder_summary_remove_range(CamelFolderSummary *s, int start, int end)
1203 CAMEL_SUMMARY_LOCK(s, summary_lock);
1204 if (start < s->messages->len) {
1205 CamelMessageInfo **infos;
1208 end = MIN(end+1, s->messages->len);
1209 infos = g_malloc((end-start)*sizeof(infos[0]));
1211 for (i=start;i<end;i++) {
1212 CamelMessageInfo *info = s->messages->pdata[i];
1214 infos[i-start] = info;
1215 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1218 memmove(s->messages->pdata+start, s->messages->pdata+end, (s->messages->len-end)*sizeof(s->messages->pdata[0]));
1219 g_ptr_array_set_size(s->messages, s->messages->len - (end - start));
1220 s->flags |= CAMEL_SUMMARY_DIRTY;
1222 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1224 for (i=start;i<end;i++)
1225 camel_folder_summary_info_free(s, infos[i-start]);
1228 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1232 /* should be sorted, for binary search */
1233 /* This is a tokenisation mechanism for strings written to the
1234 summary - to save space.
1235 This list can have at most 31 words. */
1236 static char * tokens[] = {
1261 "us-ascii", /* 25 words */
1264 #define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
1268 1-tokens_len == tokens[id-1]
1269 >=32 string, length = n-32
1274 token_search_cmp(char *key, char **index)
1276 d(printf("comparing '%s' to '%s'\n", key, *index));
1277 return strcmp(key, *index);
1282 * camel_folder_summary_encode_token:
1286 * Encode a string value, but use tokenisation and compression
1287 * to reduce the size taken for common mailer words. This
1288 * can still be used to encode normal strings as well.
1290 * Return value: -1 on error.
1293 camel_folder_summary_encode_token(FILE *out, const char *str)
1295 io(printf("Encoding token: '%s'\n", str));
1298 return camel_file_util_encode_uint32(out, 0);
1300 int len = strlen(str);
1308 lower[i] = tolower(str[i]);
1311 match = bsearch(lower, tokens, tokens_len, sizeof(char *), (int (*)(const void *, const void *))token_search_cmp);
1313 token = match-tokens;
1315 for (i=0;i<tokens_len;i++) {
1316 if (!strcmp(tokens[i], lower)) {
1324 return camel_file_util_encode_uint32(out, token+1);
1326 if (camel_file_util_encode_uint32(out, len+32) == -1)
1328 if (fwrite(str, len, 1, out) != 1)
1336 * camel_folder_summary_decode_token:
1340 * Decode a token value.
1342 * Return value: -1 on error.
1345 camel_folder_summary_decode_token(FILE *in, char **str)
1350 io(printf("Decode token ...\n"));
1352 if (camel_file_util_decode_uint32(in, &len) == -1) {
1353 io(printf ("Could not decode token from file"));
1361 } else if (len<= tokens_len) {
1362 ret = g_strdup(tokens[len-1]);
1364 io(printf ("Invalid token encountered: %d", len));
1368 } else if (len > 10240) {
1369 io(printf ("Got broken string header length: %d bytes", len));
1374 ret = g_malloc(len+1);
1375 if (fread(ret, len, 1, in) != 1) {
1383 io(printf("Token = '%s'\n", ret));
1389 static struct _node *
1390 my_list_append(struct _node **list, struct _node *n)
1392 struct _node *ln = (struct _node *)list;
1401 my_list_size(struct _node **list)
1404 struct _node *ln = (struct _node *)list;
1413 summary_header_load(CamelFolderSummary *s, FILE *in)
1415 gint32 version, flags, nextuid, count;
1418 fseek(in, 0, SEEK_SET);
1420 io(printf("Loading header\n"));
1422 if (camel_file_util_decode_fixed_int32(in, &version) == -1
1423 || camel_file_util_decode_fixed_int32(in, &flags) == -1
1424 || camel_file_util_decode_fixed_int32(in, &nextuid) == -1
1425 || camel_file_util_decode_time_t(in, &time) == -1
1426 || camel_file_util_decode_fixed_int32(in, &count) == -1) {
1430 s->nextuid = nextuid;
1433 s->saved_count = count;
1434 if (s->version != version) {
1435 d(printf ("Summary header version mismatch"));
1443 summary_header_save(CamelFolderSummary *s, FILE *out)
1445 fseek(out, 0, SEEK_SET);
1447 io(printf("Savining header\n"));
1449 camel_file_util_encode_fixed_int32(out, s->version);
1450 camel_file_util_encode_fixed_int32(out, s->flags);
1451 camel_file_util_encode_fixed_int32(out, s->nextuid);
1452 camel_file_util_encode_time_t(out, s->time);
1453 return camel_file_util_encode_fixed_int32(out, camel_folder_summary_count(s));
1456 /* are these even useful for anything??? */
1457 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1459 CamelMessageInfo *mi = NULL;
1462 state = camel_mime_parser_state(mp);
1466 case HSCAN_MULTIPART:
1467 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new(s, camel_mime_parser_headers_raw(mp));
1470 g_error("Invalid parser state");
1476 static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1478 CamelMessageContentInfo *ci = NULL;
1480 switch (camel_mime_parser_state(mp)) {
1483 case HSCAN_MULTIPART:
1484 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new(s, camel_mime_parser_headers_raw(mp));
1486 ci->type = camel_mime_parser_content_type(mp);
1487 header_content_type_ref(ci->type);
1491 g_error("Invalid parser state");
1497 static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1499 CamelMessageInfo *mi;
1501 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new(s, ((CamelMimePart *)msg)->headers);
1506 static CamelMessageContentInfo * content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp)
1508 CamelMessageContentInfo *ci;
1510 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new(s, mp->headers);
1516 summary_format_address(struct _header_raw *h, const char *name, const char *charset)
1518 struct _header_address *addr;
1522 text = header_raw_find (&h, name, NULL);
1523 addr = header_address_decode (text, charset);
1525 ret = header_address_list_format (addr);
1526 header_address_list_clear (&addr);
1528 ret = g_strdup (text);
1535 summary_format_string (struct _header_raw *h, const char *name, const char *charset)
1539 text = header_raw_find (&h, name, NULL);
1541 while (isspace ((unsigned) *text))
1543 return header_decode_string (text, charset);
1550 * camel_folder_summary_info_new:
1553 * Allocate a new camel message info, suitable for adding
1559 camel_folder_summary_info_new(CamelFolderSummary *s)
1561 CamelMessageInfo *mi;
1563 CAMEL_SUMMARY_LOCK(s, alloc_lock);
1564 if (s->message_info_chunks == NULL)
1565 s->message_info_chunks = e_memchunk_new(32, s->message_info_size);
1566 mi = e_memchunk_alloc(s->message_info_chunks);
1567 CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
1569 memset(mi, 0, s->message_info_size);
1571 mi->strings = e_poolv_new (s->message_info_strings);
1574 mi->strings = e_strv_new(s->message_info_strings);
1581 * camel_folder_summary_content_info_new:
1584 * Allocate a new camel message content info, suitable for adding
1589 CamelMessageContentInfo *
1590 camel_folder_summary_content_info_new(CamelFolderSummary *s)
1592 CamelMessageContentInfo *ci;
1594 CAMEL_SUMMARY_LOCK(s, alloc_lock);
1595 if (s->content_info_chunks == NULL)
1596 s->content_info_chunks = e_memchunk_new(32, s->content_info_size);
1597 ci = e_memchunk_alloc(s->content_info_chunks);
1598 CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
1600 memset(ci, 0, s->content_info_size);
1604 static CamelMessageInfo *
1605 message_info_new(CamelFolderSummary *s, struct _header_raw *h)
1607 CamelMessageInfo *mi;
1608 const char *received;
1610 struct _header_references *refs, *irt, *scan;
1613 char *subject, *from, *to, *cc, *mlist;
1614 struct _header_content_type *ct = NULL;
1615 const char *content, *charset = NULL;
1617 mi = camel_folder_summary_info_new(s);
1619 if ((content = header_raw_find(&h, "Content-Type", NULL))
1620 && (ct = header_content_type_decode(content))
1621 && (charset = header_content_type_param(ct, "charset"))
1622 && (strcasecmp(charset, "us-ascii") == 0))
1625 charset = charset ? e_iconv_charset_name (charset) : NULL;
1627 subject = summary_format_string(h, "subject", charset);
1628 from = summary_format_address(h, "from", charset);
1629 to = summary_format_address(h, "to", charset);
1630 cc = summary_format_address(h, "cc", charset);
1631 mlist = header_raw_check_mailing_list(&h);
1634 header_content_type_unref(ct);
1637 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_SUBJECT, subject, TRUE);
1638 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_FROM, from, TRUE);
1639 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_TO, to, TRUE);
1640 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_CC, cc, TRUE);
1641 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_MLIST, mlist, TRUE);
1642 #elif defined (DOESTRV)
1643 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_SUBJECT, subject);
1644 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_FROM, from);
1645 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_TO, to);
1646 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_CC, cc);
1647 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_MLIST, mlist);
1649 mi->subject = subject;
1656 mi->user_flags = NULL;
1657 mi->user_tags = NULL;
1658 mi->date_sent = header_decode_date(header_raw_find(&h, "date", NULL), NULL);
1659 received = header_raw_find(&h, "received", NULL);
1661 received = strrchr(received, ';');
1663 mi->date_received = header_decode_date(received + 1, NULL);
1665 mi->date_received = 0;
1667 msgid = header_msgid_decode(header_raw_find(&h, "message-id", NULL));
1669 md5_get_digest(msgid, strlen(msgid), digest);
1670 memcpy(mi->message_id.id.hash, digest, sizeof(mi->message_id.id.hash));
1674 /* decode our references and in-reply-to headers */
1675 refs = header_references_decode (header_raw_find (&h, "references", NULL));
1676 irt = header_references_inreplyto_decode (header_raw_find (&h, "in-reply-to", NULL));
1679 /* The References field is populated from the ``References'' and/or ``In-Reply-To''
1680 headers. If both headers exist, take the first thing in the In-Reply-To header
1681 that looks like a Message-ID, and append it to the References header. */
1689 count = header_references_list_size(&refs);
1690 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1694 md5_get_digest(scan->id, strlen(scan->id), digest);
1695 memcpy(mi->references->references[count].id.hash, digest, sizeof(mi->message_id.id.hash));
1699 mi->references->size = count;
1700 header_references_list_clear(&refs);
1707 static CamelMessageInfo *
1708 message_info_load(CamelFolderSummary *s, FILE *in)
1710 CamelMessageInfo *mi;
1713 char *subject, *from, *to, *cc, *mlist, *uid;;
1715 mi = camel_folder_summary_info_new(s);
1717 io(printf("Loading message info\n"));
1719 camel_file_util_decode_string(in, &uid);
1720 camel_file_util_decode_uint32(in, &mi->flags);
1721 camel_file_util_decode_uint32(in, &mi->size);
1722 camel_file_util_decode_time_t(in, &mi->date_sent);
1723 camel_file_util_decode_time_t(in, &mi->date_received);
1724 camel_file_util_decode_string(in, &subject);
1725 camel_file_util_decode_string(in, &from);
1726 camel_file_util_decode_string(in, &to);
1727 camel_file_util_decode_string(in, &cc);
1728 camel_file_util_decode_string(in, &mlist);
1731 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_UID, uid, TRUE);
1732 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_SUBJECT, subject, TRUE);
1733 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_FROM, from, TRUE);
1734 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_TO, to, TRUE);
1735 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_CC, cc, TRUE);
1736 e_poolv_set(mi->strings, CAMEL_MESSAGE_INFO_MLIST, mlist, TRUE);
1737 #elif defined (DOESTRV)
1738 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_UID, uid);
1739 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_SUBJECT, subject);
1740 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_FROM, from);
1741 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_TO, to);
1742 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_CC, cc);
1743 e_strv_set_ref_free(mi->strings, CAMEL_MESSAGE_INFO_MLIST, mlist);
1746 mi->subject = subject;
1755 camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.hi);
1756 camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.lo);
1758 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1762 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1763 mi->references->size = count;
1764 for (i=0;i<count;i++) {
1765 camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.hi);
1766 camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.lo);
1770 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1773 for (i=0;i<count;i++) {
1775 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL)
1777 camel_flag_set(&mi->user_flags, name, TRUE);
1781 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1784 for (i=0;i<count;i++) {
1786 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL
1787 || camel_file_util_decode_string(in, &value) == -1)
1789 camel_tag_set(&mi->user_tags, name, value);
1798 camel_folder_summary_info_free(s, mi);
1804 message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
1811 io(printf("Saving message info\n"));
1813 camel_file_util_encode_string(out, camel_message_info_uid(mi));
1814 camel_file_util_encode_uint32(out, mi->flags);
1815 camel_file_util_encode_uint32(out, mi->size);
1816 camel_file_util_encode_time_t(out, mi->date_sent);
1817 camel_file_util_encode_time_t(out, mi->date_received);
1818 camel_file_util_encode_string(out, camel_message_info_subject(mi));
1819 camel_file_util_encode_string(out, camel_message_info_from(mi));
1820 camel_file_util_encode_string(out, camel_message_info_to(mi));
1821 camel_file_util_encode_string(out, camel_message_info_cc(mi));
1822 camel_file_util_encode_string(out, camel_message_info_mlist(mi));
1824 camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.hi);
1825 camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.lo);
1827 if (mi->references) {
1828 camel_file_util_encode_uint32(out, mi->references->size);
1829 for (i=0;i<mi->references->size;i++) {
1830 camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.hi);
1831 camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.lo);
1834 camel_file_util_encode_uint32(out, 0);
1837 count = camel_flag_list_size(&mi->user_flags);
1838 camel_file_util_encode_uint32(out, count);
1839 flag = mi->user_flags;
1841 camel_file_util_encode_string(out, flag->name);
1845 count = camel_tag_list_size(&mi->user_tags);
1846 camel_file_util_encode_uint32(out, count);
1847 tag = mi->user_tags;
1849 camel_file_util_encode_string(out, tag->name);
1850 camel_file_util_encode_string(out, tag->value);
1858 message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
1861 e_poolv_destroy(mi->strings);
1862 #elif defined (DOESTRV)
1863 e_strv_destroy(mi->strings);
1866 g_free(mi->subject);
1872 g_free(mi->references);
1873 camel_flag_list_free(&mi->user_flags);
1874 camel_tag_list_free(&mi->user_tags);
1875 e_memchunk_free(s->message_info_chunks, mi);
1878 static CamelMessageContentInfo *
1879 content_info_new (CamelFolderSummary *s, struct _header_raw *h)
1881 CamelMessageContentInfo *ci;
1882 const char *charset;
1884 ci = camel_folder_summary_content_info_new (s);
1886 charset = e_iconv_locale_charset ();
1887 ci->id = header_msgid_decode (header_raw_find (&h, "content-id", NULL));
1888 ci->description = header_decode_string (header_raw_find (&h, "content-description", NULL), NULL);
1889 ci->encoding = header_content_encoding_decode (header_raw_find (&h, "content-transfer-encoding", NULL));
1894 static CamelMessageContentInfo *
1895 content_info_load(CamelFolderSummary *s, FILE *in)
1897 CamelMessageContentInfo *ci;
1898 char *type, *subtype;
1900 struct _header_content_type *ct;
1902 io(printf("Loading content info\n"));
1904 ci = camel_folder_summary_content_info_new(s);
1906 camel_folder_summary_decode_token(in, &type);
1907 camel_folder_summary_decode_token(in, &subtype);
1908 ct = header_content_type_new(type, subtype);
1909 g_free(type); /* can this be removed? */
1911 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1914 for (i=0;i<count;i++) {
1916 camel_folder_summary_decode_token(in, &name);
1917 camel_folder_summary_decode_token(in, &value);
1918 if (!(name && value))
1921 header_content_type_set_param(ct, name, value);
1922 /* TODO: do this so we dont have to double alloc/free */
1928 camel_folder_summary_decode_token(in, &ci->id);
1929 camel_folder_summary_decode_token(in, &ci->description);
1930 camel_folder_summary_decode_token(in, &ci->encoding);
1932 camel_file_util_decode_uint32(in, &ci->size);
1940 camel_folder_summary_content_info_free(s, ci);
1945 content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
1947 struct _header_content_type *ct;
1948 struct _header_param *hp;
1950 io(printf("Saving content info\n"));
1954 camel_folder_summary_encode_token(out, ct->type);
1955 camel_folder_summary_encode_token(out, ct->subtype);
1956 camel_file_util_encode_uint32(out, my_list_size((struct _node **)&ct->params));
1959 camel_folder_summary_encode_token(out, hp->name);
1960 camel_folder_summary_encode_token(out, hp->value);
1964 camel_folder_summary_encode_token(out, NULL);
1965 camel_folder_summary_encode_token(out, NULL);
1966 camel_file_util_encode_uint32(out, 0);
1968 camel_folder_summary_encode_token(out, ci->id);
1969 camel_folder_summary_encode_token(out, ci->description);
1970 camel_folder_summary_encode_token(out, ci->encoding);
1971 return camel_file_util_encode_uint32(out, ci->size);
1975 content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1977 header_content_type_unref(ci->type);
1979 g_free(ci->description);
1980 g_free(ci->encoding);
1981 e_memchunk_free(s->content_info_chunks, ci);
1985 next_uid_string(CamelFolderSummary *s)
1987 return g_strdup_printf("%u", camel_folder_summary_next_uid(s));
1992 Now this is where all the "smarts" happen, where the content info is built,
1993 and any indexing and what not is performed
1996 /* must have filter_lock before calling this function */
1997 static CamelMessageContentInfo *
1998 summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp)
2003 CamelMessageContentInfo *info = NULL;
2004 struct _header_content_type *ct;
2006 int enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
2007 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2008 CamelMimeFilterCharset *mfc;
2009 CamelMessageContentInfo *part;
2011 d(printf("building content info\n"));
2013 /* start of this part */
2014 state = camel_mime_parser_step(mp, &buffer, &len);
2015 body = camel_mime_parser_tell(mp);
2017 if (s->build_content)
2018 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_parser(s, mp);
2022 /* check content type for indexing, then read body */
2023 ct = camel_mime_parser_content_type(mp);
2024 /* update attachments flag as we go */
2025 if (!header_content_type_is(ct, "text", "*"))
2026 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2028 if (p->index && header_content_type_is(ct, "text", "*")) {
2030 const char *charset;
2032 d(printf("generating index:\n"));
2034 encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
2036 if (!strcasecmp(encoding, "base64")) {
2037 d(printf(" decoding base64\n"));
2038 if (p->filter_64 == NULL)
2039 p->filter_64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
2041 camel_mime_filter_reset((CamelMimeFilter *)p->filter_64);
2042 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_64);
2043 } else if (!strcasecmp(encoding, "quoted-printable")) {
2044 d(printf(" decoding quoted-printable\n"));
2045 if (p->filter_qp == NULL)
2046 p->filter_qp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC);
2048 camel_mime_filter_reset((CamelMimeFilter *)p->filter_qp);
2049 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_qp);
2050 } else if (!strcasecmp (encoding, "x-uuencode")) {
2051 d(printf(" decoding x-uuencode\n"));
2052 if (p->filter_uu == NULL)
2053 p->filter_uu = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_UU_DEC);
2055 camel_mime_filter_reset((CamelMimeFilter *)p->filter_uu);
2056 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_uu);
2058 d(printf(" ignoring encoding %s\n", encoding));
2063 charset = header_content_type_param(ct, "charset");
2065 && !(strcasecmp(charset, "us-ascii")==0
2066 || strcasecmp(charset, "utf-8")==0)) {
2067 d(printf(" Adding conversion filter from %s to UTF-8\n", charset));
2068 mfc = g_hash_table_lookup(p->filter_charset, charset);
2070 mfc = camel_mime_filter_charset_new_convert(charset, "UTF-8");
2072 g_hash_table_insert(p->filter_charset, g_strdup(charset), mfc);
2074 camel_mime_filter_reset((CamelMimeFilter *)mfc);
2077 chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc);
2079 g_warning("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset);
2083 /* we do charset conversions before this filter, which isn't strictly correct,
2084 but works in most cases */
2085 if (header_content_type_is(ct, "text", "html")) {
2086 if (p->filter_html == NULL)
2087 p->filter_html = camel_mime_filter_html_new();
2089 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2090 html_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_html);
2093 /* and this filter actually does the indexing */
2094 idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_index);
2096 /* and scan/index everything */
2097 while (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_BODY_END)
2099 /* and remove the filters */
2100 camel_mime_parser_filter_remove(mp, enc_id);
2101 camel_mime_parser_filter_remove(mp, chr_id);
2102 camel_mime_parser_filter_remove(mp, html_id);
2103 camel_mime_parser_filter_remove(mp, idx_id);
2105 case HSCAN_MULTIPART:
2106 d(printf("Summarising multipart\n"));
2107 /* update attachments flag as we go */
2108 ct = camel_mime_parser_content_type(mp);
2109 if (header_content_type_is(ct, "multipart", "mixed"))
2110 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2112 while (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_MULTIPART_END) {
2113 camel_mime_parser_unstep(mp);
2114 part = summary_build_content_info(s, msginfo, mp);
2116 part->parent = info;
2117 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2122 d(printf("Summarising message\n"));
2123 /* update attachments flag as we go */
2124 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2126 part = summary_build_content_info(s, msginfo, mp);
2128 part->parent = info;
2129 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2131 state = camel_mime_parser_step(mp, &buffer, &len);
2132 if (state != HSCAN_MESSAGE_END) {
2133 g_error("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
2134 camel_mime_parser_unstep(mp);
2139 d(printf("finished building content info\n"));
2144 /* build the content-info, from a message */
2145 /* this needs the filter lock since it uses filters to perform indexing */
2146 static CamelMessageContentInfo *
2147 summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object)
2149 CamelDataWrapper *containee;
2151 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2152 CamelMessageContentInfo *info = NULL, *child;
2154 if (s->build_content)
2155 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_message(s, object);
2157 containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));
2159 if (containee == NULL)
2162 /* TODO: I find it odd that get_part and get_content_object do not
2163 add a reference, probably need fixing for multithreading */
2165 /* check for attachments */
2166 if (header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "multipart", "*")) {
2167 if (header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "multipart", "mixed"))
2168 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2169 } else if (!header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "text", "*"))
2170 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2172 /* using the object types is more accurate than using the mime/types */
2173 if (CAMEL_IS_MULTIPART(containee)) {
2174 parts = camel_multipart_get_number(CAMEL_MULTIPART(containee));
2175 for (i=0;i<parts;i++) {
2176 CamelMimePart *part = camel_multipart_get_part(CAMEL_MULTIPART(containee), i);
2178 child = summary_build_content_info_message(s, msginfo, part);
2180 child->parent = info;
2181 my_list_append((struct _node **)&info->childs, (struct _node *)child);
2184 } else if (CAMEL_IS_MIME_MESSAGE(containee)) {
2185 /* for messages we only look at its contents */
2186 child = summary_build_content_info_message(s, msginfo, (CamelMimePart *)containee);
2188 child->parent = info;
2189 my_list_append((struct _node **)&info->childs, (struct _node *)child);
2191 } else if (p->filter_stream
2192 && header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "text", "*")) {
2193 int html_id = -1, idx_id = -1;
2195 /* pre-attach html filter if required, otherwise just index filter */
2196 if (header_content_type_is(CAMEL_DATA_WRAPPER(containee)->mime_type, "text", "html")) {
2197 if (p->filter_html == NULL)
2198 p->filter_html = camel_mime_filter_html_new();
2200 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2201 html_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_html);
2203 idx_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_index);
2205 camel_data_wrapper_write_to_stream(containee, (CamelStream *)p->filter_stream);
2206 camel_stream_flush((CamelStream *)p->filter_stream);
2208 camel_stream_filter_remove(p->filter_stream, idx_id);
2209 camel_stream_filter_remove(p->filter_stream, html_id);
2220 * Find the state of the flag @name in @list.
2222 * Return value: The state of the flag (TRUE or FALSE).
2225 camel_flag_get(CamelFlag **list, const char *name)
2230 if (!strcmp(flag->name, name))
2243 * Set the state of a flag @name in the list @list to @value.
2245 * Return value: Whether or not it changed.
2248 camel_flag_set(CamelFlag **list, const char *name, gboolean value)
2250 CamelFlag *flag, *tmp;
2252 /* this 'trick' works because flag->next is the first element */
2253 flag = (CamelFlag *)list;
2254 while (flag->next) {
2256 if (!strcmp(flag->next->name, name)) {
2258 flag->next = tmp->next;
2267 tmp = g_malloc(sizeof(*tmp) + strlen(name));
2268 strcpy(tmp->name, name);
2276 * camel_flag_list_size:
2279 * Get the length of the flag list.
2281 * Return value: The number of TRUE flags in the list.
2284 camel_flag_list_size(CamelFlag **list)
2298 * camel_flag_list_free:
2301 * Free the memory associated with the flag list @list.
2304 camel_flag_list_free(CamelFlag **list)
2306 CamelFlag *flag, *tmp;
2317 * camel_flag_list_copy:
2321 * Copy a flag list, return true if the destination list @to changed.
2326 camel_flag_list_copy(CamelFlag **to, CamelFlag **from)
2328 CamelFlag *flag, *tmp;
2329 int changed = FALSE;
2331 if (*to == NULL && from == NULL)
2334 /* Remove any now-missing flags */
2335 flag = (CamelFlag *)to;
2336 while (flag->next) {
2338 if (!camel_flag_get(from, tmp->name)) {
2339 flag->next = tmp->next;
2347 /* Add any new flags */
2350 changed |= camel_flag_set(to, flag->name, TRUE);
2358 camel_tag_get(CamelTag **list, const char *name)
2364 if (!strcmp(tag->name, name))
2365 return (const char *)tag->value;
2377 * Set the tag @name in the tag list @list to @value.
2379 * Return value: whether or not it changed
2382 camel_tag_set(CamelTag **list, const char *name, const char *value)
2384 CamelTag *tag, *tmp;
2386 /* this 'trick' works because tag->next is the first element */
2387 tag = (CamelTag *)list;
2390 if (!strcmp(tmp->name, name)) {
2391 if (value == NULL) { /* clear it? */
2392 tag->next = tmp->next;
2396 } else if (strcmp(tmp->value, value)) { /* has it changed? */
2398 tmp->value = g_strdup(value);
2407 tmp = g_malloc(sizeof(*tmp)+strlen(name));
2408 strcpy(tmp->name, name);
2409 tmp->value = g_strdup(value);
2418 * camel_tag_list_size:
2421 * Get the number of tags present in the tag list @list.
2423 * Return value: The number of tags.
2425 int camel_tag_list_size(CamelTag **list)
2439 rem_tag(char *key, char *value, CamelTag **to)
2441 camel_tag_set(to, key, NULL);
2445 * camel_tag_list_copy:
2449 * Copy a list of tags.
2454 camel_tag_list_copy(CamelTag **to, CamelTag **from)
2456 int changed = FALSE;
2460 if (*to == NULL && from == NULL)
2463 left = g_hash_table_new(g_str_hash, g_str_equal);
2466 g_hash_table_insert(left, tag->name, tag);
2472 changed |= camel_tag_set(to, tag->name, tag->value);
2473 g_hash_table_remove(left, tag->name);
2477 if (g_hash_table_size(left)>0) {
2478 g_hash_table_foreach(left, (GHFunc)rem_tag, to);
2481 g_hash_table_destroy(left);
2487 * camel_tag_list_free:
2490 * Free the tag list @list.
2492 void camel_tag_list_free(CamelTag **list)
2494 CamelTag *tag, *tmp;
2505 struct flag_names_t {
2509 { "answered", CAMEL_MESSAGE_ANSWERED },
2510 { "deleted", CAMEL_MESSAGE_DELETED },
2511 { "draft", CAMEL_MESSAGE_DELETED },
2512 { "flagged", CAMEL_MESSAGE_FLAGGED },
2513 { "seen", CAMEL_MESSAGE_SEEN },
2514 { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
2519 * camel_system_flag:
2522 * Returns the integer value of the flag string.
2525 camel_system_flag (const char *name)
2527 struct flag_names_t *flag;
2529 g_return_val_if_fail (name != NULL, 0);
2531 for (flag = flag_names; *flag->name; flag++)
2532 if (!strcasecmp (name, flag->name))
2539 * camel_system_flag_get:
2543 * Find the state of the flag @name in @flags.
2545 * Return value: The state of the flag (TRUE or FALSE).
2548 camel_system_flag_get (guint32 flags, const char *name)
2550 g_return_val_if_fail (name != NULL, FALSE);
2552 return flags & camel_system_flag (name);
2557 * camel_message_info_new:
2559 * Returns a new CamelMessageInfo structure.
2562 camel_message_info_new (void)
2564 CamelMessageInfo *info;
2566 info = g_malloc0(sizeof(*info));
2568 info->strings = e_poolv_new(CAMEL_MESSAGE_INFO_LAST);
2571 info->strings = e_strv_new (CAMEL_MESSAGE_INFO_LAST);
2579 * camel_message_info_ref:
2582 * Reference an info.
2584 * NOTE: This interface is not MT-SAFE, like the others.
2586 void camel_message_info_ref(CamelMessageInfo *info)
2588 GLOBAL_INFO_LOCK(info);
2590 GLOBAL_INFO_UNLOCK(info);
2594 * camel_message_info_new_from_header:
2595 * @header: raw header
2597 * Returns a new CamelMessageInfo structure populated by the header.
2600 camel_message_info_new_from_header (struct _header_raw *header)
2602 CamelMessageInfo *info;
2603 char *subject, *from, *to, *cc, *mlist;
2604 struct _header_content_type *ct = NULL;
2605 const char *content, *date, *charset = NULL;
2607 if ((content = header_raw_find(&header, "Content-Type", NULL))
2608 && (ct = header_content_type_decode(content))
2609 && (charset = header_content_type_param(ct, "charset"))
2610 && (strcasecmp(charset, "us-ascii") == 0))
2613 charset = charset ? e_iconv_charset_name (charset) : NULL;
2615 subject = summary_format_string(header, "subject", charset);
2616 from = summary_format_address(header, "from", charset);
2617 to = summary_format_address(header, "to", charset);
2618 cc = summary_format_address(header, "cc", charset);
2619 date = header_raw_find(&header, "date", NULL);
2620 mlist = header_raw_check_mailing_list(&header);
2623 header_content_type_unref(ct);
2625 info = camel_message_info_new();
2627 camel_message_info_set_subject(info, subject);
2628 camel_message_info_set_from(info, from);
2629 camel_message_info_set_to(info, to);
2630 camel_message_info_set_cc(info, cc);
2631 camel_message_info_set_mlist(info, mlist);
2634 info->date_sent = header_decode_date (date, NULL);
2636 info->date_sent = time (NULL);
2638 date = header_raw_find (&header, "received", NULL);
2639 if (date && (date = strrchr (date, ';')))
2643 info->date_received = header_decode_date (date, NULL);
2645 info->date_received = time (NULL);
2651 * camel_message_info_dup_to:
2652 * @from: source message info
2653 * @to: destination message info
2655 * Duplicates the contents of one CamelMessageInfo structure into another.
2656 * (The destination is assumed to be empty: its contents are not freed.)
2657 * The slightly odd interface is to allow this to be used to initialize
2658 * "subclasses" of CamelMessageInfo.
2661 camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to)
2667 to->flags = from->flags;
2668 to->size = from->size;
2669 to->date_sent = from->date_sent;
2670 to->date_received = from->date_received;
2675 to->strings = e_poolv_cpy (to->strings, from->strings);
2676 #elif defined (DOESTRV)
2677 /* to->strings = e_strv_new(CAMEL_MESSAGE_INFO_LAST); */
2678 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_SUBJECT, camel_message_info_subject(from));
2679 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_FROM, camel_message_info_from(from));
2680 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_TO, camel_message_info_to(from));
2681 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_CC, camel_message_info_cc(from));
2682 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_UID, camel_message_info_uid(from));
2683 e_strv_set(to->strings, CAMEL_MESSAGE_INFO_UID, camel_message_info_mlist(from));
2685 to->subject = g_strdup(from->subject);
2686 to->from = g_strdup(from->from);
2687 to->to = g_strdup(from->to);
2688 to->cc = g_strdup(from->cc);
2689 to->uid = g_strdup(from->uid);
2690 to->mlist = g_strdup(from->mlist);
2692 memcpy(&to->message_id, &from->message_id, sizeof(from->message_id));
2694 /* Copy structures */
2695 if (from->references) {
2696 int len = sizeof(*from->references) + ((from->references->size-1) * sizeof(from->references->references[0]));
2698 to->references = g_malloc(len);
2699 memcpy(to->references, from->references, len);
2701 to->references = NULL;
2704 flag = from->user_flags;
2706 camel_flag_set(&to->user_flags, flag->name, TRUE);
2710 tag = from->user_tags;
2712 camel_tag_set(&to->user_tags, tag->name, tag->value);
2716 /* No, this is impossible without knowing the class of summary we came from */
2717 /* FIXME some day */
2722 * camel_message_info_free:
2723 * @mi: the message info
2725 * Unref's and potentially frees a CamelMessageInfo and its contents.
2727 * Can only be used to free CamelMessageInfo's created with
2728 * camel_message_info_dup_to.
2730 * NOTE: This interface is not MT-SAFE, like the others.
2734 camel_message_info_free(CamelMessageInfo *mi)
2736 g_return_if_fail(mi != NULL);
2738 GLOBAL_INFO_LOCK(info);
2740 if (mi->refcount > 0) {
2741 GLOBAL_INFO_UNLOCK(info);
2744 GLOBAL_INFO_UNLOCK(info);
2747 e_poolv_destroy(mi->strings);
2748 #elif defined (DOESTRV)
2749 e_strv_destroy(mi->strings);
2752 g_free(mi->subject);
2758 g_free(mi->references);
2759 camel_flag_list_free(&mi->user_flags);
2760 camel_tag_list_free(&mi->user_tags);
2761 /* FIXME: content info? */
2765 #if defined (DOEPOOLV) || defined (DOESTRV)
2767 camel_message_info_string (const CamelMessageInfo *mi, int type)
2769 g_assert (mi != NULL);
2771 if (mi->strings == NULL)
2774 return e_poolv_get (mi->strings, type);
2776 return e_strv_get (mi->strings, type);
2781 camel_message_info_set_string (CamelMessageInfo *mi, int type, char *str)
2783 g_assert (mi != NULL);
2784 g_assert (mi->strings != NULL);
2786 e_poolv_set (mi->strings, type, str, TRUE);
2788 mi->strings = e_strv_set_ref_free (mi->strings, type, str);
2795 camel_content_info_dump (CamelMessageContentInfo *ci, int depth)
2799 p = alloca (depth * 4 + 1);
2800 memset (p, ' ', depth * 4);
2804 printf ("%s<empty>\n", p);
2809 printf ("%scontent-type: %s/%s\n", p, ci->type->type ? ci->type->type : "(null)",
2810 ci->type->subtype ? ci->type->subtype : "(null)");
2812 printf ("%scontent-type: <unset>\n", p);
2813 printf ("%scontent-transfer-encoding: %s\n", p, ci->encoding ? ci->encoding : "(null)");
2814 printf ("%scontent-description: %s\n", p, ci->description ? ci->description : "(null)");
2815 printf ("%ssize: %lu\n", p, (unsigned long) ci->size);
2818 camel_content_info_dump (ci, depth + 1);
2824 camel_message_info_dump (CamelMessageInfo *mi)
2827 printf("No message?\n");
2831 printf("Subject: %s\n", camel_message_info_subject(mi));
2832 printf("To: %s\n", camel_message_info_to(mi));
2833 printf("Cc: %s\n", camel_message_info_cc(mi));
2834 printf("mailing list: %s\n", camel_message_info_mlist(mi));
2835 printf("From: %s\n", camel_message_info_from(mi));
2836 printf("UID: %s\n", camel_message_info_uid(mi));
2837 printf("Flags: %04x\n", mi->flags & 0xffff);
2838 camel_content_info_dump(mi->content, 0);