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 Lesser 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 Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
34 #include <sys/types.h>
37 #include <glib/gstdio.h>
39 #include <libedataserver/e-iconv.h>
40 #include <libedataserver/e-memory.h>
41 #include <libedataserver/md5-utils.h>
43 #include "camel-file-utils.h"
44 #include "camel-folder-summary.h"
45 #include "camel-folder.h"
46 #include "camel-mime-filter-basic.h"
47 #include "camel-mime-filter-charset.h"
48 #include "camel-mime-filter-html.h"
49 #include "camel-mime-filter-index.h"
50 #include "camel-mime-filter.h"
51 #include "camel-mime-message.h"
52 #include "camel-multipart.h"
53 #include "camel-private.h"
54 #include "camel-stream-filter.h"
55 #include "camel-stream-mem.h"
56 #include "camel-stream-null.h"
57 #include "camel-string-utils.h"
59 static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER;
61 /* this lock is ONLY for the standalone messageinfo stuff */
62 #define GLOBAL_INFO_LOCK(i) pthread_mutex_lock(&info_lock)
63 #define GLOBAL_INFO_UNLOCK(i) pthread_mutex_unlock(&info_lock)
66 /* this should probably be conditional on it existing */
70 #define io(x) /* io debug */
74 extern int strdup_count, malloc_count, free_count;
77 #define CAMEL_FOLDER_SUMMARY_VERSION (13)
79 #define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv)
81 #define META_SUMMARY_SUFFIX_LEN 5 /* strlen("-meta") */
83 /* trivial lists, just because ... */
88 static struct _node *my_list_append(struct _node **list, struct _node *n);
89 static int my_list_size(struct _node **list);
91 static int summary_header_load(CamelFolderSummary *, FILE *);
92 static int summary_header_save(CamelFolderSummary *, FILE *);
93 static int summary_meta_header_load(CamelFolderSummary *, FILE *);
94 static int summary_meta_header_save(CamelFolderSummary *, FILE *);
96 static CamelMessageInfo * message_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
97 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
98 static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg);
99 static CamelMessageInfo * message_info_load(CamelFolderSummary *, FILE *);
100 static int message_info_save(CamelFolderSummary *, FILE *, CamelMessageInfo *);
101 static int meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *info);
102 static void message_info_free(CamelFolderSummary *, CamelMessageInfo *);
104 static CamelMessageContentInfo * content_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
105 static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
106 static CamelMessageContentInfo * content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp);
107 static CamelMessageContentInfo * content_info_load(CamelFolderSummary *, FILE *);
108 static int content_info_save(CamelFolderSummary *, FILE *, CamelMessageContentInfo *);
109 static void content_info_free(CamelFolderSummary *, CamelMessageContentInfo *);
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_init (CamelFolderSummary *s)
125 struct _CamelFolderSummaryPrivate *p;
127 p = _PRIVATE(s) = g_malloc0(sizeof(*p));
129 p->filter_charset = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
131 s->message_info_size = sizeof(CamelMessageInfoBase);
132 s->content_info_size = sizeof(CamelMessageContentInfo);
134 s->message_info_chunks = NULL;
135 s->content_info_chunks = NULL;
137 #if defined (DOESTRV) || defined (DOEPOOLV)
138 s->message_info_strings = CAMEL_MESSAGE_INFO_LAST;
141 s->version = CAMEL_FOLDER_SUMMARY_VERSION;
146 s->messages = g_ptr_array_new();
147 s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
149 p->summary_lock = g_mutex_new();
150 p->io_lock = g_mutex_new();
151 p->filter_lock = g_mutex_new();
152 p->alloc_lock = g_mutex_new();
153 p->ref_lock = g_mutex_new();
155 s->meta_summary = g_malloc0(sizeof(CamelFolderMetaSummary));
157 /* Default is 20, any implementor having UIDs that has length
158 exceeding 20, has to override this value
160 s->meta_summary->uid_len = 20;
163 static void free_o_name(void *key, void *value, void *data)
165 camel_object_unref((CamelObject *)value);
170 camel_folder_summary_finalize (CamelObject *obj)
172 struct _CamelFolderSummaryPrivate *p;
173 CamelFolderSummary *s = (CamelFolderSummary *)obj;
177 camel_folder_summary_clear(s);
178 g_ptr_array_free(s->messages, TRUE);
179 g_hash_table_destroy(s->messages_uid);
181 g_hash_table_foreach(p->filter_charset, free_o_name, 0);
182 g_hash_table_destroy(p->filter_charset);
184 g_free(s->summary_path);
186 if (s->message_info_chunks)
187 e_memchunk_destroy(s->message_info_chunks);
188 if (s->content_info_chunks)
189 e_memchunk_destroy(s->content_info_chunks);
192 camel_object_unref((CamelObject *)p->filter_index);
194 camel_object_unref((CamelObject *)p->filter_64);
196 camel_object_unref((CamelObject *)p->filter_qp);
198 camel_object_unref((CamelObject *)p->filter_uu);
200 camel_object_unref((CamelObject *)p->filter_save);
202 camel_object_unref((CamelObject *)p->filter_html);
204 if (p->filter_stream)
205 camel_object_unref((CamelObject *)p->filter_stream);
207 camel_object_unref((CamelObject *)p->index);
209 /* Freeing memory occupied by meta-summary-header */
210 g_free(s->meta_summary->path);
211 g_free(s->meta_summary);
213 g_mutex_free(p->summary_lock);
214 g_mutex_free(p->io_lock);
215 g_mutex_free(p->filter_lock);
216 g_mutex_free(p->alloc_lock);
217 g_mutex_free(p->ref_lock);
223 camel_folder_summary_get_type (void)
225 static CamelType type = CAMEL_INVALID_TYPE;
227 if (type == CAMEL_INVALID_TYPE) {
228 type = camel_type_register (camel_object_get_type (), "CamelFolderSummary",
229 sizeof (CamelFolderSummary),
230 sizeof (CamelFolderSummaryClass),
231 (CamelObjectClassInitFunc) camel_folder_summary_class_init,
233 (CamelObjectInitFunc) camel_folder_summary_init,
234 (CamelObjectFinalizeFunc) camel_folder_summary_finalize);
242 * camel_folder_summary_new:
243 * @folder: parent #CamelFolder object
245 * Create a new #CamelFolderSummary object.
247 * Returns a new #CamelFolderSummary object
250 camel_folder_summary_new (struct _CamelFolder *folder)
252 CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ()));
254 new->folder = folder;
261 * camel_folder_summary_set_filename:
262 * @summary: a #CamelFolderSummary object
263 * @filename: a filename
265 * Set the filename where the summary will be loaded to/saved from.
268 camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name)
270 CAMEL_SUMMARY_LOCK(s, summary_lock);
272 g_free(s->summary_path);
273 s->summary_path = g_strdup(name);
275 g_free(s->meta_summary->path);
276 s->meta_summary->path = g_strconcat(name, "-meta", NULL);
278 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
283 * camel_folder_summary_set_index:
284 * @summary: a #CamelFolderSummary object
285 * @index: a #CamelIndex
287 * Set the index used to index body content. If the index is %NULL, or
288 * not set (the default), no indexing of body content will take place.
290 * Unlike earlier behaviour, build_content need not be set to perform indexing.
293 camel_folder_summary_set_index(CamelFolderSummary *s, CamelIndex *index)
295 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
298 camel_object_unref((CamelObject *)p->index);
302 camel_object_ref((CamelObject *)index);
307 * camel_folder_summary_set_build_content:
308 * @summary: a #CamelFolderSummary object
309 * @state: to build or not to build the content
311 * Set a flag to tell the summary to build the content info summary
312 * (#CamelMessageInfo.content). The default is not to build content
316 camel_folder_summary_set_build_content(CamelFolderSummary *s, gboolean state)
318 s->build_content = state;
323 * camel_folder_summary_count:
324 * @summary: a #CamelFolderSummary object
326 * Get the number of summary items stored in this summary.
328 * Returns the number of items in the summary
331 camel_folder_summary_count(CamelFolderSummary *s)
333 return s->messages->len;
338 * camel_folder_summary_index:
339 * @summary: a #CamelFolderSummary object
342 * Retrieve a summary item by index number.
344 * A referenced to the summary item is returned, which may be
345 * ref'd or free'd as appropriate.
347 * Returns the summary item, or %NULL if @index is out of range
350 camel_folder_summary_index(CamelFolderSummary *s, int i)
352 CamelMessageInfo *info = NULL;
354 CAMEL_SUMMARY_LOCK(s, summary_lock);
355 CAMEL_SUMMARY_LOCK(s, ref_lock);
357 if (i<s->messages->len)
358 info = g_ptr_array_index(s->messages, i);
363 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
364 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
371 * camel_folder_summary_array:
372 * @summary: a #CamelFolderSummary object
374 * Obtain a copy of the summary array. This is done atomically,
375 * so cannot contain empty entries.
377 * It must be freed using #camel_folder_summary_array_free.
379 * Returns a #GPtrArray of #CamelMessageInfo items
382 camel_folder_summary_array(CamelFolderSummary *s)
384 CamelMessageInfo *info;
385 GPtrArray *res = g_ptr_array_new();
388 CAMEL_SUMMARY_LOCK(s, summary_lock);
389 CAMEL_SUMMARY_LOCK(s, ref_lock);
391 g_ptr_array_set_size(res, s->messages->len);
392 for (i=0;i<s->messages->len;i++) {
393 info = res->pdata[i] = g_ptr_array_index(s->messages, i);
397 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
398 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
405 * camel_folder_summary_array_free:
406 * @summary: a #CamelFolderSummary object
407 * @array: array of #CamelMessageInfo items as returned from #camel_folder_summary_array
409 * Free the folder summary array.
412 camel_folder_summary_array_free(CamelFolderSummary *s, GPtrArray *array)
416 /* FIXME: do the locking around the whole lot to make it faster */
417 for (i=0;i<array->len;i++)
418 camel_message_info_free(array->pdata[i]);
420 g_ptr_array_free(array, TRUE);
425 * camel_folder_summary_uid:
426 * @summary: a #CamelFolderSummary object
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 * Returns the summary item, or %NULL if the uid @uid is not available
437 camel_folder_summary_uid(CamelFolderSummary *s, const char *uid)
439 CamelMessageInfo *info;
441 CAMEL_SUMMARY_LOCK(s, summary_lock);
442 CAMEL_SUMMARY_LOCK(s, ref_lock);
444 info = g_hash_table_lookup(s->messages_uid, uid);
449 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
450 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
457 * camel_folder_summary_next_uid:
458 * @summary: a #CamelFolderSummary object
460 * Generate a new unique uid value as an integer. This
461 * may be used to create a unique sequence of numbers.
463 * Returns the next unique uid value
466 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);*/
484 * camel_folder_summary_set_uid:
485 * @summary: a #CamelFolderSummary object
486 * @uid: The next minimum uid to assign. To avoid clashing
487 * uid's, set this to the uid of a given messages + 1.
489 * Set the next minimum uid available. This can be used to
490 * ensure new uid's do not clash with existing uid's.
493 camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid)
495 /* TODO: sync to disk? */
496 CAMEL_SUMMARY_LOCK(s, summary_lock);
498 s->nextuid = MAX(s->nextuid, uid);
500 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
505 * camel_folder_summary_next_uid_string:
506 * @summary: a #CamelFolderSummary object
508 * Retrieve the next uid, but as a formatted string.
510 * Returns the next uid as an unsigned integer string.
511 * This string must be freed by the caller.
514 camel_folder_summary_next_uid_string(CamelFolderSummary *s)
516 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->next_uid_string(s);
519 /* loads the content descriptions, recursively */
520 static CamelMessageContentInfo *
521 perform_content_info_load(CamelFolderSummary *s, FILE *in)
525 CamelMessageContentInfo *ci, *part;
527 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_load(s, in);
531 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500) {
532 camel_folder_summary_content_info_free(s, ci);
536 for (i=0;i<count;i++) {
537 part = perform_content_info_load(s, in);
539 my_list_append((struct _node **)&ci->childs, (struct _node *)part);
542 d(fprintf (stderr, "Summary file format messed up?"));
543 camel_folder_summary_content_info_free(s, ci);
552 * camel_folder_summary_load:
553 * @summary: a #CamelFolderSummary object
555 * Load the summary from disk.
557 * Returns %0 on success or %-1 on fail
560 camel_folder_summary_load(CamelFolderSummary *s)
564 CamelMessageInfo *mi;
566 if (s->summary_path == NULL ||
567 s->meta_summary->path == NULL)
570 in = g_fopen(s->summary_path, "rb");
574 CAMEL_SUMMARY_LOCK(s, io_lock);
575 if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
578 /* now read in each message ... */
579 for (i=0;i<s->saved_count;i++) {
580 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_load(s, in);
585 /* FIXME: this should be done differently, how i don't know */
586 if (s->build_content) {
587 ((CamelMessageInfoBase *)mi)->content = perform_content_info_load(s, in);
588 if (((CamelMessageInfoBase *)mi)->content == NULL) {
589 camel_message_info_free(mi);
594 camel_folder_summary_add(s, mi);
597 CAMEL_SUMMARY_UNLOCK(s, io_lock);
599 if (fclose (in) != 0)
602 s->flags &= ~CAMEL_SUMMARY_DIRTY;
608 g_warning ("Cannot load summary file: `%s': %s", s->summary_path, g_strerror (errno));
610 CAMEL_SUMMARY_UNLOCK(s, io_lock);
612 s->flags |= ~CAMEL_SUMMARY_DIRTY;
617 /* saves the content descriptions, recursively */
619 perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
621 CamelMessageContentInfo *part;
623 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->content_info_save (s, out, ci) == -1)
626 if (camel_file_util_encode_uint32 (out, my_list_size ((struct _node **)&ci->childs)) == -1)
631 if (perform_content_info_save (s, out, part) == -1)
641 * camel_folder_summary_save:
642 * @summary: a #CamelFolderSummary object
644 * Writes the summary to disk. The summary is only written if changes
647 * Returns %0 on success or %-1 on fail
650 camel_folder_summary_save(CamelFolderSummary *s)
656 CamelMessageInfo *mi;
660 g_assert(s->message_info_size >= sizeof(CamelMessageInfoBase));
662 if (s->summary_path == NULL
663 || s->meta_summary->path == NULL
664 || (s->flags & CAMEL_SUMMARY_DIRTY) == 0)
667 path = alloca(strlen(s->summary_path)+4);
668 sprintf(path, "%s~", s->summary_path);
669 fd = g_open(path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
672 out = fdopen(fd, "wb");
681 /* Meta summary code */
682 /* This meta summary will be used by beagle in order to
683 quickly pass through the actual summary file, which
684 is quite time consuming otherwise.
686 /* FIXME: Merge meta-summary and summary */
687 path_meta = alloca(strlen(s->meta_summary->path)+4);
688 sprintf(path_meta, "%s~", s->meta_summary->path);
689 fd_meta = g_open(path_meta, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
694 out_meta = fdopen(fd_meta, "wb");
695 if (out_meta == NULL) {
705 io(printf("saving header\n"));
707 CAMEL_SUMMARY_LOCK(s, io_lock);
709 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1)
712 if (summary_meta_header_save(s, out_meta) == -1)
715 /* now write out each message ... */
716 /* we check ferorr when done for i/o errors */
717 count = s->messages->len;
718 for (i = 0; i < count; i++) {
719 mi = s->messages->pdata[i];
720 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->meta_message_info_save (s, out_meta, out, mi) == -1)
723 if (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (s)))->message_info_save (s, out, mi) == -1)
726 if (s->build_content) {
727 if (perform_content_info_save (s, out, ((CamelMessageInfoBase *)mi)->content) == -1)
732 /* FIXME: Can't we use the above "fd" variables, instead of fileno()? */
733 if (fflush (out) != 0 || fsync (fileno (out)) == -1)
736 if (fflush (out_meta) != 0 || fsync (fileno (out_meta)) == -1)
742 CAMEL_SUMMARY_UNLOCK(s, io_lock);
745 g_unlink(s->summary_path);
747 if (g_rename(path, s->summary_path) == -1) {
754 if (g_rename(path_meta, s->meta_summary->path) == -1) {
761 s->flags &= ~CAMEL_SUMMARY_DIRTY;
771 CAMEL_SUMMARY_UNLOCK(s, io_lock);
774 g_unlink (path_meta);
782 * camel_folder_summary_header_load:
783 * @summary: a #CamelFolderSummary object
785 * Only load the header information from the summary,
786 * keep the rest on disk. This should only be done on
787 * a fresh summary object.
789 * Returns %0 on success or %-1 on fail
792 camel_folder_summary_header_load(CamelFolderSummary *s)
798 if (s->summary_path == NULL ||
799 s->meta_summary->path == NULL)
802 in = g_fopen(s->summary_path, "rb");
806 in_meta = g_fopen(s->meta_summary->path, "rb");
807 if (in_meta == NULL) {
812 CAMEL_SUMMARY_LOCK(s, io_lock);
813 ret = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in);
814 ret = summary_meta_header_load(s, in_meta);
815 CAMEL_SUMMARY_UNLOCK(s, io_lock);
819 s->flags &= ~CAMEL_SUMMARY_DIRTY;
824 summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info)
827 CamelMessageInfo *mi;
829 uid = camel_message_info_uid(info);
830 if (uid == NULL || uid[0] == 0) {
832 uid = info->uid = camel_folder_summary_next_uid_string(s);
835 CAMEL_SUMMARY_LOCK(s, summary_lock);
837 while ((mi = g_hash_table_lookup(s->messages_uid, uid))) {
838 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
841 d(printf ("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid(info)));
843 uid = info->uid = camel_folder_summary_next_uid_string(s);
844 camel_message_info_set_flags(info, CAMEL_MESSAGE_FOLDER_FLAGGED, CAMEL_MESSAGE_FOLDER_FLAGGED);
845 CAMEL_SUMMARY_LOCK(s, summary_lock);
848 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
855 * camel_folder_summary_add:
856 * @summary: a #CamelFolderSummary object
857 * @info: a #CamelMessageInfo
859 * Adds a new @info record to the summary. If @info->uid is %NULL,
860 * then a new uid is automatically re-assigned by calling
861 * #camel_folder_summary_next_uid_string.
863 * The @info record should have been generated by calling one of the
864 * info_new_*() functions, as it will be free'd based on the summary
865 * class. And MUST NOT be allocated directly using malloc.
868 camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info)
873 if (summary_assign_uid(s, info) == 0)
876 CAMEL_SUMMARY_LOCK(s, summary_lock);
878 /* unnecessary for pooled vectors */
880 /* this is vitally important, and also if this is ever modified, then
881 the hash table needs to be resynced */
882 info->strings = e_strv_pack(info->strings);
885 g_ptr_array_add(s->messages, info);
886 g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info);
887 s->flags |= CAMEL_SUMMARY_DIRTY;
889 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
894 * camel_folder_summary_add_from_header:
895 * @summary: a #CamelFolderSummary object
896 * @headers: rfc822 headers
898 * Build a new info record based on a set of headers, and add it to
901 * Note that this function should not be used if build_content_info
902 * has been specified for this summary.
904 * Returns the newly added record
907 camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
909 CamelMessageInfo *info = camel_folder_summary_info_new_from_header(s, h);
911 camel_folder_summary_add(s, info);
918 * camel_folder_summary_add_from_parser:
919 * @summary: a #CamelFolderSummary object
920 * @parser: a #CamelMimeParser object
922 * Build a new info record based on the current position of a #CamelMimeParser.
924 * The parser should be positioned before the start of the message to summarise.
925 * This function may be used if build_contnet_info or an index has been
926 * specified for the summary.
928 * Returns the newly added record
931 camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
933 CamelMessageInfo *info;
935 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (s), NULL);
936 g_return_val_if_fail (CAMEL_IS_MIME_PARSER (mp), NULL);
938 info = camel_folder_summary_info_new_from_parser(s, mp);
940 camel_folder_summary_add(s, info);
947 * camel_folder_summary_add_from_message:
948 * @summary: a #CamelFolderSummary object
949 * @message: a #CamelMimeMessage object
951 * Add a summary item from an existing message.
953 * Returns the newly added record
956 camel_folder_summary_add_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
958 CamelMessageInfo *info = camel_folder_summary_info_new_from_message(s, msg);
960 camel_folder_summary_add(s, info);
967 * camel_folder_summary_info_new_from_header:
968 * @summary: a #CamelFolderSummary object
969 * @headers: rfc822 headers
971 * Create a new info record from a header.
973 * Returns the newly allocated record which must be freed with
974 * #camel_message_info_free
977 camel_folder_summary_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
979 return ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, h);
984 * camel_folder_summary_info_new_from_parser:
985 * @summary: a #CamelFolderSummary object
986 * @parser: a #CamelMimeParser object
988 * Create a new info record from a parser. If the parser cannot
989 * determine a uid, then none will be assigned.
991 * If indexing is enabled, and the parser cannot determine a new uid, then
992 * one is automatically assigned.
994 * If indexing is enabled, then the content will be indexed based
995 * on this new uid. In this case, the message info MUST be
996 * added using :add().
998 * Once complete, the parser will be positioned at the end of
1001 * Returns the newly allocated record which must be freed with
1002 * #camel_message_info_free
1005 camel_folder_summary_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1007 CamelMessageInfo *info = NULL;
1010 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
1012 CamelIndexName *name = NULL;
1014 /* should this check the parser is in the right state, or assume it is?? */
1016 start = camel_mime_parser_tell(mp);
1017 if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_EOF) {
1018 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_parser(s, mp);
1020 camel_mime_parser_unstep(mp);
1022 /* assign a unique uid, this is slightly 'wrong' as we do not really
1023 * know if we are going to store this in the summary, but no matter */
1025 summary_assign_uid(s, info);
1027 CAMEL_SUMMARY_LOCK(s, filter_lock);
1030 if (p->filter_index == NULL)
1031 p->filter_index = camel_mime_filter_index_new_index(p->index);
1032 camel_index_delete_name(p->index, camel_message_info_uid(info));
1033 name = camel_index_add_name(p->index, camel_message_info_uid(info));
1034 camel_mime_filter_index_set_name(p->filter_index, name);
1037 /* always scan the content info, even if we dont save it */
1038 ((CamelMessageInfoBase *)info)->content = summary_build_content_info(s, info, mp);
1040 if (name && p->index) {
1041 camel_index_write_name(p->index, name);
1042 camel_object_unref((CamelObject *)name);
1043 camel_mime_filter_index_set_name(p->filter_index, NULL);
1046 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1048 ((CamelMessageInfoBase *)info)->size = camel_mime_parser_tell(mp) - start;
1055 * camel_folder_summary_info_new_from_message:
1056 * @summary: a #CamelFodlerSummary object
1057 * @message: a #CamelMimeMessage object
1059 * Create a summary item from a message.
1061 * Returns the newly allocated record which must be freed using
1062 * #camel_message_info_free
1065 camel_folder_summary_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1067 CamelMessageInfo *info;
1068 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
1069 CamelIndexName *name = NULL;
1071 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_message(s, msg);
1073 /* assign a unique uid, this is slightly 'wrong' as we do not really
1074 * know if we are going to store this in the summary, but we need it set for indexing */
1076 summary_assign_uid(s, info);
1078 CAMEL_SUMMARY_LOCK(s, filter_lock);
1081 if (p->filter_index == NULL)
1082 p->filter_index = camel_mime_filter_index_new_index(p->index);
1083 camel_index_delete_name(p->index, camel_message_info_uid(info));
1084 name = camel_index_add_name(p->index, camel_message_info_uid(info));
1085 camel_mime_filter_index_set_name(p->filter_index, name);
1087 if (p->filter_stream == NULL) {
1088 CamelStream *null = camel_stream_null_new();
1090 p->filter_stream = camel_stream_filter_new_with_stream(null);
1091 camel_object_unref((CamelObject *)null);
1095 ((CamelMessageInfoBase *)info)->content = summary_build_content_info_message(s, info, (CamelMimePart *)msg);
1098 camel_index_write_name(p->index, name);
1099 camel_object_unref((CamelObject *)name);
1100 camel_mime_filter_index_set_name(p->filter_index, NULL);
1103 CAMEL_SUMMARY_UNLOCK(s, filter_lock);
1110 * camel_folder_summary_content_info_free:
1111 * @summary: a #CamelFolderSummary object
1112 * @ci: a #CamelMessageContentInfo
1114 * Free the content info @ci, and all associated memory.
1117 camel_folder_summary_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
1119 CamelMessageContentInfo *pw, *pn;
1122 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_free(s, ci);
1125 camel_folder_summary_content_info_free(s, pw);
1132 * camel_folder_summary_touch:
1133 * @summary: a #CamelFolderSummary object
1135 * Mark the summary as changed, so that a save will force it to be
1136 * written back to disk.
1139 camel_folder_summary_touch(CamelFolderSummary *s)
1141 CAMEL_SUMMARY_LOCK(s, summary_lock);
1142 s->flags |= CAMEL_SUMMARY_DIRTY;
1143 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1148 * camel_folder_summary_clear:
1149 * @summary: a #CamelFolderSummary object
1151 * Empty the summary contents.
1154 camel_folder_summary_clear(CamelFolderSummary *s)
1158 CAMEL_SUMMARY_LOCK(s, summary_lock);
1159 if (camel_folder_summary_count(s) == 0) {
1160 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1164 for (i=0;i<s->messages->len;i++)
1165 camel_message_info_free(s->messages->pdata[i]);
1167 g_ptr_array_set_size(s->messages, 0);
1168 g_hash_table_destroy(s->messages_uid);
1169 s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
1170 s->flags |= CAMEL_SUMMARY_DIRTY;
1171 s->meta_summary->msg_expunged = TRUE;
1172 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1177 * camel_folder_summary_remove:
1178 * @summary: a #CamelFolderSummary object
1179 * @info: a #CamelMessageInfo
1181 * Remove a specific @info record from the summary.
1184 camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info)
1186 CAMEL_SUMMARY_LOCK(s, summary_lock);
1187 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1188 g_ptr_array_remove(s->messages, info);
1189 s->flags |= CAMEL_SUMMARY_DIRTY;
1190 s->meta_summary->msg_expunged = TRUE;
1191 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1193 camel_message_info_free(info);
1198 * camel_folder_summary_remove_uid:
1199 * @summary: a #CamelFolderSummary object
1202 * Remove a specific info record from the summary, by @uid.
1205 camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid)
1207 CamelMessageInfo *oldinfo;
1210 CAMEL_SUMMARY_LOCK(s, summary_lock);
1211 CAMEL_SUMMARY_LOCK(s, ref_lock);
1212 if (g_hash_table_lookup_extended(s->messages_uid, uid, (void *)&olduid, (void *)&oldinfo)) {
1213 /* make sure it doesn't vanish while we're removing it */
1214 oldinfo->refcount++;
1215 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1216 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1217 camel_folder_summary_remove(s, oldinfo);
1218 camel_message_info_free(oldinfo);
1220 CAMEL_SUMMARY_UNLOCK(s, ref_lock);
1221 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1227 * camel_folder_summary_remove_index:
1228 * @summary: a #CamelFolderSummary object
1229 * @index: record index
1231 * Remove a specific info record from the summary, by index.
1234 camel_folder_summary_remove_index(CamelFolderSummary *s, int index)
1236 CAMEL_SUMMARY_LOCK(s, summary_lock);
1237 if (index < s->messages->len) {
1238 CamelMessageInfo *info = s->messages->pdata[index];
1240 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1241 g_ptr_array_remove_index(s->messages, index);
1242 s->flags |= CAMEL_SUMMARY_DIRTY;
1244 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1245 camel_message_info_free(info);
1247 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1253 * camel_folder_summary_remove_range:
1254 * @summary: a #CamelFolderSummary object
1255 * @start: initial index
1256 * @end: last index to remove
1258 * Removes an indexed range of info records.
1261 camel_folder_summary_remove_range(CamelFolderSummary *s, int start, int end)
1266 CAMEL_SUMMARY_LOCK(s, summary_lock);
1267 if (start < s->messages->len) {
1268 CamelMessageInfo **infos;
1271 end = MIN(end+1, s->messages->len);
1272 infos = g_malloc((end-start)*sizeof(infos[0]));
1274 for (i=start;i<end;i++) {
1275 CamelMessageInfo *info = s->messages->pdata[i];
1277 infos[i-start] = info;
1278 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
1281 memmove(s->messages->pdata+start, s->messages->pdata+end, (s->messages->len-end)*sizeof(s->messages->pdata[0]));
1282 g_ptr_array_set_size(s->messages, s->messages->len - (end - start));
1283 s->flags |= CAMEL_SUMMARY_DIRTY;
1285 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1287 for (i=start;i<end;i++)
1288 camel_message_info_free(infos[i-start]);
1291 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
1295 /* should be sorted, for binary search */
1296 /* This is a tokenisation mechanism for strings written to the
1297 summary - to save space.
1298 This list can have at most 31 words. */
1299 static char * tokens[] = {
1324 "us-ascii", /* 25 words */
1327 #define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
1331 1-tokens_len == tokens[id-1]
1332 >=32 string, length = n-32
1337 token_search_cmp(char *key, char **index)
1339 d(printf("comparing '%s' to '%s'\n", key, *index));
1340 return strcmp(key, *index);
1345 * camel_folder_summary_encode_token:
1346 * @out: output FILE pointer
1347 * @str: string token to encode
1349 * Encode a string value, but use tokenisation and compression
1350 * to reduce the size taken for common mailer words. This
1351 * can still be used to encode normal strings as well.
1353 * Returns %0 on success or %-1 on fail
1356 camel_folder_summary_encode_token(FILE *out, const char *str)
1358 io(printf("Encoding token: '%s'\n", str));
1361 return camel_file_util_encode_uint32(out, 0);
1363 int len = strlen(str);
1371 lower[i] = tolower(str[i]);
1374 match = bsearch(lower, tokens, tokens_len, sizeof(char *), (int (*)(const void *, const void *))token_search_cmp);
1376 token = match-tokens;
1378 for (i=0;i<tokens_len;i++) {
1379 if (!strcmp(tokens[i], lower)) {
1387 return camel_file_util_encode_uint32(out, token+1);
1389 if (camel_file_util_encode_uint32(out, len+32) == -1)
1391 if (fwrite(str, len, 1, out) != 1)
1400 * camel_folder_summary_decode_token:
1401 * @in: input FILE pointer
1402 * @str: string pointer to hold the decoded result
1404 * Decode a token value.
1406 * Returns %0 on success or %-1 on fail
1409 camel_folder_summary_decode_token(FILE *in, char **str)
1414 io(printf("Decode token ...\n"));
1416 if (camel_file_util_decode_uint32(in, &len) == -1) {
1417 io(printf ("Could not decode token from file"));
1425 } else if (len<= tokens_len) {
1426 ret = g_strdup(tokens[len-1]);
1428 io(printf ("Invalid token encountered: %d", len));
1432 } else if (len > 10240) {
1433 io(printf ("Got broken string header length: %d bytes", len));
1438 ret = g_malloc(len+1);
1439 if (len > 0 && fread(ret, len, 1, in) != 1) {
1447 io(printf("Token = '%s'\n", ret));
1453 static struct _node *
1454 my_list_append(struct _node **list, struct _node *n)
1456 struct _node *ln = (struct _node *)list;
1465 my_list_size(struct _node **list)
1468 struct _node *ln = (struct _node *)list;
1477 summary_meta_header_load(CamelFolderSummary *s, FILE *in)
1479 if (!s->meta_summary->path)
1482 fseek(in, 0, SEEK_SET);
1484 io(printf("Loading meta-header\n"));
1486 if (camel_file_util_decode_uint32(in, &s->meta_summary->major) == -1
1487 || camel_file_util_decode_uint32(in, &s->meta_summary->minor) == -1
1488 || camel_file_util_decode_uint32(in, &s->meta_summary->uid_len) == -1) {
1496 summary_header_load(CamelFolderSummary *s, FILE *in)
1498 if (!s->summary_path)
1501 fseek(in, 0, SEEK_SET);
1503 io(printf("Loading header\n"));
1505 if (camel_file_util_decode_fixed_int32(in, &s->version) == -1)
1508 /* Legacy version check, before version 12 we have no upgrade knowledge */
1509 if ((s->version > 0xff) && (s->version & 0xff) < 12) {
1510 io(printf ("Summary header version mismatch"));
1515 if (!(s->version < 0x100 && s->version >= 13))
1516 io(printf("Loading legacy summary\n"));
1518 io(printf("loading new-format summary\n"));
1520 /* legacy version */
1521 if (camel_file_util_decode_fixed_int32(in, &s->flags) == -1
1522 || camel_file_util_decode_fixed_int32(in, &s->nextuid) == -1
1523 || camel_file_util_decode_time_t(in, &s->time) == -1
1524 || camel_file_util_decode_fixed_int32(in, &s->saved_count) == -1) {
1529 if (s->version < 0x100 && s->version >= 13
1530 && (camel_file_util_decode_fixed_int32(in, &s->unread_count) == -1
1531 || camel_file_util_decode_fixed_int32(in, &s->deleted_count) == -1
1532 || camel_file_util_decode_fixed_int32(in, &s->junk_count) == -1)) {
1540 summary_header_save(CamelFolderSummary *s, FILE *out)
1542 int unread = 0, deleted = 0, junk = 0, count, i;
1544 fseek(out, 0, SEEK_SET);
1546 io(printf("Savining header\n"));
1548 /* we always write out the current version */
1549 camel_file_util_encode_fixed_int32(out, CAMEL_FOLDER_SUMMARY_VERSION);
1550 camel_file_util_encode_fixed_int32(out, s->flags);
1551 camel_file_util_encode_fixed_int32(out, s->nextuid);
1552 camel_file_util_encode_time_t(out, s->time);
1554 count = camel_folder_summary_count(s);
1555 for (i=0; i<count; i++) {
1556 CamelMessageInfo *info = camel_folder_summary_index(s, i);
1562 flags = camel_message_info_flags(info);
1563 if ((flags & CAMEL_MESSAGE_SEEN) == 0)
1565 if ((flags & CAMEL_MESSAGE_DELETED) != 0)
1567 if ((flags & CAMEL_MESSAGE_JUNK) != 0)
1570 camel_message_info_free(info);
1573 camel_file_util_encode_fixed_int32(out, count);
1574 camel_file_util_encode_fixed_int32(out, unread);
1575 camel_file_util_encode_fixed_int32(out, deleted);
1577 return camel_file_util_encode_fixed_int32(out, junk);
1581 summary_meta_header_save(CamelFolderSummary *s, FILE *out_meta)
1583 fseek(out_meta, 0, SEEK_SET);
1585 /* Save meta-summary header */
1586 if (s->meta_summary->msg_expunged) {
1587 s->meta_summary->msg_expunged = FALSE;
1588 camel_file_util_encode_uint32(out_meta, ++s->meta_summary->major);
1589 camel_file_util_encode_uint32(out_meta, (s->meta_summary->minor=0));
1591 camel_file_util_encode_uint32(out_meta, s->meta_summary->major);
1592 camel_file_util_encode_uint32(out_meta, ++s->meta_summary->minor);
1594 camel_file_util_encode_uint32(out_meta, s->meta_summary->uid_len);
1596 return ferror(out_meta);
1599 /* are these even useful for anything??? */
1600 static CamelMessageInfo *
1601 message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1603 CamelMessageInfo *mi = NULL;
1606 state = camel_mime_parser_state(mp);
1608 case CAMEL_MIME_PARSER_STATE_HEADER:
1609 case CAMEL_MIME_PARSER_STATE_MESSAGE:
1610 case CAMEL_MIME_PARSER_STATE_MULTIPART:
1611 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, camel_mime_parser_headers_raw(mp));
1614 g_error("Invalid parser state");
1620 static CamelMessageContentInfo *
1621 content_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
1623 CamelMessageContentInfo *ci = NULL;
1625 switch (camel_mime_parser_state(mp)) {
1626 case CAMEL_MIME_PARSER_STATE_HEADER:
1627 case CAMEL_MIME_PARSER_STATE_MESSAGE:
1628 case CAMEL_MIME_PARSER_STATE_MULTIPART:
1629 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_header(s, camel_mime_parser_headers_raw(mp));
1631 ci->type = camel_mime_parser_content_type(mp);
1632 camel_content_type_ref(ci->type);
1636 g_error("Invalid parser state");
1642 static CamelMessageInfo *
1643 message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
1645 CamelMessageInfo *mi;
1647 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_header(s, ((CamelMimePart *)msg)->headers);
1652 static CamelMessageContentInfo *
1653 content_info_new_from_message(CamelFolderSummary *s, CamelMimePart *mp)
1655 CamelMessageContentInfo *ci;
1657 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_header(s, mp->headers);
1663 summary_format_address(struct _camel_header_raw *h, const char *name, const char *charset)
1665 struct _camel_header_address *addr;
1669 text = camel_header_raw_find (&h, name, NULL);
1670 addr = camel_header_address_decode (text, charset);
1672 ret = camel_header_address_list_format (addr);
1673 camel_header_address_list_clear (&addr);
1675 ret = g_strdup (text);
1682 summary_format_string (struct _camel_header_raw *h, const char *name, const char *charset)
1686 text = camel_header_raw_find (&h, name, NULL);
1688 while (isspace ((unsigned) *text))
1690 return camel_header_decode_string (text, charset);
1698 * camel_folder_summary_content_info_new:
1699 * @summary: a #CamelFolderSummary object
1701 * Allocate a new #CamelMessageContentInfo, suitable for adding
1704 * Returns a newly allocated #CamelMessageContentInfo
1706 CamelMessageContentInfo *
1707 camel_folder_summary_content_info_new(CamelFolderSummary *s)
1709 CamelMessageContentInfo *ci;
1711 CAMEL_SUMMARY_LOCK(s, alloc_lock);
1712 if (s->content_info_chunks == NULL)
1713 s->content_info_chunks = e_memchunk_new(32, s->content_info_size);
1714 ci = e_memchunk_alloc(s->content_info_chunks);
1715 CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
1717 memset(ci, 0, s->content_info_size);
1721 static CamelMessageInfo *
1722 message_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
1724 CamelMessageInfoBase *mi;
1725 const char *received;
1727 struct _camel_header_references *refs, *irt, *scan;
1730 char *subject, *from, *to, *cc, *mlist;
1731 CamelContentType *ct = NULL;
1732 const char *content, *charset = NULL;
1734 mi = (CamelMessageInfoBase *)camel_message_info_new(s);
1736 if ((content = camel_header_raw_find(&h, "Content-Type", NULL))
1737 && (ct = camel_content_type_decode(content))
1738 && (charset = camel_content_type_param(ct, "charset"))
1739 && (g_ascii_strcasecmp(charset, "us-ascii") == 0))
1742 charset = charset ? e_iconv_charset_name (charset) : NULL;
1744 subject = summary_format_string(h, "subject", charset);
1745 from = summary_format_address(h, "from", charset);
1746 to = summary_format_address(h, "to", charset);
1747 cc = summary_format_address(h, "cc", charset);
1748 mlist = camel_header_raw_check_mailing_list(&h);
1751 camel_content_type_unref(ct);
1753 mi->subject = camel_pstring_add (subject, TRUE);
1754 mi->from = camel_pstring_add (from, TRUE);
1755 mi->to = camel_pstring_add (to, TRUE);
1756 mi->cc = camel_pstring_add (cc, TRUE);
1757 mi->mlist = camel_pstring_add (mlist, TRUE);
1759 mi->user_flags = NULL;
1760 mi->user_tags = NULL;
1761 mi->date_sent = camel_header_decode_date(camel_header_raw_find(&h, "date", NULL), NULL);
1762 received = camel_header_raw_find(&h, "received", NULL);
1764 received = strrchr(received, ';');
1766 mi->date_received = camel_header_decode_date(received + 1, NULL);
1768 mi->date_received = 0;
1770 msgid = camel_header_msgid_decode(camel_header_raw_find(&h, "message-id", NULL));
1772 md5_get_digest(msgid, strlen(msgid), digest);
1773 memcpy(mi->message_id.id.hash, digest, sizeof(mi->message_id.id.hash));
1777 /* decode our references and in-reply-to headers */
1778 refs = camel_header_references_decode (camel_header_raw_find (&h, "references", NULL));
1779 irt = camel_header_references_inreplyto_decode (camel_header_raw_find (&h, "in-reply-to", NULL));
1782 /* The References field is populated from the ``References'' and/or ``In-Reply-To''
1783 headers. If both headers exist, take the first thing in the In-Reply-To header
1784 that looks like a Message-ID, and append it to the References header. */
1792 count = camel_header_references_list_size(&refs);
1793 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1797 md5_get_digest(scan->id, strlen(scan->id), digest);
1798 memcpy(mi->references->references[count].id.hash, digest, sizeof(mi->message_id.id.hash));
1802 mi->references->size = count;
1803 camel_header_references_list_clear(&refs);
1806 return (CamelMessageInfo *)mi;
1809 static CamelMessageInfo *
1810 message_info_load(CamelFolderSummary *s, FILE *in)
1812 CamelMessageInfoBase *mi;
1815 char *subject, *from, *to, *cc, *mlist, *uid;
1817 mi = (CamelMessageInfoBase *)camel_message_info_new(s);
1819 io(printf("Loading message info\n"));
1821 camel_file_util_decode_string(in, &uid);
1822 camel_file_util_decode_uint32(in, &mi->flags);
1823 camel_file_util_decode_uint32(in, &mi->size);
1824 camel_file_util_decode_time_t(in, &mi->date_sent);
1825 camel_file_util_decode_time_t(in, &mi->date_received);
1826 camel_file_util_decode_string(in, &subject);
1827 camel_file_util_decode_string(in, &from);
1828 camel_file_util_decode_string(in, &to);
1829 camel_file_util_decode_string(in, &cc);
1830 camel_file_util_decode_string(in, &mlist);
1833 mi->subject = camel_pstring_add (subject, TRUE);
1834 mi->from = camel_pstring_add (from, TRUE);
1835 mi->to = camel_pstring_add (to, TRUE);
1836 mi->cc = camel_pstring_add (cc, TRUE);
1837 mi->mlist = camel_pstring_add (mlist, TRUE);
1841 camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.hi);
1842 camel_file_util_decode_fixed_int32(in, &mi->message_id.id.part.lo);
1844 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1848 mi->references = g_malloc(sizeof(*mi->references) + ((count-1) * sizeof(mi->references->references[0])));
1849 mi->references->size = count;
1850 for (i=0;i<count;i++) {
1851 camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.hi);
1852 camel_file_util_decode_fixed_int32(in, &mi->references->references[i].id.part.lo);
1856 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1859 for (i=0;i<count;i++) {
1861 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL)
1863 camel_flag_set(&mi->user_flags, name, TRUE);
1867 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
1870 for (i=0;i<count;i++) {
1872 if (camel_file_util_decode_string(in, &name) == -1 || name == NULL
1873 || camel_file_util_decode_string(in, &value) == -1)
1875 camel_tag_set(&mi->user_tags, name, value);
1881 return (CamelMessageInfo *)mi;
1884 camel_message_info_free((CamelMessageInfo *)mi);
1890 meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *info)
1894 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1897 offset = ftell (out);
1898 /* FIXME: errno check after ftell */
1900 camel_file_util_encode_time_t(out_meta, timestamp);
1901 camel_file_util_encode_fixed_string(out_meta, camel_message_info_uid(mi), s->meta_summary->uid_len);
1902 camel_file_util_encode_uint32(out_meta, mi->flags);
1903 camel_file_util_encode_off_t(out_meta, offset);
1909 message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
1915 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1917 io(printf("Saving message info\n"));
1919 camel_file_util_encode_string(out, camel_message_info_uid(mi));
1920 camel_file_util_encode_uint32(out, mi->flags);
1921 camel_file_util_encode_uint32(out, mi->size);
1922 camel_file_util_encode_time_t(out, mi->date_sent);
1923 camel_file_util_encode_time_t(out, mi->date_received);
1924 camel_file_util_encode_string(out, camel_message_info_subject(mi));
1925 camel_file_util_encode_string(out, camel_message_info_from(mi));
1926 camel_file_util_encode_string(out, camel_message_info_to(mi));
1927 camel_file_util_encode_string(out, camel_message_info_cc(mi));
1928 camel_file_util_encode_string(out, camel_message_info_mlist(mi));
1930 camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.hi);
1931 camel_file_util_encode_fixed_int32(out, mi->message_id.id.part.lo);
1933 if (mi->references) {
1934 camel_file_util_encode_uint32(out, mi->references->size);
1935 for (i=0;i<mi->references->size;i++) {
1936 camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.hi);
1937 camel_file_util_encode_fixed_int32(out, mi->references->references[i].id.part.lo);
1940 camel_file_util_encode_uint32(out, 0);
1943 count = camel_flag_list_size(&mi->user_flags);
1944 camel_file_util_encode_uint32(out, count);
1945 flag = mi->user_flags;
1947 camel_file_util_encode_string(out, flag->name);
1951 count = camel_tag_list_size(&mi->user_tags);
1952 camel_file_util_encode_uint32(out, count);
1953 tag = mi->user_tags;
1955 camel_file_util_encode_string(out, tag->name);
1956 camel_file_util_encode_string(out, tag->value);
1964 message_info_free(CamelFolderSummary *s, CamelMessageInfo *info)
1966 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
1969 camel_pstring_free(mi->subject);
1970 camel_pstring_free(mi->from);
1971 camel_pstring_free(mi->to);
1972 camel_pstring_free(mi->cc);
1973 camel_pstring_free(mi->mlist);
1974 g_free(mi->references);
1975 camel_flag_list_free(&mi->user_flags);
1976 camel_tag_list_free(&mi->user_tags);
1978 e_memchunk_free(s->message_info_chunks, mi);
1983 static CamelMessageContentInfo *
1984 content_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
1986 CamelMessageContentInfo *ci;
1987 const char *charset;
1989 ci = camel_folder_summary_content_info_new (s);
1991 charset = e_iconv_locale_charset ();
1992 ci->id = camel_header_msgid_decode (camel_header_raw_find (&h, "content-id", NULL));
1993 ci->description = camel_header_decode_string (camel_header_raw_find (&h, "content-description", NULL), charset);
1994 ci->encoding = camel_content_transfer_encoding_decode (camel_header_raw_find (&h, "content-transfer-encoding", NULL));
1995 ci->type = camel_content_type_decode(camel_header_raw_find(&h, "content-type", NULL));
2000 static CamelMessageContentInfo *
2001 content_info_load(CamelFolderSummary *s, FILE *in)
2003 CamelMessageContentInfo *ci;
2004 char *type, *subtype;
2006 CamelContentType *ct;
2008 io(printf("Loading content info\n"));
2010 ci = camel_folder_summary_content_info_new(s);
2012 camel_folder_summary_decode_token(in, &type);
2013 camel_folder_summary_decode_token(in, &subtype);
2014 ct = camel_content_type_new(type, subtype);
2015 g_free(type); /* can this be removed? */
2017 if (camel_file_util_decode_uint32(in, &count) == -1 || count > 500)
2020 for (i=0;i<count;i++) {
2022 camel_folder_summary_decode_token(in, &name);
2023 camel_folder_summary_decode_token(in, &value);
2024 if (!(name && value))
2027 camel_content_type_set_param(ct, name, value);
2028 /* TODO: do this so we dont have to double alloc/free */
2034 camel_folder_summary_decode_token(in, &ci->id);
2035 camel_folder_summary_decode_token(in, &ci->description);
2036 camel_folder_summary_decode_token(in, &ci->encoding);
2038 camel_file_util_decode_uint32(in, &ci->size);
2046 camel_folder_summary_content_info_free(s, ci);
2051 content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
2053 CamelContentType *ct;
2054 struct _camel_header_param *hp;
2056 io(printf("Saving content info\n"));
2060 camel_folder_summary_encode_token(out, ct->type);
2061 camel_folder_summary_encode_token(out, ct->subtype);
2062 camel_file_util_encode_uint32(out, my_list_size((struct _node **)&ct->params));
2065 camel_folder_summary_encode_token(out, hp->name);
2066 camel_folder_summary_encode_token(out, hp->value);
2070 camel_folder_summary_encode_token(out, NULL);
2071 camel_folder_summary_encode_token(out, NULL);
2072 camel_file_util_encode_uint32(out, 0);
2074 camel_folder_summary_encode_token(out, ci->id);
2075 camel_folder_summary_encode_token(out, ci->description);
2076 camel_folder_summary_encode_token(out, ci->encoding);
2077 return camel_file_util_encode_uint32(out, ci->size);
2081 content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
2083 camel_content_type_unref(ci->type);
2085 g_free(ci->description);
2086 g_free(ci->encoding);
2087 e_memchunk_free(s->content_info_chunks, ci);
2091 next_uid_string(CamelFolderSummary *s)
2093 return g_strdup_printf("%u", camel_folder_summary_next_uid(s));
2098 Now this is where all the "smarts" happen, where the content info is built,
2099 and any indexing and what not is performed
2102 /* must have filter_lock before calling this function */
2103 static CamelMessageContentInfo *
2104 summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp)
2109 CamelMessageContentInfo *info = NULL;
2110 CamelContentType *ct;
2111 int enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
2112 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2113 CamelMimeFilterCharset *mfc;
2114 CamelMessageContentInfo *part;
2116 d(printf("building content info\n"));
2118 /* start of this part */
2119 state = camel_mime_parser_step(mp, &buffer, &len);
2121 if (s->build_content)
2122 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_parser(s, mp);
2125 case CAMEL_MIME_PARSER_STATE_HEADER:
2126 /* check content type for indexing, then read body */
2127 ct = camel_mime_parser_content_type(mp);
2128 /* update attachments flag as we go */
2129 if (camel_content_type_is(ct, "application", "pgp-signature")
2131 || camel_content_type_is(ct, "application", "x-pkcs7-signature")
2132 || camel_content_type_is(ct, "application", "pkcs7-signature")
2135 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2137 if (p->index && camel_content_type_is(ct, "text", "*")) {
2139 const char *charset;
2141 d(printf("generating index:\n"));
2143 encoding = camel_content_transfer_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
2145 if (!g_ascii_strcasecmp(encoding, "base64")) {
2146 d(printf(" decoding base64\n"));
2147 if (p->filter_64 == NULL)
2148 p->filter_64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
2150 camel_mime_filter_reset((CamelMimeFilter *)p->filter_64);
2151 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_64);
2152 } else if (!g_ascii_strcasecmp(encoding, "quoted-printable")) {
2153 d(printf(" decoding quoted-printable\n"));
2154 if (p->filter_qp == NULL)
2155 p->filter_qp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC);
2157 camel_mime_filter_reset((CamelMimeFilter *)p->filter_qp);
2158 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_qp);
2159 } else if (!g_ascii_strcasecmp (encoding, "x-uuencode")) {
2160 d(printf(" decoding x-uuencode\n"));
2161 if (p->filter_uu == NULL)
2162 p->filter_uu = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_UU_DEC);
2164 camel_mime_filter_reset((CamelMimeFilter *)p->filter_uu);
2165 enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_uu);
2167 d(printf(" ignoring encoding %s\n", encoding));
2172 charset = camel_content_type_param(ct, "charset");
2174 && !(g_ascii_strcasecmp(charset, "us-ascii")==0
2175 || g_ascii_strcasecmp(charset, "utf-8")==0)) {
2176 d(printf(" Adding conversion filter from %s to UTF-8\n", charset));
2177 mfc = g_hash_table_lookup(p->filter_charset, charset);
2179 mfc = camel_mime_filter_charset_new_convert(charset, "UTF-8");
2181 g_hash_table_insert(p->filter_charset, g_strdup(charset), mfc);
2183 camel_mime_filter_reset((CamelMimeFilter *)mfc);
2186 chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc);
2188 w(g_warning("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset));
2192 /* we do charset conversions before this filter, which isn't strictly correct,
2193 but works in most cases */
2194 if (camel_content_type_is(ct, "text", "html")) {
2195 if (p->filter_html == NULL)
2196 p->filter_html = camel_mime_filter_html_new();
2198 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2199 html_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_html);
2202 /* and this filter actually does the indexing */
2203 idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_index);
2205 /* and scan/index everything */
2206 while (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
2208 /* and remove the filters */
2209 camel_mime_parser_filter_remove(mp, enc_id);
2210 camel_mime_parser_filter_remove(mp, chr_id);
2211 camel_mime_parser_filter_remove(mp, html_id);
2212 camel_mime_parser_filter_remove(mp, idx_id);
2214 case CAMEL_MIME_PARSER_STATE_MULTIPART:
2215 d(printf("Summarising multipart\n"));
2216 /* update attachments flag as we go */
2217 ct = camel_mime_parser_content_type(mp);
2218 if (camel_content_type_is(ct, "multipart", "mixed"))
2219 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2220 if (camel_content_type_is(ct, "multipart", "signed")
2221 || camel_content_type_is(ct, "multipart", "encrypted"))
2222 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2224 while (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
2225 camel_mime_parser_unstep(mp);
2226 part = summary_build_content_info(s, msginfo, mp);
2228 part->parent = info;
2229 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2233 case CAMEL_MIME_PARSER_STATE_MESSAGE:
2234 d(printf("Summarising message\n"));
2235 /* update attachments flag as we go */
2236 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2238 part = summary_build_content_info(s, msginfo, mp);
2240 part->parent = info;
2241 my_list_append((struct _node **)&info->childs, (struct _node *)part);
2243 state = camel_mime_parser_step(mp, &buffer, &len);
2244 if (state != CAMEL_MIME_PARSER_STATE_MESSAGE_END) {
2245 g_error("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
2246 camel_mime_parser_unstep(mp);
2251 d(printf("finished building content info\n"));
2256 /* build the content-info, from a message */
2257 /* this needs the filter lock since it uses filters to perform indexing */
2258 static CamelMessageContentInfo *
2259 summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object)
2261 CamelDataWrapper *containee;
2263 struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
2264 CamelMessageContentInfo *info = NULL, *child;
2265 CamelContentType *ct;
2267 if (s->build_content)
2268 info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_message(s, object);
2270 containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));
2272 if (containee == NULL)
2275 /* TODO: I find it odd that get_part and get_content_object do not
2276 add a reference, probably need fixing for multithreading */
2278 /* check for attachments */
2279 ct = ((CamelDataWrapper *)containee)->mime_type;
2280 if (camel_content_type_is(ct, "multipart", "*")) {
2281 if (camel_content_type_is(ct, "multipart", "mixed"))
2282 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
2283 if (camel_content_type_is(ct, "multipart", "signed")
2284 || camel_content_type_is(ct, "multipart", "encrypted"))
2285 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2286 } else if (camel_content_type_is(ct, "application", "pgp-signature")
2288 || camel_content_type_is(ct, "application", "x-pkcs7-signature")
2289 || camel_content_type_is(ct, "application", "pkcs7-signature")
2292 camel_message_info_set_flags(msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
2295 /* using the object types is more accurate than using the mime/types */
2296 if (CAMEL_IS_MULTIPART(containee)) {
2297 parts = camel_multipart_get_number(CAMEL_MULTIPART(containee));
2298 for (i=0;i<parts;i++) {
2299 CamelMimePart *part = camel_multipart_get_part(CAMEL_MULTIPART(containee), i);
2301 child = summary_build_content_info_message(s, msginfo, part);
2303 child->parent = info;
2304 my_list_append((struct _node **)&info->childs, (struct _node *)child);
2307 } else if (CAMEL_IS_MIME_MESSAGE(containee)) {
2308 /* for messages we only look at its contents */
2309 child = summary_build_content_info_message(s, msginfo, (CamelMimePart *)containee);
2311 child->parent = info;
2312 my_list_append((struct _node **)&info->childs, (struct _node *)child);
2314 } else if (p->filter_stream
2315 && camel_content_type_is(ct, "text", "*")) {
2316 int html_id = -1, idx_id = -1;
2318 /* pre-attach html filter if required, otherwise just index filter */
2319 if (camel_content_type_is(ct, "text", "html")) {
2320 if (p->filter_html == NULL)
2321 p->filter_html = camel_mime_filter_html_new();
2323 camel_mime_filter_reset((CamelMimeFilter *)p->filter_html);
2324 html_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_html);
2326 idx_id = camel_stream_filter_add(p->filter_stream, (CamelMimeFilter *)p->filter_index);
2328 camel_data_wrapper_decode_to_stream(containee, (CamelStream *)p->filter_stream);
2329 camel_stream_flush((CamelStream *)p->filter_stream);
2331 camel_stream_filter_remove(p->filter_stream, idx_id);
2332 camel_stream_filter_remove(p->filter_stream, html_id);
2340 * @list: the address of a #CamelFlag list
2341 * @name: name of the flag to get
2343 * Find the state of the flag @name in @list.
2345 * Returns the state of the flag (%TRUE or %FALSE)
2348 camel_flag_get(CamelFlag **list, const char *name)
2353 if (!strcmp(flag->name, name))
2363 * @list: the address of a #CamelFlag list
2364 * @name: name of the flag to set or change
2365 * @value: the value to set on the flag
2367 * Set the state of a flag @name in the list @list to @value.
2369 * Returns %TRUE if the value of the flag has been changed or %FALSE
2373 camel_flag_set(CamelFlag **list, const char *name, gboolean value)
2375 CamelFlag *flag, *tmp;
2377 /* this 'trick' works because flag->next is the first element */
2378 flag = (CamelFlag *)list;
2379 while (flag->next) {
2381 if (!strcmp(flag->next->name, name)) {
2383 flag->next = tmp->next;
2392 tmp = g_malloc(sizeof(*tmp) + strlen(name));
2393 strcpy(tmp->name, name);
2402 * camel_flag_list_size:
2403 * @list: the address of a #CamelFlag list
2405 * Get the length of the flag list.
2407 * Returns the number of flags in the list
2410 camel_flag_list_size(CamelFlag **list)
2425 * camel_flag_list_free:
2426 * @list: the address of a #CamelFlag list
2428 * Free the memory associated with the flag list @list.
2431 camel_flag_list_free(CamelFlag **list)
2433 CamelFlag *flag, *tmp;
2445 * camel_flag_list_copy:
2446 * @to: the address of the #CamelFlag list to copy to
2447 * @from: the address of the #CamelFlag list to copy from
2451 * Returns %TRUE if @to is changed or %FALSE otherwise
2454 camel_flag_list_copy(CamelFlag **to, CamelFlag **from)
2456 CamelFlag *flag, *tmp;
2457 int changed = FALSE;
2459 if (*to == NULL && from == NULL)
2462 /* Remove any now-missing flags */
2463 flag = (CamelFlag *)to;
2464 while (flag->next) {
2466 if (!camel_flag_get(from, tmp->name)) {
2467 flag->next = tmp->next;
2475 /* Add any new flags */
2478 changed |= camel_flag_set(to, flag->name, TRUE);
2488 * @list: the address of a #CamelTag list
2489 * @name: name of the tag to get
2491 * Find the flag @name in @list and get the value.
2493 * Returns the value of the flag or %NULL if unset
2496 camel_tag_get(CamelTag **list, const char *name)
2502 if (!strcmp(tag->name, name))
2503 return (const char *)tag->value;
2512 * @list: the address of a #CamelTag list
2513 * @name: name of the tag to set
2514 * @value: value to set on the tag
2516 * Set the tag @name in the tag list @list to @value.
2518 * Returns %TRUE if the value on the tag changed or %FALSE otherwise
2521 camel_tag_set(CamelTag **list, const char *name, const char *value)
2523 CamelTag *tag, *tmp;
2525 /* this 'trick' works because tag->next is the first element */
2526 tag = (CamelTag *)list;
2529 if (!strcmp(tmp->name, name)) {
2530 if (value == NULL) { /* clear it? */
2531 tag->next = tmp->next;
2535 } else if (strcmp(tmp->value, value)) { /* has it changed? */
2537 tmp->value = g_strdup(value);
2546 tmp = g_malloc(sizeof(*tmp)+strlen(name));
2547 strcpy(tmp->name, name);
2548 tmp->value = g_strdup(value);
2558 * camel_tag_list_size:
2559 * @list: the address of a #CamelTag list
2561 * Get the number of tags present in the tag list @list.
2563 * Returns the number of tags
2566 camel_tag_list_size(CamelTag **list)
2580 rem_tag(char *key, char *value, CamelTag **to)
2582 camel_tag_set(to, key, NULL);
2587 * camel_tag_list_copy:
2588 * @to: the address of the #CamelTag list to copy to
2589 * @from: the address of the #CamelTag list to copy from
2593 * Returns %TRUE if @to is changed or %FALSE otherwise
2596 camel_tag_list_copy(CamelTag **to, CamelTag **from)
2598 int changed = FALSE;
2602 if (*to == NULL && from == NULL)
2605 left = g_hash_table_new(g_str_hash, g_str_equal);
2608 g_hash_table_insert(left, tag->name, tag);
2614 changed |= camel_tag_set(to, tag->name, tag->value);
2615 g_hash_table_remove(left, tag->name);
2619 if (g_hash_table_size(left)>0) {
2620 g_hash_table_foreach(left, (GHFunc)rem_tag, to);
2623 g_hash_table_destroy(left);
2630 * camel_tag_list_free:
2631 * @list: the address of a #CamelTag list
2633 * Free the tag list @list.
2636 camel_tag_list_free(CamelTag **list)
2638 CamelTag *tag, *tmp;
2649 static struct flag_names_t {
2653 { "answered", CAMEL_MESSAGE_ANSWERED },
2654 { "deleted", CAMEL_MESSAGE_DELETED },
2655 { "draft", CAMEL_MESSAGE_DELETED },
2656 { "flagged", CAMEL_MESSAGE_FLAGGED },
2657 { "seen", CAMEL_MESSAGE_SEEN },
2658 { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
2659 { "junk", CAMEL_MESSAGE_JUNK },
2660 { "secure", CAMEL_MESSAGE_SECURE },
2666 * camel_system_flag:
2667 * @name: name of a system flag
2669 * Returns the integer value of the system flag string
2672 camel_system_flag (const char *name)
2674 struct flag_names_t *flag;
2676 g_return_val_if_fail (name != NULL, 0);
2678 for (flag = flag_names; *flag->name; flag++)
2679 if (!g_ascii_strcasecmp (name, flag->name))
2687 * camel_system_flag_get:
2688 * @flags: bitwise system flags
2689 * @name: name of the flag to check for
2691 * Find the state of the flag @name in @flags.
2693 * Returns %TRUE if the named flag is set or %FALSE otherwise
2696 camel_system_flag_get (guint32 flags, const char *name)
2698 g_return_val_if_fail (name != NULL, FALSE);
2700 return flags & camel_system_flag (name);
2705 * camel_message_info_new:
2706 * @summary: a #CamelFolderSummary object or %NULL
2708 * Create a new #CamelMessageInfo.
2710 * Returns a new #CamelMessageInfo
2713 camel_message_info_new (CamelFolderSummary *s)
2715 CamelMessageInfo *info;
2718 CAMEL_SUMMARY_LOCK(s, alloc_lock);
2719 if (s->message_info_chunks == NULL)
2720 s->message_info_chunks = e_memchunk_new(32, s->message_info_size);
2721 info = e_memchunk_alloc0(s->message_info_chunks);
2722 CAMEL_SUMMARY_UNLOCK(s, alloc_lock);
2724 info = g_malloc0(sizeof(CamelMessageInfoBase));
2735 * camel_message_info_ref:
2736 * @info: a #CamelMessageInfo
2738 * Reference an info.
2741 camel_message_info_ref(void *o)
2743 CamelMessageInfo *mi = o;
2746 CAMEL_SUMMARY_LOCK(mi->summary, ref_lock);
2747 g_assert(mi->refcount >= 1);
2749 CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2751 GLOBAL_INFO_LOCK(info);
2752 g_assert(mi->refcount >= 1);
2754 GLOBAL_INFO_UNLOCK(info);
2760 * camel_message_info_new_from_header:
2761 * @summary: a #CamelFolderSummary object or %NULL
2762 * @header: raw header
2764 * Create a new #CamelMessageInfo pre-populated with info from
2767 * Returns a new #CamelMessageInfo
2770 camel_message_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *header)
2773 return ((CamelFolderSummaryClass *)((CamelObject *)s)->klass)->message_info_new_from_header(s, header);
2775 return message_info_new_from_header(NULL, header);
2780 * camel_message_info_free:
2781 * @info: a #CamelMessageInfo
2783 * Unref's and potentially frees a #CamelMessageInfo and its contents.
2786 camel_message_info_free(void *o)
2788 CamelMessageInfo *mi = o;
2790 g_return_if_fail(mi != NULL);
2793 CAMEL_SUMMARY_LOCK(mi->summary, ref_lock);
2795 if (mi->refcount >= 1)
2797 if (mi->refcount > 0) {
2798 CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2802 CAMEL_SUMMARY_UNLOCK(mi->summary, ref_lock);
2804 /* FIXME: this is kinda busted, should really be handled by message info free */
2805 if (mi->summary->build_content
2806 && ((CamelMessageInfoBase *)mi)->content) {
2807 camel_folder_summary_content_info_free(mi->summary, ((CamelMessageInfoBase *)mi)->content);
2810 ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(mi->summary)))->message_info_free(mi->summary, mi);
2812 GLOBAL_INFO_LOCK(info);
2814 if (mi->refcount > 0) {
2815 GLOBAL_INFO_UNLOCK(info);
2818 GLOBAL_INFO_UNLOCK(info);
2820 message_info_free(NULL, mi);
2824 static CamelMessageInfo *
2825 message_info_clone(CamelFolderSummary *s, const CamelMessageInfo *mi)
2827 CamelMessageInfoBase *to, *from = (CamelMessageInfoBase *)mi;
2831 to = (CamelMessageInfoBase *)camel_message_info_new(s);
2833 to->flags = from->flags;
2834 to->size = from->size;
2835 to->date_sent = from->date_sent;
2836 to->date_received = from->date_received;
2839 /* NB: We don't clone the uid */
2841 to->subject = camel_pstring_strdup(from->subject);
2842 to->from = camel_pstring_strdup(from->from);
2843 to->to = camel_pstring_strdup(from->to);
2844 to->cc = camel_pstring_strdup(from->cc);
2845 to->mlist = camel_pstring_strdup(from->mlist);
2846 memcpy(&to->message_id, &from->message_id, sizeof(to->message_id));
2848 if (from->references) {
2849 int len = sizeof(*from->references) + ((from->references->size-1) * sizeof(from->references->references[0]));
2851 to->references = g_malloc(len);
2852 memcpy(to->references, from->references, len);
2855 flag = from->user_flags;
2857 camel_flag_set(&to->user_flags, flag->name, TRUE);
2861 tag = from->user_tags;
2863 camel_tag_set(&to->user_tags, tag->name, tag->value);
2867 if (from->content) {
2868 /* FIXME: copy content-infos */
2871 return (CamelMessageInfo *)to;
2876 * camel_message_info_clone:
2877 * @info: a #CamelMessageInfo
2879 * Duplicate a #CamelMessageInfo.
2881 * Returns the duplicated #CamelMessageInfo
2884 camel_message_info_clone(const void *o)
2886 const CamelMessageInfo *mi = o;
2889 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->message_info_clone(mi->summary, mi);
2891 return message_info_clone(NULL, mi);
2895 info_ptr(const CamelMessageInfo *mi, int id)
2898 case CAMEL_MESSAGE_INFO_SUBJECT:
2899 return ((const CamelMessageInfoBase *)mi)->subject;
2900 case CAMEL_MESSAGE_INFO_FROM:
2901 return ((const CamelMessageInfoBase *)mi)->from;
2902 case CAMEL_MESSAGE_INFO_TO:
2903 return ((const CamelMessageInfoBase *)mi)->to;
2904 case CAMEL_MESSAGE_INFO_CC:
2905 return ((const CamelMessageInfoBase *)mi)->cc;
2906 case CAMEL_MESSAGE_INFO_MLIST:
2907 return ((const CamelMessageInfoBase *)mi)->mlist;
2908 case CAMEL_MESSAGE_INFO_MESSAGE_ID:
2909 return &((const CamelMessageInfoBase *)mi)->message_id;
2910 case CAMEL_MESSAGE_INFO_REFERENCES:
2911 return ((const CamelMessageInfoBase *)mi)->references;
2912 case CAMEL_MESSAGE_INFO_USER_FLAGS:
2913 return ((const CamelMessageInfoBase *)mi)->user_flags;
2914 case CAMEL_MESSAGE_INFO_USER_TAGS:
2915 return ((const CamelMessageInfoBase *)mi)->user_tags;
2922 info_uint32(const CamelMessageInfo *mi, int id)
2925 case CAMEL_MESSAGE_INFO_FLAGS:
2926 return ((const CamelMessageInfoBase *)mi)->flags;
2927 case CAMEL_MESSAGE_INFO_SIZE:
2928 return ((const CamelMessageInfoBase *)mi)->size;
2935 info_time(const CamelMessageInfo *mi, int id)
2938 case CAMEL_MESSAGE_INFO_DATE_SENT:
2939 return ((const CamelMessageInfoBase *)mi)->date_sent;
2940 case CAMEL_MESSAGE_INFO_DATE_RECEIVED:
2941 return ((const CamelMessageInfoBase *)mi)->date_received;
2948 info_user_flag(const CamelMessageInfo *mi, const char *id)
2950 return camel_flag_get(&((CamelMessageInfoBase *)mi)->user_flags, id);
2954 info_user_tag(const CamelMessageInfo *mi, const char *id)
2956 return camel_tag_get(&((CamelMessageInfoBase *)mi)->user_tags, id);
2961 * camel_message_info_ptr:
2962 * @mi: a #CamelMessageInfo
2965 * Generic accessor method for getting pointer data.
2967 * Returns the pointer data
2970 camel_message_info_ptr(const CamelMessageInfo *mi, int id)
2973 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_ptr(mi, id);
2975 return info_ptr(mi, id);
2980 * camel_message_info_uint32:
2981 * @mi: a #CamelMessageInfo
2984 * Generic accessor method for getting 32bit int data.
2986 * Returns the int data
2989 camel_message_info_uint32(const CamelMessageInfo *mi, int id)
2992 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_uint32(mi, id);
2994 return info_uint32(mi, id);
2999 * camel_message_info_time:
3000 * @mi: a #CamelMessageInfo
3003 * Generic accessor method for getting time_t data.
3005 * Returns the time_t data
3008 camel_message_info_time(const CamelMessageInfo *mi, int id)
3011 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_time(mi, id);
3013 return info_time(mi, id);
3018 * camel_message_info_user_flag:
3019 * @mi: a #CamelMessageInfo
3020 * @id: user flag to get
3022 * Get the state of a user flag named @id.
3024 * Returns the state of the user flag
3027 camel_message_info_user_flag(const CamelMessageInfo *mi, const char *id)
3030 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_user_flag(mi, id);
3032 return info_user_flag(mi, id);
3037 * camel_message_info_user_tag:
3038 * @mi: a #CamelMessageInfo
3039 * @id: user tag to get
3041 * Get the value of a user tag named @id.
3043 * Returns the value of the user tag
3046 camel_message_info_user_tag(const CamelMessageInfo *mi, const char *id)
3049 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_user_tag(mi, id);
3051 return info_user_tag(mi, id);
3055 info_set_flags(CamelMessageInfo *info, guint32 flags, guint32 set)
3058 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3060 /* TODO: locking? */
3063 mi->flags = (old & ~flags) | (set & flags);
3064 if (old != mi->flags) {
3065 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3067 camel_folder_summary_touch(mi->summary);
3070 if (((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK)))
3073 if (mi->summary && mi->summary->folder && mi->uid) {
3074 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3076 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3077 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3078 camel_folder_change_info_free(changes);
3086 * camel_message_info_set_flags:
3087 * @mi: a #CamelMessageInfo
3088 * @flags: mask of flags to change
3089 * @set: state the flags should be changed to
3091 * Change the state of the system flags on the #CamelMessageInfo
3093 * Returns %TRUE if any of the flags changed or %FALSE otherwise
3096 camel_message_info_set_flags(CamelMessageInfo *mi, guint32 flags, guint32 set)
3099 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_flags(mi, flags, set);
3101 return info_set_flags(mi, flags, set);
3105 info_set_user_flag(CamelMessageInfo *info, const char *name, gboolean value)
3107 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3110 res = camel_flag_set(&mi->user_flags, name, value);
3112 /* TODO: check this item is still in the summary first */
3113 if (mi->summary && res && mi->summary->folder && mi->uid) {
3114 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3116 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3117 camel_folder_summary_touch(mi->summary);
3118 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3119 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3120 camel_folder_change_info_free(changes);
3128 * camel_message_info_set_user_flag:
3129 * @mi: a #CamelMessageInfo
3130 * @id: name of the user flag to set
3131 * @state: state to set the flag to
3133 * Set the state of a user flag on a #CamelMessageInfo.
3135 * Returns %TRUE if the state changed or %FALSE otherwise
3138 camel_message_info_set_user_flag(CamelMessageInfo *mi, const char *id, gboolean state)
3141 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_user_flag(mi, id, state);
3143 return info_set_user_flag(mi, id, state);
3147 info_set_user_tag(CamelMessageInfo *info, const char *name, const char *value)
3149 CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;
3152 res = camel_tag_set(&mi->user_tags, name, value);
3154 if (mi->summary && res && mi->summary->folder && mi->uid) {
3155 CamelFolderChangeInfo *changes = camel_folder_change_info_new();
3157 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3158 camel_folder_summary_touch(mi->summary);
3159 camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
3160 camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
3161 camel_folder_change_info_free(changes);
3169 * camel_message_info_set_user_tag:
3170 * @mi: a #CamelMessageInfo
3171 * @id: name of the user tag to set
3172 * @val: value to set
3174 * Set the value of a user tag on a #CamelMessageInfo.
3176 * Returns %TRUE if the value changed or %FALSE otherwise
3179 camel_message_info_set_user_tag(CamelMessageInfo *mi, const char *id, const char *val)
3182 return ((CamelFolderSummaryClass *)((CamelObject *)mi->summary)->klass)->info_set_user_tag(mi, id, val);
3184 return info_set_user_tag(mi, id, val);
3188 camel_content_info_dump (CamelMessageContentInfo *ci, int depth)
3192 p = alloca (depth * 4 + 1);
3193 memset (p, ' ', depth * 4);
3197 printf ("%s<empty>\n", p);
3202 printf ("%scontent-type: %s/%s\n", p, ci->type->type ? ci->type->type : "(null)",
3203 ci->type->subtype ? ci->type->subtype : "(null)");
3205 printf ("%scontent-type: <unset>\n", p);
3206 printf ("%scontent-transfer-encoding: %s\n", p, ci->encoding ? ci->encoding : "(null)");
3207 printf ("%scontent-description: %s\n", p, ci->description ? ci->description : "(null)");
3208 printf ("%ssize: %lu\n", p, (unsigned long) ci->size);
3211 camel_content_info_dump (ci, depth + 1);
3217 camel_message_info_dump (CamelMessageInfo *mi)
3220 printf("No message?\n");
3224 printf("Subject: %s\n", camel_message_info_subject(mi));
3225 printf("To: %s\n", camel_message_info_to(mi));
3226 printf("Cc: %s\n", camel_message_info_cc(mi));
3227 printf("mailing list: %s\n", camel_message_info_mlist(mi));
3228 printf("From: %s\n", camel_message_info_from(mi));
3229 printf("UID: %s\n", camel_message_info_uid(mi));
3230 printf("Flags: %04x\n", camel_message_info_flags(mi));
3231 /*camel_content_info_dump(mi->content, 0);*/
3236 camel_folder_summary_class_init (CamelFolderSummaryClass *klass)
3238 camel_folder_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
3240 klass->summary_header_load = summary_header_load;
3241 klass->summary_header_save = summary_header_save;
3243 klass->message_info_new_from_header = message_info_new_from_header;
3244 klass->message_info_new_from_parser = message_info_new_from_parser;
3245 klass->message_info_new_from_message = message_info_new_from_message;
3246 klass->message_info_load = message_info_load;
3247 klass->message_info_save = message_info_save;
3248 klass->meta_message_info_save = meta_message_info_save;
3249 klass->message_info_free = message_info_free;
3250 klass->message_info_clone = message_info_clone;
3252 klass->content_info_new_from_header = content_info_new_from_header;
3253 klass->content_info_new_from_parser = content_info_new_from_parser;
3254 klass->content_info_new_from_message = content_info_new_from_message;
3255 klass->content_info_load = content_info_load;
3256 klass->content_info_save = content_info_save;
3257 klass->content_info_free = content_info_free;
3259 klass->next_uid_string = next_uid_string;
3261 klass->info_ptr = info_ptr;
3262 klass->info_uint32 = info_uint32;
3263 klass->info_time = info_time;
3264 klass->info_user_flag = info_user_flag;
3265 klass->info_user_tag = info_user_tag;
3268 klass->info_set_string = info_set_string;
3269 klass->info_set_uint32 = info_set_uint32;
3270 klass->info_set_time = info_set_time;
3271 klass->info_set_ptr = info_set_ptr;
3273 klass->info_set_user_flag = info_set_user_flag;
3274 klass->info_set_user_tag = info_set_user_tag;
3276 klass->info_set_flags = info_set_flags;