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 "string-utils.h"
54 #include "e-util/md5-utils.h"
55 #include "e-util/e-memory.h"
57 #include "camel-private.h"
62 static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER;
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)
68 #define GLOBAL_INFO_LOCK(i)
69 #define GLOBAL_INFO_UNLOCK(i)
72 /* this should probably be conditional on it existing */
76 #define io(x) /* io debug */
79 extern int strdup_count, malloc_count, free_count;
82 #define CAMEL_FOLDER_SUMMARY_VERSION (12)
84 #define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv)
86 /* trivial lists, just because ... */
91 static struct _node *my_list_append(struct _node **list, struct _node *n);
92 static int my_list_size(struct _node **list);
94 static int summary_header_load(CamelFolderSummary *, FILE *);
95 static int summary_header_save(CamelFolderSummary *, FILE *);
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 *);
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 *);
111 static char *next_uid_string(CamelFolderSummary *s);
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);
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);
120 static CamelObjectClass *camel_folder_summary_parent;
123 camel_folder_summary_class_init (CamelFolderSummaryClass *klass)
125 camel_folder_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
127 klass->summary_header_load = summary_header_load;
128 klass->summary_header_save = summary_header_save;
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;
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;
144 klass->next_uid_string = next_uid_string;
148 camel_folder_summary_init (CamelFolderSummary *s)
150 struct _CamelFolderSummaryPrivate *p;
152 p = _PRIVATE(s) = g_malloc0(sizeof(*p));
154 p->filter_charset = g_hash_table_new(g_strcase_hash, g_strcase_equal);
156 s->message_info_size = sizeof(CamelMessageInfo);
157 s->content_info_size = sizeof(CamelMessageContentInfo);
159 s->message_info_chunks = NULL;
160 s->content_info_chunks = NULL;
162 #if defined (DOESTRV) || defined (DOEPOOLV)
163 s->message_info_strings = CAMEL_MESSAGE_INFO_LAST;
166 s->version = CAMEL_FOLDER_SUMMARY_VERSION;
171 s->messages = g_ptr_array_new();
172 s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
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();
183 static void free_o_name(void *key, void *value, void *data)
185 camel_object_unref((CamelObject *)value);
190 camel_folder_summary_finalize (CamelObject *obj)
192 struct _CamelFolderSummaryPrivate *p;
193 CamelFolderSummary *s = (CamelFolderSummary *)obj;
197 camel_folder_summary_clear(s);
198 g_ptr_array_free(s->messages, TRUE);
199 g_hash_table_destroy(s->messages_uid);
201 g_hash_table_foreach(p->filter_charset, free_o_name, 0);
202 g_hash_table_destroy(p->filter_charset);
204 g_free(s->summary_path);
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);
212 camel_object_unref((CamelObject *)p->filter_index);
214 camel_object_unref((CamelObject *)p->filter_64);
216 camel_object_unref((CamelObject *)p->filter_qp);
218 camel_object_unref((CamelObject *)p->filter_uu);
220 camel_object_unref((CamelObject *)p->filter_save);
222 camel_object_unref((CamelObject *)p->filter_html);
224 if (p->filter_stream)
225 camel_object_unref((CamelObject *)p->filter_stream);
227 camel_object_unref((CamelObject *)p->index);
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);
241 camel_folder_summary_get_type (void)
243 static CamelType type = CAMEL_INVALID_TYPE;
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,
251 (CamelObjectInitFunc) camel_folder_summary_init,
252 (CamelObjectFinalizeFunc) camel_folder_summary_finalize);
259 * camel_folder_summary_new:
261 * Create a new CamelFolderSummary object.
263 * Return value: A new CamelFolderSummary widget.
266 camel_folder_summary_new (void)
268 CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ())); return new;
273 * camel_folder_summary_set_filename:
277 * Set the filename where the summary will be loaded to/saved from.
279 void camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name)
281 CAMEL_SUMMARY_LOCK(s, summary_lock);
283 g_free(s->summary_path);
284 s->summary_path = g_strdup(name);
286 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
290 * camel_folder_summary_set_index:
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.
297 * Unlike earlier behaviour, build_content need not be set to perform indexing.
299 void camel_folder_summary_set_index(CamelFolderSummary *s, CamelIndex *index)
301 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
304 camel_object_unref((CamelObject *)p->index);
308 camel_object_ref((CamelObject *)index);
312 * camel_folder_summary_set_build_content:
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
320 void camel_folder_summary_set_build_content(CamelFolderSummary *s, gboolean state)
322 s->build_content = state;
326 * camel_folder_summary_count:
329 * Get the number of summary items stored in this summary.
331 * Return value: The number of items int he summary.
334 camel_folder_summary_count(CamelFolderSummary *s)
336 return s->messages->len;
340 * camel_folder_summary_index:
344 * Retrieve a summary item by index number.
346 * A referenced to the summary item is returned, which may be
347 * ref'd or free'd as appropriate.
349 * Return value: The summary item, or NULL if the index @i is out
351 * It must be freed using camel_folder_summary_info_free().
354 camel_folder_summary_index(CamelFolderSummary *s, int i)
356 CamelMessageInfo *info = NULL;
358 CAMEL_SUMMARY_LOCK(s, summary_lock);
359 CAMEL_SUMMARY_LOCK(s, ref_lock);
361 if (i<s->messages->len)
362 info = g_ptr_array_index(s->messages, i);
367 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
368 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
374 * camel_folder_summary_index:
378 * Obtain a copy of the summary array. This is done atomically,
379 * so cannot contain empty entries.
381 * It must be freed using camel_folder_summary_array_free().
384 camel_folder_summary_array(CamelFolderSummary *s)
386 CamelMessageInfo *info;
387 GPtrArray *res = g_ptr_array_new();
390 CAMEL_SUMMARY_LOCK(s, summary_lock);
391 CAMEL_SUMMARY_LOCK(s, ref_lock);
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);
399 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
400 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
406 * camel_folder_summary_array_free:
410 * Free the folder summary array.
413 camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array)
417 for (i=0;i<array->len;i++)
418 camel_folder_summary_info_free(s, array->pdata[i]);
420 g_ptr_array_free(array, TRUE);
424 * camel_folder_summary_uid:
428 * Retrieve a summary item by uid.
430 * A referenced to the summary item is returned, which may be
431 * ref'd or free'd as appropriate.
433 * Return value: The summary item, or NULL if the uid @uid
435 * It must be freed using camel_folder_summary_info_free().
438 camel_folder_summary_uid(CamelFolderSummary *s, const char *uid)
440 CamelMessageInfo *info;
442 CAMEL_SUMMARY_LOCK(s, summary_lock);
443 CAMEL_SUMMARY_LOCK(s, ref_lock);
445 info = g_hash_table_lookup(s->messages_uid, uid);
450 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
451 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
457 * camel_folder_summary_next_uid:
460 * Generate a new unique uid value as an integer. This
461 * may be used to create a unique sequence of numbers.
463 * Return value: The next unique uid value.
465 guint32 camel_folder_summary_next_uid(CamelFolderSummary *s)
470 CAMEL_SUMMARY_LOCK(s, summary_lock);
474 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
476 /* FIXME: sync this to disk */
477 /* summary_header_save(s);*/
482 * camel_folder_summary_set_uid:
484 * @uid: The next minimum uid to assign. To avoid clashing
485 * uid's, set this to the uid of a given messages + 1.
487 * Set the next minimum uid available. This can be used to
488 * ensure new uid's do not clash with existing uid's.
490 void camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid)
492 /* TODO: sync to disk? */
493 CAMEL_SUMMARY_LOCK(s, summary_lock);
495 s->nextuid = MAX(s->nextuid, uid);
497 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
501 * camel_folder_summary_next_uid_string:
504 * Retrieve the next uid, but as a formatted string.
506 * Return value: The next uid as an unsigned integer string.
507 * This string must be freed by the caller.
510 camel_folder_summary_next_uid_string(CamelFolderSummary *s)
512 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->next_uid_string(s);
515 /* loads the content descriptions, recursively */
516 static CamelMessageContentInfo *
517 perform_content_info_load(CamelFolderSummary *s, FILE *in)
521 CamelMessageContentInfo *ci, *part;
523 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_load(s, in);
527 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500) {
528 camel_folder_summary_content_info_free(s, ci);
532 for (i=0;i<count;i++) {
533 part = perform_content_info_load(s, in);
535 my_list_append((struct _node **)&ci->childs, (struct _node *)part);
538 d(fprintf (stderr, "Summary file format messed up?"));
539 camel_folder_summary_content_info_free(s, ci);
547 camel_folder_summary_load(CamelFolderSummary *s)
551 CamelMessageInfo *mi;
553 if (s->summary_path == NULL)
556 in = fopen(s->summary_path, "r");
560 CAMEL_SUMMARY_LOCK(s, io_lock);
561 if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
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);
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);
579 camel_folder_summary_add(s, mi);
582 CAMEL_SUMMARY_UNLOCK(s, io_lock);
584 if (fclose (in) != 0)
587 s->flags &= ~CAMEL_SUMMARY_DIRTY;
593 g_warning ("Cannot load summary file: `%s': %s", s->summary_path, g_strerror (errno));
595 CAMEL_SUMMARY_UNLOCK(s, io_lock);
597 s->flags |= ~CAMEL_SUMMARY_DIRTY;
602 /* saves the content descriptions, recursively */
604 perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
606 CamelMessageContentInfo *part;
608 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->content_info_save (s, out, ci) == -1)
611 if (camel_file_util_encode_uint32 (out, my_list_size ((struct _node **)&ci->childs)) == -1)
616 if (perform_content_info_save (s, out, part) == -1)
625 * camel_folder_summary_save:
628 * Writes the summary to disk. The summary is only written if changes
631 * Return value: Returns -1 on error.
634 camel_folder_summary_save(CamelFolderSummary *s)
639 CamelMessageInfo *mi;
642 if (s->summary_path == NULL
643 || (s->flags & CAMEL_SUMMARY_DIRTY) == 0)
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);
651 out = fdopen(fd, "w");
660 io(printf("saving header\n"));
662 CAMEL_SUMMARY_LOCK(s, io_lock);
664 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1)
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)
675 if (s->build_content) {
676 if (perform_content_info_save (s, out, mi->content) == -1)
681 if (fflush (out) != 0 || fsync (fileno (out)) == -1)
686 CAMEL_SUMMARY_UNLOCK(s, io_lock);
688 if (rename(path, s->summary_path) == -1) {
695 s->flags &= ~CAMEL_SUMMARY_DIRTY;
704 CAMEL_SUMMARY_UNLOCK(s, io_lock);
713 * camel_folder_summary_header_load:
714 * @s: Summary object.
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.
720 * Return value: -1 on error.
722 int camel_folder_summary_header_load(CamelFolderSummary *s)
727 if (s->summary_path == NULL)
730 in = fopen(s->summary_path, "r");
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);
739 s->flags &= ~CAMEL_SUMMARY_DIRTY;
744 summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info)
747 CamelMessageInfo *mi;
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);
755 CAMEL_SUMMARY_LOCK(s, summary_lock);
757 while ((mi = g_hash_table_lookup(s->messages_uid, uid))) {
758 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
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);
768 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
773 * camel_folder_summary_add:
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().
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.
784 void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info)
789 if (summary_assign_uid(s, info) == 0)
792 CAMEL_SUMMARY_LOCK(s, summary_lock);
794 /* unnecessary for pooled vectors */
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);
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;
805 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
809 * camel_folder_summary_add_from_header:
813 * Build a new info record based on a set of headers, and add it to the
816 * Note that this function should not be used if build_content_info has
817 * been specified for this summary.
819 * Return value: The newly added record.
821 CamelMessageInfo *camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _header_raw *h)
823 CamelMessageInfo *info = camel_folder_summary_info_new_from_header(s, h);
825 camel_folder_summary_add(s, info);
831 * camel_folder_summary_add_from_parser:
835 * Build a new info record based on the current position of a CamelMimeParser.
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.
841 * Return value: The newly added record.
843 CamelMessageInfo *camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
845 CamelMessageInfo *info = camel_folder_summary_info_new_from_parser(s, mp);
847 camel_folder_summary_add(s, info);
853 * camel_folder_summary_add_from_message:
857 * Add a summary item from an existing message.
861 CamelMessageInfo *camel_folder_summary_add_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
863 CamelMessageInfo *info = camel_folder_summary_info_new_from_message(s, msg);
865 camel_folder_summary_add(s, info);
871 * camel_folder_summary_info_new_from_header:
875 * Create a new info record from a header.
877 * Return value: Guess? This info record MUST be freed using
878 * camel_folder_summary_info_free(), camel_message_info_free() will not work.
880 CamelMessageInfo *camel_folder_summary_info_new_from_header(CamelFolderSummary *s, struct _header_raw *h)
882 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> message_info_new(s, h);
886 * camel_folder_summary_info_new_from_parser:
890 * Create a new info record from a parser. If the parser cannot
891 * determine a uid, then none will be assigned.
893 * If indexing is enabled, and the parser cannot determine a new uid, then
894 * one is automatically assigned.
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().
900 * Once complete, the parser will be positioned at the end of
903 * Return value: Guess? This info record MUST be freed using
904 * camel_folder_summary_info_free(), camel_message_info_free() will not work.
906 CamelMessageInfo *camel_folder_summary_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
908 CamelMessageInfo *info = NULL;
911 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
913 CamelIndexName *name = NULL;
915 /* should this check the parser is in the right state, or assume it is?? */
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);
921 camel_mime_parser_unstep(mp);
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 */
926 summary_assign_uid(s, info);
928 CAMEL_SUMMARY_LOCK(s, filter_lock);
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);
938 /* always scan the content info, even if we dont save it */
939 info->content = summary_build_content_info(s, info, mp);
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);
947 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
949 info->size = camel_mime_parser_tell(mp) - start;
955 * camel_folder_summary_info_new_from_message:
959 * Create a summary item from a message.
963 CamelMessageInfo *camel_folder_summary_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
965 CamelMessageInfo *info;
966 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
967 CamelIndexName *name = NULL;
969 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_message(s, msg);
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 */
974 summary_assign_uid(s, info);
976 CAMEL_SUMMARY_LOCK(s, filter_lock);
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);
985 if (p->filter_stream == NULL) {
986 CamelStream *null = camel_stream_null_new();
988 p->filter_stream = camel_stream_filter_new_with_stream(null);
989 camel_object_unref((CamelObject *)null);
993 info->content = summary_build_content_info_message(s, info, (CamelMimePart *)msg);
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);
1001 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1007 * camel_folder_summary_content_info_free:
1011 * Free the content info @ci, and all associated memory.
1014 camel_folder_summary_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1016 CamelMessageContentInfo *pw, *pn;
1019 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_free(s, ci);
1022 camel_folder_summary_content_info_free(s, pw);
1028 * camel_folder_summary_info_free:
1032 * Unref and potentially free the message info @mi, and all associated memory.
1034 void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
1036 CamelMessageContentInfo *ci;
1041 CAMEL_SUMMARY_LOCK(s, ref_lock);
1043 g_assert(mi->refcount >= 1);
1046 if (mi->refcount > 0) {
1047 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1051 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
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);
1062 * camel_folder_summary_info_ref:
1066 * Add an extra reference to @mi.
1068 void camel_folder_summary_info_ref(CamelFolderSummary *s, CamelMessageInfo *mi)
1073 CAMEL_SUMMARY_LOCK(s, ref_lock);
1074 g_assert(mi->refcount >= 1);
1076 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1080 * camel_folder_summary_touch:
1083 * Mark the summary as changed, so that a save will save it.
1086 camel_folder_summary_touch(CamelFolderSummary *s)
1088 CAMEL_SUMMARY_LOCK(s, summary_lock);
1089 s->flags |= CAMEL_SUMMARY_DIRTY;
1090 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1094 * camel_folder_summary_clear:
1097 * Empty the summary contents.
1100 camel_folder_summary_clear(CamelFolderSummary *s)
1104 CAMEL_SUMMARY_LOCK(s, summary_lock);
1105 if (camel_folder_summary_count(s) == 0) {
1106 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1110 for (i=0;i<s->messages->len;i++)
1111 camel_folder_summary_info_free(s, s->messages->pdata[i]);
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);
1121 * camel_folder_summary_remove:
1125 * Remove a specific @info record from the summary.
1127 void camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info)
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);
1135 camel_folder_summary_info_free(s, info);
1139 * camel_folder_summary_remove_uid:
1143 * Remove a specific info record from the summary, by @uid.
1145 void camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid)
1147 CamelMessageInfo *oldinfo;
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);
1160 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1161 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1166 * camel_folder_summary_remove_index:
1170 * Remove a specific info record from the summary, by index.
1172 void camel_folder_summary_remove_index(CamelFolderSummary *s, int index)
1174 CAMEL_SUMMARY_LOCK(s, summary_lock);
1175 if (index < s->messages->len) {
1176 CamelMessageInfo *info = s->messages->pdata[index];
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;
1182 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1183 camel_folder_summary_info_free(s, info);
1185 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1190 * camel_folder_summary_remove_range:
1192 * @start: initial index
1193 * @end: last index to remove
1195 * Removes an indexed range of info records.
1197 void camel_folder_summary_remove_range(CamelFolderSummary *s, int start, int end)
1202 CAMEL_SUMMARY_LOCK(s, summary_lock);
1203 if (start < s->messages->len) {
1204 CamelMessageInfo **infos;
1207 end = MIN(end+1, s->messages->len);
1208 infos = g_malloc((end-start)*sizeof(infos[0]));
1210 for (i=start;i<end;i++) {
1211 CamelMessageInfo *info = s->messages->pdata[i];
1213 infos[i-start] = info;
1214 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
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;
1221 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1223 for (i=start;i<end;i++)
1224 camel_folder_summary_info_free(s, infos[i-start]);
1227 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
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[] = {
1260 "us-ascii", /* 25 words */
1263 #define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
1267 1-tokens_len == tokens[id-1]
1268 >=32 string, length = n-32
1273 token_search_cmp(char *key, char **index)
1275 d(printf("comparing '%s' to '%s'\n", key, *index));
1276 return strcmp(key, *index);
1281 * camel_folder_summary_encode_token:
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.
1289 * Return value: -1 on error.
1292 camel_folder_summary_encode_token(FILE *out, const char *str)
1294 io(printf("Encoding token: '%s'\n", str));
1297 return camel_file_util_encode_uint32(out, 0);
1299 int len = strlen(str);
1307 lower[i] = tolower(str[i]);
1310 match = bsearch(lower, tokens, tokens_len, sizeof(char *), (int (*)(const void *, const void *))token_search_cmp);
1312 token = match-tokens;
1314 for (i=0;i<tokens_len;i++) {
1315 if (!strcmp(tokens[i], lower)) {
1323 return camel_file_util_encode_uint32(out, token+1);
1325 if (camel_file_util_encode_uint32(out, len+32) == -1)
1327 if (fwrite(str, len, 1, out) != 1)
1335 * camel_folder_summary_decode_token:
1339 * Decode a token value.
1341 * Return value: -1 on error.
1344 camel_folder_summary_decode_token(FILE *in, char **str)
1349 io(printf("Decode token ...\n"));
1351 if (camel_file_util_decode_uint32(in, &len) == -1) {
1352 io(printf ("Could not decode token from file"));
1360 } else if (len<= tokens_len) {
1361 ret = g_strdup(tokens[len-1]);
1363 io(printf ("Invalid token encountered: %d", len));
1367 } else if (len > 10240) {
1368 io(printf ("Got broken string header length: %d bytes", len));
1373 ret = g_malloc(len+1);
1374 if (fread(ret, len, 1, in) != 1) {
1382 io(printf("Token = '%s'\n", ret));
1388 static struct _node *
1389 my_list_append(struct _node **list, struct _node *n)
1391 struct _node *ln = (struct _node *)list;
1400 my_list_size(struct _node **list)
1403 struct _node *ln = (struct _node *)list;
1412 summary_header_load(CamelFolderSummary *s, FILE *in)
1414 gint32 version, flags, nextuid, count;
1417 fseek(in, 0, SEEK_SET);
1419 io(printf("Loading header\n"));
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) {
1429 s->nextuid = nextuid;
1432 s->saved_count = count;
1433 if (s->version != version) {
1434 d(printf ("Summary header version mismatch"));
1442 summary_header_save(CamelFolderSummary *s, FILE *out)
1444 fseek(out, 0, SEEK_SET);
1446 io(printf("Savining header\n"));
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));
1455 /* are these even useful for anything??? */
1456 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1458 CamelMessageInfo *mi = NULL;
1461 state = camel_mime_parser_state(mp);
1465 case HSCAN_MULTIPART:
1466 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new(s, camel_mime_parser_headers_raw(mp));
1469 g_error("Invalid parser state");
1475 static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1477 CamelMessageContentInfo *ci = NULL;
1479 switch (camel_mime_parser_state(mp)) {
1482 case HSCAN_MULTIPART:
1483 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new(s, camel_mime_parser_headers_raw(mp));
1485 ci->type = camel_mime_parser_content_type(mp);
1486 header_content_type_ref(ci->type);
1490 g_error("Invalid parser state");
1496 static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1498 CamelMessageInfo *mi;
1500 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new(s, ((CamelMimePart *)msg)->headers);
1505 static CamelMessageContentInfo * content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp)
1507 CamelMessageContentInfo *ci;
1509 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new(s, mp->headers);
1515 summary_format_address(struct _header_raw *h, const char *name, const char *charset)
1517 struct _header_address *addr;
1521 text = header_raw_find (&h, name, NULL);
1522 addr = header_address_decode (text, charset);
1524 ret = header_address_list_format (addr);
1525 header_address_list_clear (&addr);
1527 ret = g_strdup (text);
1534 summary_format_string (struct _header_raw *h, const char *name, const char *charset)
1538 text = header_raw_find (&h, name, NULL);
1540 while (isspace ((unsigned) *text))
1542 return header_decode_string (text, charset);
1549 * camel_folder_summary_info_new:
1552 * Allocate a new camel message info, suitable for adding
1558 camel_folder_summary_info_new(CamelFolderSummary *s)
1560 CamelMessageInfo *mi;
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);
1568 memset(mi, 0, s->message_info_size);
1570 mi->strings = e_poolv_new (s->message_info_strings);
1573 mi->strings = e_strv_new(s->message_info_strings);
1580 * camel_folder_summary_content_info_new:
1583 * Allocate a new camel message content info, suitable for adding
1588 CamelMessageContentInfo *
1589 camel_folder_summary_content_info_new(CamelFolderSummary *s)
1591 CamelMessageContentInfo *ci;
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);
1599 memset(ci, 0, s->content_info_size);
1603 static CamelMessageInfo *
1604 message_info_new(CamelFolderSummary *s, struct _header_raw *h)
1606 CamelMessageInfo *mi;
1607 const char *received;
1609 struct _header_references *refs, *irt, *scan;
1612 char *subject, *from, *to, *cc, *mlist;
1613 struct _header_content_type *ct = NULL;
1614 const char *content, *charset = NULL;
1616 mi = camel_folder_summary_info_new(s);
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))
1624 charset = charset ? e_iconv_charset_name (charset) : NULL;
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);
1633 header_content_type_unref(ct);
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);
1648 mi->subject = subject;
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);
1660 received = strrchr(received, ';');
1662 mi->date_received = header_decode_date(received + 1, NULL);
1664 mi->date_received = 0;
1666 msgid = header_msgid_decode(header_raw_find(&h, "message-id", NULL));
1668 md5_get_digest(msgid, strlen(msgid), digest);
1669 memcpy(mi->message_id.id.hash, digest, sizeof(mi->message_id.id.hash));
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));
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. */
1688 count = header_references_list_size(&refs);
1689 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
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));
1698 mi->references->size = count;
1699 header_references_list_clear(&refs);
1706 static CamelMessageInfo *
1707 message_info_load(CamelFolderSummary *s, FILE *in)
1709 CamelMessageInfo *mi;
1712 char *subject, *from, *to, *cc, *mlist, *uid;;
1714 mi = camel_folder_summary_info_new(s);
1716 io(printf("Loading message info\n"));
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);
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);
1745 mi->subject = subject;
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);
1757 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
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);
1769 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1772 for (i=0;i<count;i++) {
1774 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL)
1776 camel_flag_set(&mi->user_flags, name, TRUE);
1780 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1783 for (i=0;i<count;i++) {
1785 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL
1786 || camel_file_util_decode_string(in, &value) == -1)
1788 camel_tag_set(&mi->user_tags, name, value);
1797 camel_folder_summary_info_free(s, mi);
1803 message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
1810 io(printf("Saving message info\n"));
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));
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);
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);
1833 camel_file_util_encode_uint32(out, 0);
1836 count = camel_flag_list_size(&mi->user_flags);
1837 camel_file_util_encode_uint32(out, count);
1838 flag = mi->user_flags;
1840 camel_file_util_encode_string(out, flag->name);
1844 count = camel_tag_list_size(&mi->user_tags);
1845 camel_file_util_encode_uint32(out, count);
1846 tag = mi->user_tags;
1848 camel_file_util_encode_string(out, tag->name);
1849 camel_file_util_encode_string(out, tag->value);
1857 message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
1860 e_poolv_destroy(mi->strings);
1861 #elif defined (DOESTRV)
1862 e_strv_destroy(mi->strings);
1865 g_free(mi->subject);
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);
1877 static CamelMessageContentInfo *
1878 content_info_new (CamelFolderSummary *s, struct _header_raw *h)
1880 CamelMessageContentInfo *ci;
1881 const char *charset;
1883 ci = camel_folder_summary_content_info_new (s);
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));
1893 static CamelMessageContentInfo *
1894 content_info_load(CamelFolderSummary *s, FILE *in)
1896 CamelMessageContentInfo *ci;
1897 char *type, *subtype;
1899 struct _header_content_type *ct;
1901 io(printf("Loading content info\n"));
1903 ci = camel_folder_summary_content_info_new(s);
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? */
1910 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1913 for (i=0;i<count;i++) {
1915 camel_folder_summary_decode_token(in, &name);
1916 camel_folder_summary_decode_token(in, &value);
1917 if (!(name && value))
1920 header_content_type_set_param(ct, name, value);
1921 /* TODO: do this so we dont have to double alloc/free */
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);
1931 camel_file_util_decode_uint32(in, &ci->size);
1939 camel_folder_summary_content_info_free(s, ci);
1944 content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
1946 struct _header_content_type *ct;
1947 struct _header_param *hp;
1949 io(printf("Saving content info\n"));
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));
1958 camel_folder_summary_encode_token(out, hp->name);
1959 camel_folder_summary_encode_token(out, hp->value);
1963 camel_folder_summary_encode_token(out, NULL);
1964 camel_folder_summary_encode_token(out, NULL);
1965 camel_file_util_encode_uint32(out, 0);
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);
1974 content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1976 header_content_type_unref(ci->type);
1978 g_free(ci->description);
1979 g_free(ci->encoding);
1980 e_memchunk_free(s->content_info_chunks, ci);
1984 next_uid_string(CamelFolderSummary *s)
1986 return g_strdup_printf("%u", camel_folder_summary_next_uid(s));
1991 Now this is where all the "smarts" happen, where the content info is built,
1992 and any indexing and what not is performed
1995 /* must have filter_lock before calling this function */
1996 static CamelMessageContentInfo *
1997 summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp)
2002 CamelMessageContentInfo *info = NULL;
2003 struct _header_content_type *ct;
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;
2010 d(printf("building content info\n"));
2012 /* start of this part */
2013 state = camel_mime_parser_step(mp, &buffer, &len);
2014 body = camel_mime_parser_tell(mp);
2016 if (s->build_content)
2017 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_parser(s, mp);
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;
2027 if (p->index && header_content_type_is(ct, "text", "*")) {
2029 const char *charset;
2031 d(printf("generating index:\n"));
2033 encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
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);
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);
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);
2054 camel_mime_filter_reset((CamelMimeFilter *)p->filter_uu);
2055 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_uu);
2057 d(printf(" ignoring encoding %s\n", encoding));
2062 charset = header_content_type_param(ct, "charset");
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);
2069 mfc = camel_mime_filter_charset_new_convert(charset, "UTF-8");
2071 g_hash_table_insert(p->filter_charset, g_strdup(charset), mfc);
2073 camel_mime_filter_reset((CamelMimeFilter *)mfc);
2076 chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc);
2078 g_warning("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset);
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();
2088 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2089 html_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_html);
2092 /* and this filter actually does the indexing */
2093 idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_index);
2095 /* and scan/index everything */
2096 while (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_BODY_END)
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);
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;
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);
2115 part->parent = info;
2116 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2121 d(printf("Summarising message\n"));
2122 /* update attachments flag as we go */
2123 msginfo->flags |= CAMEL_MESSAGE_ATTACHMENTS;
2125 part = summary_build_content_info(s, msginfo, mp);
2127 part->parent = info;
2128 my_list_append((struct _node **)&info->childs, (struct _node *)part);
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);
2138 d(printf("finished building content info\n"));
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)
2148 CamelDataWrapper *containee;
2150 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2151 CamelMessageContentInfo *info = NULL, *child;
2153 if (s->build_content)
2154 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_message(s, object);
2156 containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));
2158 if (containee == NULL)
2161 /* TODO: I find it odd that get_part and get_content_object do not
2162 add a reference, probably need fixing for multithreading */
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;
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);
2177 child = summary_build_content_info_message(s, msginfo, part);
2179 child->parent = info;
2180 my_list_append((struct _node **)&info->childs, (struct _node *)child);
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);
2187 child->parent = info;
2188 my_list_append((struct _node **)&info->childs, (struct _node *)child);
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;
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();
2199 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2200 html_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_html);
2202 idx_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_index);
2204 camel_data_wrapper_write_to_stream(containee, (CamelStream *)p->filter_stream);
2205 camel_stream_flush((CamelStream *)p->filter_stream);
2207 camel_stream_filter_remove(p->filter_stream, idx_id);
2208 camel_stream_filter_remove(p->filter_stream, html_id);
2219 * Find the state of the flag @name in @list.
2221 * Return value: The state of the flag (TRUE or FALSE).
2224 camel_flag_get(CamelFlag **list, const char *name)
2229 if (!strcmp(flag->name, name))
2242 * Set the state of a flag @name in the list @list to @value.
2244 * Return value: Whether or not it changed.
2247 camel_flag_set(CamelFlag **list, const char *name, gboolean value)
2249 CamelFlag *flag, *tmp;
2251 /* this 'trick' works because flag->next is the first element */
2252 flag = (CamelFlag *)list;
2253 while (flag->next) {
2255 if (!strcmp(flag->next->name, name)) {
2257 flag->next = tmp->next;
2266 tmp = g_malloc(sizeof(*tmp) + strlen(name));
2267 strcpy(tmp->name, name);
2275 * camel_flag_list_size:
2278 * Get the length of the flag list.
2280 * Return value: The number of TRUE flags in the list.
2283 camel_flag_list_size(CamelFlag **list)
2297 * camel_flag_list_free:
2300 * Free the memory associated with the flag list @list.
2303 camel_flag_list_free(CamelFlag **list)
2305 CamelFlag *flag, *tmp;
2316 * camel_flag_list_copy:
2320 * Copy a flag list, return true if the destination list @to changed.
2325 camel_flag_list_copy(CamelFlag **to, CamelFlag **from)
2327 CamelFlag *flag, *tmp;
2328 int changed = FALSE;
2330 if (*to == NULL && from == NULL)
2333 /* Remove any now-missing flags */
2334 flag = (CamelFlag *)to;
2335 while (flag->next) {
2337 if (!camel_flag_get(from, tmp->name)) {
2338 flag->next = tmp->next;
2346 /* Add any new flags */
2349 changed |= camel_flag_set(to, flag->name, TRUE);
2357 camel_tag_get(CamelTag **list, const char *name)
2363 if (!strcmp(tag->name, name))
2364 return (const char *)tag->value;
2376 * Set the tag @name in the tag list @list to @value.
2378 * Return value: whether or not it changed
2381 camel_tag_set(CamelTag **list, const char *name, const char *value)
2383 CamelTag *tag, *tmp;
2385 /* this 'trick' works because tag->next is the first element */
2386 tag = (CamelTag *)list;
2389 if (!strcmp(tmp->name, name)) {
2390 if (value == NULL) { /* clear it? */
2391 tag->next = tmp->next;
2395 } else if (strcmp(tmp->value, value)) { /* has it changed? */
2397 tmp->value = g_strdup(value);
2406 tmp = g_malloc(sizeof(*tmp)+strlen(name));
2407 strcpy(tmp->name, name);
2408 tmp->value = g_strdup(value);
2417 * camel_tag_list_size:
2420 * Get the number of tags present in the tag list @list.
2422 * Return value: The number of tags.
2424 int camel_tag_list_size(CamelTag **list)
2438 rem_tag(char *key, char *value, CamelTag **to)
2440 camel_tag_set(to, key, NULL);
2444 * camel_tag_list_copy:
2448 * Copy a list of tags.
2453 camel_tag_list_copy(CamelTag **to, CamelTag **from)
2455 int changed = FALSE;
2459 if (*to == NULL && from == NULL)
2462 left = g_hash_table_new(g_str_hash, g_str_equal);
2465 g_hash_table_insert(left, tag->name, tag);
2471 changed |= camel_tag_set(to, tag->name, tag->value);
2472 g_hash_table_remove(left, tag->name);
2476 if (g_hash_table_size(left)>0) {
2477 g_hash_table_foreach(left, (GHFunc)rem_tag, to);
2480 g_hash_table_destroy(left);
2486 * camel_tag_list_free:
2489 * Free the tag list @list.
2491 void camel_tag_list_free(CamelTag **list)
2493 CamelTag *tag, *tmp;
2504 struct flag_names_t {
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 },
2518 * camel_system_flag:
2521 * Returns the integer value of the flag string.
2524 camel_system_flag (const char *name)
2526 struct flag_names_t *flag;
2528 g_return_val_if_fail (name != NULL, 0);
2530 for (flag = flag_names; *flag->name; flag++)
2531 if (!strcasecmp (name, flag->name))
2538 * camel_system_flag_get:
2542 * Find the state of the flag @name in @flags.
2544 * Return value: The state of the flag (TRUE or FALSE).
2547 camel_system_flag_get (guint32 flags, const char *name)
2549 g_return_val_if_fail (name != NULL, FALSE);
2551 return flags & camel_system_flag (name);
2556 * camel_message_info_new:
2558 * Returns a new CamelMessageInfo structure.
2561 camel_message_info_new (void)
2563 CamelMessageInfo *info;
2565 info = g_malloc0(sizeof(*info));
2567 info->strings = e_poolv_new(CAMEL_MESSAGE_INFO_LAST);
2570 info->strings = e_strv_new (CAMEL_MESSAGE_INFO_LAST);
2578 * camel_message_info_ref:
2581 * Reference an info.
2583 * NOTE: This interface is not MT-SAFE, like the others.
2585 void camel_message_info_ref(CamelMessageInfo *info)
2587 GLOBAL_INFO_LOCK(info);
2589 GLOBAL_INFO_UNLOCK(info);
2593 * camel_message_info_new_from_header:
2594 * @header: raw header
2596 * Returns a new CamelMessageInfo structure populated by the header.
2599 camel_message_info_new_from_header (struct _header_raw *header)
2601 CamelMessageInfo *info;
2602 char *subject, *from, *to, *cc, *mlist;
2603 struct _header_content_type *ct = NULL;
2604 const char *content, *date, *charset = NULL;
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))
2612 charset = charset ? e_iconv_charset_name (charset) : NULL;
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);
2622 header_content_type_unref(ct);
2624 info = camel_message_info_new();
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);
2633 info->date_sent = header_decode_date (date, NULL);
2635 info->date_sent = time (NULL);
2637 date = header_raw_find (&header, "received", NULL);
2638 if (date && (date = strrchr (date, ';')))
2642 info->date_received = header_decode_date (date, NULL);
2644 info->date_received = time (NULL);
2650 * camel_message_info_dup_to:
2651 * @from: source message info
2652 * @to: destination message info
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.
2660 camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to)
2666 to->flags = from->flags;
2667 to->size = from->size;
2668 to->date_sent = from->date_sent;
2669 to->date_received = from->date_received;
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));
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);
2691 memcpy(&to->message_id, &from->message_id, sizeof(from->message_id));
2693 /* Copy structures */
2694 if (from->references) {
2695 int len = sizeof(*from->references) + ((from->references->size-1) * sizeof(from->references->references[0]));
2697 to->references = g_malloc(len);
2698 memcpy(to->references, from->references, len);
2700 to->references = NULL;
2703 flag = from->user_flags;
2705 camel_flag_set(&to->user_flags, flag->name, TRUE);
2709 tag = from->user_tags;
2711 camel_tag_set(&to->user_tags, tag->name, tag->value);
2715 /* No, this is impossible without knowing the class of summary we came from */
2716 /* FIXME some day */
2721 * camel_message_info_free:
2722 * @mi: the message info
2724 * Unref's and potentially frees a CamelMessageInfo and its contents.
2726 * Can only be used to free CamelMessageInfo's created with
2727 * camel_message_info_dup_to.
2729 * NOTE: This interface is not MT-SAFE, like the others.
2733 camel_message_info_free(CamelMessageInfo *mi)
2735 g_return_if_fail(mi != NULL);
2737 GLOBAL_INFO_LOCK(info);
2739 if (mi->refcount > 0) {
2740 GLOBAL_INFO_UNLOCK(info);
2743 GLOBAL_INFO_UNLOCK(info);
2746 e_poolv_destroy(mi->strings);
2747 #elif defined (DOESTRV)
2748 e_strv_destroy(mi->strings);
2751 g_free(mi->subject);
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? */
2764 #if defined (DOEPOOLV) || defined (DOESTRV)
2766 camel_message_info_string (const CamelMessageInfo *mi, int type)
2768 g_assert (mi != NULL);
2770 if (mi->strings == NULL)
2773 return e_poolv_get (mi->strings, type);
2775 return e_strv_get (mi->strings, type);
2780 camel_message_info_set_string (CamelMessageInfo *mi, int type, char *str)
2782 g_assert (mi != NULL);
2783 g_assert (mi->strings != NULL);
2785 e_poolv_set (mi->strings, type, str, TRUE);
2787 mi->strings = e_strv_set_ref_free (mi->strings, type, str);
2794 camel_content_info_dump (CamelMessageContentInfo *ci, int depth)
2798 p = alloca (depth * 4 + 1);
2799 memset (p, ' ', depth * 4);
2803 printf ("%s<empty>\n", p);
2808 printf ("%scontent-type: %s/%s\n", p, ci->type->type ? ci->type->type : "(null)",
2809 ci->type->subtype ? ci->type->subtype : "(null)");
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);
2817 camel_content_info_dump (ci, depth + 1);
2823 camel_message_info_dump (CamelMessageInfo *mi)
2826 printf("No message?\n");
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);