1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * This library is free software you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, see <http://www.gnu.org/licenses/>.
27 #include <sys/types.h>
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
37 #include "camel-debug.h"
38 #include "camel-file-utils.h"
39 #include "camel-folder-summary.h"
40 #include "camel-folder.h"
41 #include "camel-iconv.h"
42 #include "camel-mime-filter-basic.h"
43 #include "camel-mime-filter-charset.h"
44 #include "camel-mime-filter-html.h"
45 #include "camel-mime-filter-index.h"
46 #include "camel-mime-filter.h"
47 #include "camel-mime-message.h"
48 #include "camel-multipart.h"
49 #include "camel-session.h"
50 #include "camel-stream-filter.h"
51 #include "camel-stream-mem.h"
52 #include "camel-stream-null.h"
53 #include "camel-string-utils.h"
54 #include "camel-store.h"
55 #include "camel-vee-folder.h"
56 #include "camel-vtrash-folder.h"
57 #include "camel-mime-part-utils.h"
59 #define CAMEL_FOLDER_SUMMARY_GET_PRIVATE(obj) \
60 (G_TYPE_INSTANCE_GET_PRIVATE \
61 ((obj), CAMEL_TYPE_FOLDER_SUMMARY, CamelFolderSummaryPrivate))
63 /* Make 5 minutes as default cache drop */
64 #define SUMMARY_CACHE_DROP 300
65 #define dd(x) if (camel_debug("sync")) x
67 struct _CamelFolderSummaryPrivate {
68 GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */
70 struct _CamelMimeFilter *filter_index;
71 struct _CamelMimeFilter *filter_64;
72 struct _CamelMimeFilter *filter_qp;
73 struct _CamelMimeFilter *filter_uu;
74 struct _CamelMimeFilter *filter_save;
75 struct _CamelMimeFilter *filter_html;
77 struct _CamelStream *filter_stream;
79 struct _CamelIndex *index;
81 GRecMutex summary_lock; /* for the summary hashtable/array */
82 GRecMutex filter_lock; /* for accessing any of the filtering/indexing stuff, since we share them */
84 gboolean need_preview;
85 GHashTable *preview_updates;
87 guint32 nextuid; /* next uid? */
88 guint32 saved_count; /* how many were saved/loaded */
89 guint32 unread_count; /* handy totals */
90 guint32 deleted_count;
92 guint32 junk_not_deleted_count;
93 guint32 visible_count;
95 gboolean build_content; /* do we try and parse/index the content, or not? */
97 GHashTable *uids; /* uids of all known message infos; the 'value' are used flags for the message info */
98 GHashTable *loaded_infos; /* uid->CamelMessageInfo *, those currently in memory */
100 struct _CamelFolder *folder; /* parent folder, for events */
101 time_t cache_load_time;
102 guint timeout_handle;
105 /* this should probably be conditional on it existing */
109 #define io(x) /* io debug */
112 #define CAMEL_FOLDER_SUMMARY_VERSION (14)
114 /* trivial lists, just because ... */
119 static void cfs_schedule_info_release_timer (CamelFolderSummary *summary);
121 static struct _node *my_list_append (struct _node **list, struct _node *n);
122 static gint my_list_size (struct _node **list);
124 static CamelMessageInfo * message_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
125 static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
126 static CamelMessageInfo * message_info_new_from_message (CamelFolderSummary *summary, CamelMimeMessage *msg, const gchar *bodystructure);
127 static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);
129 static CamelMessageContentInfo * content_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
130 static CamelMessageContentInfo * content_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
131 static CamelMessageContentInfo * content_info_new_from_message (CamelFolderSummary *summary, CamelMimePart *mp);
132 static void content_info_free (CamelFolderSummary *, CamelMessageContentInfo *);
134 static gint save_message_infos_to_db (CamelFolderSummary *summary, GError **error);
135 static gint camel_read_mir_callback (gpointer ref, gint ncol, gchar ** cols, gchar ** name);
137 static gchar *next_uid_string (CamelFolderSummary *summary);
139 static CamelMessageContentInfo * summary_build_content_info (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimeParser *mp);
140 static CamelMessageContentInfo * summary_build_content_info_message (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimePart *object);
142 static CamelMessageInfo * message_info_from_uid (CamelFolderSummary *summary, const gchar *uid);
151 PROP_JUNK_NOT_DELETED_COUNT,
157 G_DEFINE_TYPE (CamelFolderSummary, camel_folder_summary, G_TYPE_OBJECT)
160 remove_each_item (gpointer uid,
164 GSList **to_remove_infos = user_data;
166 *to_remove_infos = g_slist_prepend (*to_remove_infos, mi);
172 remove_all_loaded (CamelFolderSummary *summary)
174 GSList *to_remove_infos = NULL;
176 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
178 camel_folder_summary_lock (summary);
180 g_hash_table_foreach_remove (summary->priv->loaded_infos, remove_each_item, &to_remove_infos);
182 g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_unref, NULL);
183 g_slist_free (to_remove_infos);
185 camel_folder_summary_unlock (summary);
189 free_o_name (gpointer key,
193 g_object_unref (value);
198 folder_summary_dispose (GObject *object)
200 CamelFolderSummaryPrivate *priv;
202 priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (object);
204 if (priv->timeout_handle) {
205 /* this should not happen, because the release timer
206 * holds a reference on object */
207 g_source_remove (priv->timeout_handle);
208 priv->timeout_handle = 0;
211 g_clear_object (&priv->filter_index);
212 g_clear_object (&priv->filter_64);
213 g_clear_object (&priv->filter_qp);
214 g_clear_object (&priv->filter_uu);
215 g_clear_object (&priv->filter_save);
216 g_clear_object (&priv->filter_html);
217 g_clear_object (&priv->filter_stream);
218 g_clear_object (&priv->filter_index);
221 g_object_weak_unref (G_OBJECT (priv->folder), (GWeakNotify) g_nullify_pointer, &priv->folder);
225 /* Chain up to parent's dispose() method. */
226 G_OBJECT_CLASS (camel_folder_summary_parent_class)->dispose (object);
230 folder_summary_finalize (GObject *object)
232 CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (object);
233 CamelFolderSummaryPrivate *priv = summary->priv;
235 g_hash_table_destroy (priv->uids);
236 remove_all_loaded (summary);
237 g_hash_table_destroy (priv->loaded_infos);
239 g_hash_table_foreach (priv->filter_charset, free_o_name, NULL);
240 g_hash_table_destroy (priv->filter_charset);
242 g_hash_table_destroy (priv->preview_updates);
244 g_rec_mutex_clear (&priv->summary_lock);
245 g_rec_mutex_clear (&priv->filter_lock);
247 /* Chain up to parent's finalize() method. */
248 G_OBJECT_CLASS (camel_folder_summary_parent_class)->finalize (object);
252 folder_summary_set_folder (CamelFolderSummary *summary,
255 g_return_if_fail (summary->priv->folder == NULL);
256 /* folder can be NULL in certain cases, see maildir-store */
258 summary->priv->folder = folder;
260 g_object_weak_ref (G_OBJECT (folder), (GWeakNotify) g_nullify_pointer, &summary->priv->folder);
264 folder_summary_set_property (GObject *object,
269 switch (property_id) {
271 folder_summary_set_folder (
272 CAMEL_FOLDER_SUMMARY (object),
273 CAMEL_FOLDER (g_value_get_object (value)));
276 case PROP_BUILD_CONTENT:
277 camel_folder_summary_set_build_content (
278 CAMEL_FOLDER_SUMMARY (object),
279 g_value_get_boolean (value));
282 case PROP_NEED_PREVIEW:
283 camel_folder_summary_set_need_preview (
284 CAMEL_FOLDER_SUMMARY (object),
285 g_value_get_boolean (value));
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
293 folder_summary_get_property (GObject *object,
298 switch (property_id) {
302 camel_folder_summary_get_folder (
303 CAMEL_FOLDER_SUMMARY (object)));
306 case PROP_SAVED_COUNT:
309 camel_folder_summary_get_saved_count (
310 CAMEL_FOLDER_SUMMARY (object)));
313 case PROP_UNREAD_COUNT:
316 camel_folder_summary_get_unread_count (
317 CAMEL_FOLDER_SUMMARY (object)));
320 case PROP_DELETED_COUNT:
323 camel_folder_summary_get_deleted_count (
324 CAMEL_FOLDER_SUMMARY (object)));
327 case PROP_JUNK_COUNT:
330 camel_folder_summary_get_junk_count (
331 CAMEL_FOLDER_SUMMARY (object)));
334 case PROP_JUNK_NOT_DELETED_COUNT:
337 camel_folder_summary_get_junk_not_deleted_count (
338 CAMEL_FOLDER_SUMMARY (object)));
341 case PROP_VISIBLE_COUNT:
344 camel_folder_summary_get_visible_count (
345 CAMEL_FOLDER_SUMMARY (object)));
348 case PROP_BUILD_CONTENT:
349 g_value_set_boolean (
351 camel_folder_summary_get_build_content (
352 CAMEL_FOLDER_SUMMARY (object)));
355 case PROP_NEED_PREVIEW:
356 g_value_set_boolean (
358 camel_folder_summary_get_need_preview (
359 CAMEL_FOLDER_SUMMARY (object)));
363 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
367 is_in_memory_summary (CamelFolderSummary *summary)
369 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
371 return (summary->flags & CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY) != 0;
374 #define UPDATE_COUNTS_ADD (1)
375 #define UPDATE_COUNTS_SUB (2)
376 #define UPDATE_COUNTS_ADD_WITHOUT_TOTAL (3)
377 #define UPDATE_COUNTS_SUB_WITHOUT_TOTAL (4)
380 folder_summary_update_counts_by_flags (CamelFolderSummary *summary,
384 gint unread = 0, deleted = 0, junk = 0;
385 gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
386 gboolean subtract = op_type == UPDATE_COUNTS_SUB || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
387 gboolean without_total = op_type == UPDATE_COUNTS_ADD_WITHOUT_TOTAL || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
388 gboolean changed = FALSE;
389 GObject *summary_object;
391 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
393 summary_object = G_OBJECT (summary);
395 if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
396 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
398 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
399 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
402 if (!(flags & CAMEL_MESSAGE_SEEN))
403 unread = subtract ? -1 : 1;
405 if (flags & CAMEL_MESSAGE_DELETED)
406 deleted = subtract ? -1 : 1;
408 if (flags & CAMEL_MESSAGE_JUNK)
409 junk = subtract ? -1 : 1;
411 dd (printf ("%p: %d %d %d | %d %d %d \n", (gpointer) summary, unread, deleted, junk, summary->priv->unread_count, summary->priv->visible_count, summary->priv->saved_count));
413 g_object_freeze_notify (summary_object);
416 summary->priv->deleted_count += deleted;
417 g_object_notify (summary_object, "deleted-count");
422 summary->priv->junk_count += junk;
423 g_object_notify (summary_object, "junk-count");
427 if (junk && !deleted) {
428 summary->priv->junk_not_deleted_count += junk;
429 g_object_notify (summary_object, "junk-not-deleted-count");
433 if (!junk && !deleted) {
434 summary->priv->visible_count += subtract ? -1 : 1;
435 g_object_notify (summary_object, "visible-count");
439 if (junk && !is_junk_folder)
441 if (deleted && !is_trash_folder)
445 summary->priv->unread_count += unread;
446 g_object_notify (summary_object, "unread-count");
450 if (!without_total) {
451 summary->priv->saved_count += subtract ? -1 : 1;
452 g_object_notify (summary_object, "saved-count");
457 camel_folder_summary_touch (summary);
459 g_object_thaw_notify (summary_object);
461 dd (printf ("%p: %d %d %d | %d %d %d\n", (gpointer) summary, unread, deleted, junk, summary->priv->unread_count, summary->priv->visible_count, summary->priv->saved_count));
467 summary_header_from_db (CamelFolderSummary *summary,
468 CamelFIRecord *record)
470 io (printf ("Loading header from db \n"));
472 summary->version = record->version;
474 /* We may not worry, as we are setting a new standard here */
476 /* Legacy version check, before version 12 we have no upgrade knowledge */
477 if ((summary->version > 0xff) && (summary->version & 0xff) < 12) {
478 io (printf ("Summary header version mismatch"));
483 if (!(summary->version < 0x100 && summary->version >= 13))
484 io (printf ("Loading legacy summary\n"));
486 io (printf ("loading new-format summary\n"));
489 summary->flags = record->flags;
490 summary->priv->nextuid = record->nextuid;
491 summary->time = record->time;
492 summary->priv->saved_count = record->saved_count;
494 summary->priv->unread_count = record->unread_count;
495 summary->priv->deleted_count = record->deleted_count;
496 summary->priv->junk_count = record->junk_count;
497 summary->priv->visible_count = record->visible_count;
498 summary->priv->junk_not_deleted_count = record->jnd_count;
503 static CamelFIRecord *
504 summary_header_to_db (CamelFolderSummary *summary,
507 CamelFIRecord * record = g_new0 (CamelFIRecord, 1);
508 CamelStore *parent_store;
510 const gchar *table_name;
512 /* Though we are going to read, we do this during write,
513 * so lets use it that way. */
514 table_name = camel_folder_get_full_name (summary->priv->folder);
515 parent_store = camel_folder_get_parent_store (summary->priv->folder);
516 db = parent_store->cdb_w;
518 io (printf ("Savining header to db\n"));
520 record->folder_name = g_strdup (table_name);
522 /* we always write out the current version */
523 record->version = CAMEL_FOLDER_SUMMARY_VERSION;
524 record->flags = summary->flags;
525 record->nextuid = summary->priv->nextuid;
526 record->time = summary->time;
528 if (!is_in_memory_summary (summary)) {
529 /* FIXME: Ever heard of Constructors and initializing ? */
530 if (camel_db_count_total_message_info (db, table_name, &(record->saved_count), NULL))
531 record->saved_count = 0;
532 if (camel_db_count_junk_message_info (db, table_name, &(record->junk_count), NULL))
533 record->junk_count = 0;
534 if (camel_db_count_deleted_message_info (db, table_name, &(record->deleted_count), NULL))
535 record->deleted_count = 0;
536 if (camel_db_count_unread_message_info (db, table_name, &(record->unread_count), NULL))
537 record->unread_count = 0;
538 if (camel_db_count_visible_message_info (db, table_name, &(record->visible_count), NULL))
539 record->visible_count = 0;
540 if (camel_db_count_junk_not_deleted_message_info (db, table_name, &(record->jnd_count), NULL))
541 record->jnd_count = 0;
544 summary->priv->unread_count = record->unread_count;
545 summary->priv->deleted_count = record->deleted_count;
546 summary->priv->junk_count = record->junk_count;
547 summary->priv->visible_count = record->visible_count;
548 summary->priv->junk_not_deleted_count = record->jnd_count;
553 static CamelMessageInfo *
554 message_info_from_db (CamelFolderSummary *summary,
555 CamelMIRecord *record)
557 CamelMessageInfoBase *mi;
562 mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
564 io (printf ("Loading message info from db\n"));
566 mi->flags = record->flags;
567 mi->size = record->size;
568 mi->date_sent = record->dsent;
569 mi->date_received = record->dreceived;
571 mi->uid = (gchar *) camel_pstring_strdup (record->uid);
572 mi->subject = (gchar *) camel_pstring_add (record->subject, FALSE);
573 mi->from = (gchar *) camel_pstring_add (record->from, FALSE);
574 mi->to = (gchar *) camel_pstring_add (record->to, FALSE);
575 mi->cc = (gchar *) camel_pstring_add (record->cc, FALSE);
576 mi->mlist = (gchar *) camel_pstring_add (record->mlist, FALSE);
578 /* Evolution itself doesn't yet use this, so we ignore it (saving some memory) */
579 mi->bodystructure = NULL;
581 /* Extract Message id & References */
585 mi->message_id.id.part.hi = bdata_extract_digit (&part);
586 mi->message_id.id.part.lo = bdata_extract_digit (&part);
587 count = bdata_extract_digit (&part);
590 mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
591 mi->references->size = count;
592 for (i = 0; i < count; i++) {
593 mi->references->references[i].id.part.hi = bdata_extract_digit (&part);
594 mi->references->references[i].id.part.lo = bdata_extract_digit (&part);
597 mi->references = NULL;
601 /* Extract User flags/labels */
602 part = record->labels;
605 for (i = 0; part[i]; i++) {
607 if (part[i] == ' ') {
609 camel_flag_set (&mi->user_flags, label, TRUE);
610 label = &(part[i + 1]);
613 camel_flag_set (&mi->user_flags, label, TRUE);
616 /* Extract User tags */
617 part = record->usertags;
618 count = bdata_extract_digit (&part);
619 for (i = 0; i < count; i++) {
622 name = bdata_extract_string (&part);
623 value = bdata_extract_string (&part);
624 camel_tag_set (&mi->user_tags, name, value);
630 return (CamelMessageInfo *) mi;
633 static CamelMIRecord *
634 message_info_to_db (CamelFolderSummary *summary,
635 CamelMessageInfo *info)
637 CamelMIRecord *record = g_new0 (CamelMIRecord, 1);
638 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
644 /* Assume that we dont have to take care of DB Safeness. It will be done while doing the DB transaction */
645 record->uid = (gchar *) camel_pstring_strdup (camel_message_info_uid (mi));
646 record->flags = mi->flags;
648 record->read = ((mi->flags & (CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_JUNK))) ? 1 : 0;
649 record->deleted = mi->flags & CAMEL_MESSAGE_DELETED ? 1 : 0;
650 record->replied = mi->flags & CAMEL_MESSAGE_ANSWERED ? 1 : 0;
651 record->important = mi->flags & CAMEL_MESSAGE_FLAGGED ? 1 : 0;
652 record->junk = mi->flags & CAMEL_MESSAGE_JUNK ? 1 : 0;
653 record->dirty = mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED ? 1 : 0;
654 record->attachment = mi->flags & CAMEL_MESSAGE_ATTACHMENTS ? 1 : 0;
656 record->size = mi->size;
657 record->dsent = mi->date_sent;
658 record->dreceived = mi->date_received;
660 record->subject = (gchar *) camel_pstring_strdup (camel_message_info_subject (mi));
661 record->from = (gchar *) camel_pstring_strdup (camel_message_info_from (mi));
662 record->to = (gchar *) camel_pstring_strdup (camel_message_info_to (mi));
663 record->cc = (gchar *) camel_pstring_strdup (camel_message_info_cc (mi));
664 record->mlist = (gchar *) camel_pstring_strdup (camel_message_info_mlist (mi));
666 record->followup_flag = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "follow-up"));
667 record->followup_completed_on = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "completed-on"));
668 record->followup_due_by = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "due-by"));
670 record->bodystructure = mi->bodystructure ? g_strdup (mi->bodystructure) : NULL;
672 tmp = g_string_new (NULL);
673 if (mi->references) {
674 g_string_append_printf (tmp, "%lu %lu %lu", (gulong) mi->message_id.id.part.hi, (gulong) mi->message_id.id.part.lo, (gulong) mi->references->size);
675 for (i = 0; i < mi->references->size; i++)
676 g_string_append_printf (tmp, " %lu %lu", (gulong) mi->references->references[i].id.part.hi, (gulong) mi->references->references[i].id.part.lo);
678 g_string_append_printf (tmp, "%lu %lu %lu", (gulong) mi->message_id.id.part.hi, (gulong) mi->message_id.id.part.lo, (gulong) 0);
680 record->part = tmp->str;
681 g_string_free (tmp, FALSE);
683 tmp = g_string_new (NULL);
684 flag = mi->user_flags;
686 g_string_append_printf (tmp, "%s ", flag->name);
690 /* Strip off the last space */
694 record->labels = tmp->str;
695 g_string_free (tmp, FALSE);
697 tmp = g_string_new (NULL);
698 count = camel_tag_list_size (&mi->user_tags);
699 g_string_append_printf (tmp, "%lu", (gulong) count);
702 /* FIXME: Should we handle empty tags? Can it be empty? If it potential crasher ahead*/
703 g_string_append_printf (tmp, " %lu-%s %lu-%s", (gulong) strlen (tag->name), tag->name, (gulong) strlen (tag->value), tag->value);
706 record->usertags = tmp->str;
707 g_string_free (tmp, FALSE);
712 static CamelMessageContentInfo *
713 content_info_from_db (CamelFolderSummary *summary,
714 CamelMIRecord *record)
716 CamelMessageContentInfo *ci;
717 gchar *type, *subtype;
719 CamelContentType *ct;
720 gchar *part = record->cinfo;
722 io (printf ("Loading content info from db\n"));
727 ci = camel_folder_summary_content_info_new (summary);
728 if (*part == ' ') part++; /* Move off the space in the record */
730 type = bdata_extract_string (&part);
731 subtype = bdata_extract_string (&part);
732 ct = camel_content_type_new (type, subtype);
733 g_free (type); /* can this be removed? */
735 count = bdata_extract_digit (&part);
737 for (i = 0; i < count; i++) {
739 name = bdata_extract_string (&part);
740 value = bdata_extract_string (&part);
742 camel_content_type_set_param (ct, name, value);
743 /* TODO: do this so we dont have to double alloc/free */
749 /* FIXME[disk-summary] move all these to camel pstring */
750 ci->id = bdata_extract_string (&part);
751 ci->description = bdata_extract_string (&part);
752 ci->encoding = bdata_extract_string (&part);
753 ci->size = bdata_extract_digit (&part);
755 record->cinfo = part; /* Keep moving the cursor in the record */
763 content_info_to_db (CamelFolderSummary *summary,
764 CamelMessageContentInfo *ci,
765 CamelMIRecord *record)
767 CamelContentType *ct;
768 struct _camel_header_param *hp;
769 GString *str = g_string_new (NULL);
772 io (printf ("Saving content info to db\n"));
777 g_string_append_printf (str, " %d-%s", (gint) strlen (ct->type), ct->type);
779 g_string_append_printf (str, " 0-");
781 g_string_append_printf (str, " %d-%s", (gint) strlen (ct->subtype), ct->subtype);
783 g_string_append_printf (str, " 0-");
784 g_string_append_printf (str, " %d", my_list_size ((struct _node **) &ct->params));
788 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->name), hp->name);
790 g_string_append_printf (str, " 0-");
792 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->value), hp->value);
794 g_string_append_printf (str, " 0-");
798 g_string_append_printf (str, " %d-", 0);
799 g_string_append_printf (str, " %d-", 0);
800 g_string_append_printf (str, " %d", 0);
804 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->id), ci->id);
806 g_string_append_printf (str, " 0-");
808 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->description), ci->description);
810 g_string_append_printf (str, " 0-");
812 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->encoding), ci->encoding);
814 g_string_append_printf (str, " 0-");
815 g_string_append_printf (str, " %u", ci->size);
818 oldr = record->cinfo;
819 record->cinfo = g_strconcat (oldr, str->str, NULL);
820 g_free (oldr); g_string_free (str, TRUE);
822 record->cinfo = str->str;
823 g_string_free (str, FALSE);
830 * camel_folder_summary_replace_flags:
831 * @summary: a #CamelFolderSummary
832 * @info: a #CamelMessageInfo
834 * Updates internal counts based on the flags in @info.
836 * Returns: Whether any count changed
841 camel_folder_summary_replace_flags (CamelFolderSummary *summary,
842 CamelMessageInfo *info)
844 guint32 old_flags, new_flags, added_flags, removed_flags;
845 gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
846 GObject *summary_object;
847 gboolean changed = FALSE;
849 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
850 g_return_val_if_fail (info != NULL, FALSE);
852 if (!camel_message_info_uid (info) ||
853 !camel_folder_summary_check_uid (summary, camel_message_info_uid (info)))
856 summary_object = G_OBJECT (summary);
858 camel_folder_summary_lock (summary);
859 g_object_freeze_notify (summary_object);
861 old_flags = GPOINTER_TO_UINT (g_hash_table_lookup (summary->priv->uids, camel_message_info_uid (info)));
862 new_flags = camel_message_info_flags (info);
864 if ((old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED) == (new_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED)) {
865 g_object_thaw_notify (summary_object);
866 camel_folder_summary_unlock (summary);
870 if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
871 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
873 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
874 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
877 added_flags = new_flags & (~(old_flags & new_flags));
878 removed_flags = old_flags & (~(old_flags & new_flags));
880 if ((old_flags & CAMEL_MESSAGE_SEEN) == (new_flags & CAMEL_MESSAGE_SEEN)) {
881 /* unread count is different from others, it asks for nonexistence
882 * of the flag, thus if it wasn't changed, then simply set it
883 * in added/removed, thus there are no false notifications
884 * on unread counts */
885 added_flags |= CAMEL_MESSAGE_SEEN;
886 removed_flags |= CAMEL_MESSAGE_SEEN;
887 } else if ((!is_junk_folder && (new_flags & CAMEL_MESSAGE_JUNK) != 0 &&
888 (old_flags & CAMEL_MESSAGE_JUNK) == (new_flags & CAMEL_MESSAGE_JUNK)) ||
889 (!is_trash_folder && (new_flags & CAMEL_MESSAGE_DELETED) != 0 &&
890 (old_flags & CAMEL_MESSAGE_DELETED) == (new_flags & CAMEL_MESSAGE_DELETED))) {
891 /* The message was set read or unread, but it is a junk or deleted message,
892 * in a non-Junk/non-Trash folder, thus it doesn't influence an unread count
893 * there, thus pretend unread didn't change */
894 added_flags |= CAMEL_MESSAGE_SEEN;
895 removed_flags |= CAMEL_MESSAGE_SEEN;
898 /* decrement counts with removed flags */
899 changed = folder_summary_update_counts_by_flags (summary, removed_flags, UPDATE_COUNTS_SUB_WITHOUT_TOTAL) || changed;
900 /* increment counts with added flags */
901 changed = folder_summary_update_counts_by_flags (summary, added_flags, UPDATE_COUNTS_ADD_WITHOUT_TOTAL) || changed;
903 /* update current flags on the summary */
904 g_hash_table_insert (
906 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
907 GUINT_TO_POINTER (new_flags));
909 g_object_thaw_notify (summary_object);
910 camel_folder_summary_unlock (summary);
915 static CamelMessageInfo *
916 message_info_clone (CamelFolderSummary *summary,
917 const CamelMessageInfo *mi)
919 CamelMessageInfoBase *to, *from = (CamelMessageInfoBase *) mi;
923 to = (CamelMessageInfoBase *) camel_message_info_new (summary);
925 to->flags = from->flags;
926 to->size = from->size;
927 to->date_sent = from->date_sent;
928 to->date_received = from->date_received;
931 /* NB: We don't clone the uid */
933 to->subject = camel_pstring_strdup (from->subject);
934 to->from = camel_pstring_strdup (from->from);
935 to->to = camel_pstring_strdup (from->to);
936 to->cc = camel_pstring_strdup (from->cc);
937 to->mlist = camel_pstring_strdup (from->mlist);
938 memcpy (&to->message_id, &from->message_id, sizeof (to->message_id));
939 to->preview = g_strdup (from->preview);
940 if (from->references) {
941 gint len = sizeof (*from->references) + ((from->references->size - 1) * sizeof (from->references->references[0]));
943 to->references = g_malloc (len);
944 memcpy (to->references, from->references, len);
947 flag = from->user_flags;
949 camel_flag_set (&to->user_flags, flag->name, TRUE);
953 tag = from->user_tags;
955 camel_tag_set (&to->user_tags, tag->name, tag->value);
960 /* FIXME: copy content-infos */
963 return (CamelMessageInfo *) to;
967 info_ptr (const CamelMessageInfo *mi,
971 case CAMEL_MESSAGE_INFO_SUBJECT:
972 return ((const CamelMessageInfoBase *) mi)->subject;
973 case CAMEL_MESSAGE_INFO_FROM:
974 return ((const CamelMessageInfoBase *) mi)->from;
975 case CAMEL_MESSAGE_INFO_TO:
976 return ((const CamelMessageInfoBase *) mi)->to;
977 case CAMEL_MESSAGE_INFO_CC:
978 return ((const CamelMessageInfoBase *) mi)->cc;
979 case CAMEL_MESSAGE_INFO_MLIST:
980 return ((const CamelMessageInfoBase *) mi)->mlist;
981 case CAMEL_MESSAGE_INFO_MESSAGE_ID:
982 return &((const CamelMessageInfoBase *) mi)->message_id;
983 case CAMEL_MESSAGE_INFO_REFERENCES:
984 return ((const CamelMessageInfoBase *) mi)->references;
985 case CAMEL_MESSAGE_INFO_USER_FLAGS:
986 return ((const CamelMessageInfoBase *) mi)->user_flags;
987 case CAMEL_MESSAGE_INFO_USER_TAGS:
988 return ((const CamelMessageInfoBase *) mi)->user_tags;
989 case CAMEL_MESSAGE_INFO_HEADERS:
990 return ((const CamelMessageInfoBase *) mi)->headers;
991 case CAMEL_MESSAGE_INFO_CONTENT:
992 return ((const CamelMessageInfoBase *) mi)->content;
993 case CAMEL_MESSAGE_INFO_PREVIEW:
994 return ((const CamelMessageInfoBase *) mi)->preview;
996 g_return_val_if_reached (NULL);
1001 info_uint32 (const CamelMessageInfo *mi,
1005 case CAMEL_MESSAGE_INFO_FLAGS:
1006 return ((const CamelMessageInfoBase *) mi)->flags;
1007 case CAMEL_MESSAGE_INFO_SIZE:
1008 return ((const CamelMessageInfoBase *) mi)->size;
1010 g_return_val_if_reached (0);
1015 info_time (const CamelMessageInfo *mi,
1019 case CAMEL_MESSAGE_INFO_DATE_SENT:
1020 return ((const CamelMessageInfoBase *) mi)->date_sent;
1021 case CAMEL_MESSAGE_INFO_DATE_RECEIVED:
1022 return ((const CamelMessageInfoBase *) mi)->date_received;
1024 g_return_val_if_reached (0);
1029 info_user_flag (const CamelMessageInfo *mi,
1032 return camel_flag_get (&((CamelMessageInfoBase *) mi)->user_flags, id);
1035 static const gchar *
1036 info_user_tag (const CamelMessageInfo *mi,
1039 return camel_tag_get (&((CamelMessageInfoBase *) mi)->user_tags, id);
1043 info_set_user_flag (CamelMessageInfo *info,
1047 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1050 res = camel_flag_set (&mi->user_flags, name, value);
1052 if (mi->summary && res && mi->summary->priv->folder && mi->uid
1053 && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1054 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1056 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1058 camel_folder_summary_touch (mi->summary);
1059 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1060 camel_folder_changed (mi->summary->priv->folder, changes);
1061 camel_folder_change_info_free (changes);
1068 info_set_user_tag (CamelMessageInfo *info,
1072 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1075 res = camel_tag_set (&mi->user_tags, name, value);
1077 if (mi->summary && res && mi->summary->priv->folder && mi->uid
1078 && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1079 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1081 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1083 camel_folder_summary_touch (mi->summary);
1084 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1085 camel_folder_changed (mi->summary->priv->folder, changes);
1086 camel_folder_change_info_free (changes);
1093 info_set_flags (CamelMessageInfo *info,
1098 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1099 gboolean counts_changed = FALSE;
1101 old = camel_message_info_flags (info);
1102 mi->flags = (old & ~flags) | (set & flags);
1103 if (old != mi->flags) {
1104 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1107 camel_folder_summary_touch (mi->summary);
1111 camel_folder_summary_lock (mi->summary);
1112 g_object_freeze_notify (G_OBJECT (mi->summary));
1113 counts_changed = camel_folder_summary_replace_flags (mi->summary, info);
1116 if (!counts_changed && ((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK))) {
1118 g_object_thaw_notify (G_OBJECT (mi->summary));
1119 camel_folder_summary_unlock (mi->summary);
1125 g_object_thaw_notify (G_OBJECT (mi->summary));
1126 camel_folder_summary_unlock (mi->summary);
1129 if (mi->summary && mi->summary->priv->folder && mi->uid) {
1130 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1132 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1133 camel_folder_changed (mi->summary->priv->folder, changes);
1134 camel_folder_change_info_free (changes);
1141 camel_folder_summary_class_init (CamelFolderSummaryClass *class)
1143 GObjectClass *object_class;
1145 g_type_class_add_private (class, sizeof (CamelFolderSummaryPrivate));
1147 object_class = G_OBJECT_CLASS (class);
1148 object_class->set_property = folder_summary_set_property;
1149 object_class->get_property = folder_summary_get_property;
1150 object_class->dispose = folder_summary_dispose;
1151 object_class->finalize = folder_summary_finalize;
1153 class->message_info_size = sizeof (CamelMessageInfoBase);
1154 class->content_info_size = sizeof (CamelMessageContentInfo);
1156 class->summary_header_from_db = summary_header_from_db;
1157 class->summary_header_to_db = summary_header_to_db;
1158 class->message_info_from_db = message_info_from_db;
1159 class->message_info_to_db = message_info_to_db;
1160 class->content_info_from_db = content_info_from_db;
1161 class->content_info_to_db = content_info_to_db;
1163 class->message_info_new_from_header = message_info_new_from_header;
1164 class->message_info_new_from_parser = message_info_new_from_parser;
1165 class->message_info_new_from_message = message_info_new_from_message;
1166 class->message_info_free = message_info_free;
1167 class->message_info_clone = message_info_clone;
1168 class->message_info_from_uid = message_info_from_uid;
1170 class->content_info_new_from_header = content_info_new_from_header;
1171 class->content_info_new_from_parser = content_info_new_from_parser;
1172 class->content_info_new_from_message = content_info_new_from_message;
1173 class->content_info_free = content_info_free;
1175 class->next_uid_string = next_uid_string;
1177 class->info_ptr = info_ptr;
1178 class->info_uint32 = info_uint32;
1179 class->info_time = info_time;
1180 class->info_user_flag = info_user_flag;
1181 class->info_user_tag = info_user_tag;
1183 class->info_set_user_flag = info_set_user_flag;
1184 class->info_set_user_tag = info_set_user_tag;
1186 class->info_set_flags = info_set_flags;
1189 * CamelFolderSummary:folder
1191 * The #CamelFolder to which the folder summary belongs.
1193 g_object_class_install_property (
1196 g_param_spec_object (
1199 "The folder to which the folder summary belongs",
1202 G_PARAM_CONSTRUCT_ONLY));
1205 * CamelFolderSummary:saved-count
1207 * How many infos is saved in a summary.
1209 g_object_class_install_property (
1215 "How many infos is savef in a summary",
1217 0, G_PARAM_READABLE));
1220 * CamelFolderSummary:unread-count
1222 * How many unread infos is saved in a summary.
1224 g_object_class_install_property (
1230 "How many unread infos is saved in a summary",
1232 0, G_PARAM_READABLE));
1235 * CamelFolderSummary:deleted-count
1237 * How many deleted infos is saved in a summary.
1239 g_object_class_install_property (
1245 "How many deleted infos is saved in a summary",
1247 0, G_PARAM_READABLE));
1250 * CamelFolderSummary:junk-count
1252 * How many junk infos is saved in a summary.
1254 g_object_class_install_property (
1260 "How many junk infos is saved in a summary",
1262 0, G_PARAM_READABLE));
1265 * CamelFolderSummary:junk-not-deleted-count
1267 * How many junk and not deleted infos is saved in a summary.
1269 g_object_class_install_property (
1271 PROP_JUNK_NOT_DELETED_COUNT,
1273 "junk-not-deleted-count",
1274 "Junk not deleted count",
1275 "How many junk and not deleted infos is saved in a summary",
1277 0, G_PARAM_READABLE));
1280 * CamelFolderSummary:visible-count
1282 * How many visible (not deleted and not junk) infos is saved in a summary.
1284 g_object_class_install_property (
1290 "How many visible (not deleted and not junk) infos is saved in a summary",
1292 0, G_PARAM_READABLE));
1295 * CamelFolderSummary:build-content
1297 * Whether to build CamelMessageInfo.content.
1299 g_object_class_install_property (
1302 g_param_spec_boolean (
1305 "Whether to build CamelMessageInfo.content",
1307 G_PARAM_READWRITE));
1310 * CamelFolderSummary:need-preview
1313 g_object_class_install_property (
1316 g_param_spec_boolean (
1321 G_PARAM_READWRITE));
1325 camel_folder_summary_init (CamelFolderSummary *summary)
1327 summary->priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (summary);
1329 summary->version = CAMEL_FOLDER_SUMMARY_VERSION;
1333 summary->priv->filter_charset = g_hash_table_new (
1334 camel_strcase_hash, camel_strcase_equal);
1336 summary->priv->need_preview = FALSE;
1337 summary->priv->preview_updates = g_hash_table_new (g_str_hash, g_str_equal);
1339 summary->priv->nextuid = 1;
1340 summary->priv->uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
1341 summary->priv->loaded_infos = g_hash_table_new (g_str_hash, g_str_equal);
1343 g_rec_mutex_init (&summary->priv->summary_lock);
1344 g_rec_mutex_init (&summary->priv->filter_lock);
1346 summary->priv->cache_load_time = 0;
1347 summary->priv->timeout_handle = 0;
1351 * camel_folder_summary_new:
1352 * @folder: parent #CamelFolder object
1354 * Create a new #CamelFolderSummary object.
1356 * Returns: a new #CamelFolderSummary object
1358 CamelFolderSummary *
1359 camel_folder_summary_new (CamelFolder *folder)
1361 return g_object_new (CAMEL_TYPE_FOLDER_SUMMARY, "folder", folder, NULL);
1365 * camel_folder_summary_get_index:
1366 * @summary: a #CamelFolderSummary object
1368 * Returns: a #CamelFolder to which the summary if associated.
1373 camel_folder_summary_get_folder (CamelFolderSummary *summary)
1375 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1377 return summary->priv->folder;
1381 * camel_folder_summary_get_saved_count:
1382 * @summary: a #CamelFolderSummary object
1384 * Returns: Count of saved infos.
1389 camel_folder_summary_get_saved_count (CamelFolderSummary *summary)
1391 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1393 return summary->priv->saved_count;
1397 * camel_folder_summary_get_unread_count:
1398 * @summary: a #CamelFolderSummary object
1400 * Returns: Count of unread infos.
1405 camel_folder_summary_get_unread_count (CamelFolderSummary *summary)
1407 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1409 return summary->priv->unread_count;
1413 * camel_folder_summary_get_deleted_count:
1414 * @summary: a #CamelFolderSummary object
1416 * Returns: Count of deleted infos.
1421 camel_folder_summary_get_deleted_count (CamelFolderSummary *summary)
1423 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1425 return summary->priv->deleted_count;
1429 * camel_folder_summary_get_junk_count:
1430 * @summary: a #CamelFolderSummary object
1432 * Returns: Count of junk infos.
1437 camel_folder_summary_get_junk_count (CamelFolderSummary *summary)
1439 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1441 return summary->priv->junk_count;
1445 * camel_folder_summary_get_junk_not_deleted_count:
1446 * @summary: a #CamelFolderSummary object
1448 * Returns: Count of junk and not deleted infos.
1453 camel_folder_summary_get_junk_not_deleted_count (CamelFolderSummary *summary)
1455 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1457 return summary->priv->junk_not_deleted_count;
1461 * camel_folder_summary_get_visible_count:
1462 * @summary: a #CamelFolderSummary object
1464 * Returns: Count of visible (not junk and not deleted) infos.
1469 camel_folder_summary_get_visible_count (CamelFolderSummary *summary)
1471 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1473 return summary->priv->visible_count;
1477 * camel_folder_summary_set_index:
1478 * @summary: a #CamelFolderSummary object
1479 * @index: a #CamelIndex
1481 * Set the index used to index body content. If the index is %NULL, or
1482 * not set (the default), no indexing of body content will take place.
1484 * Unlike earlier behaviour, build_content need not be set to perform indexing.
1487 camel_folder_summary_set_index (CamelFolderSummary *summary,
1490 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1493 g_object_ref (index);
1495 if (summary->priv->index != NULL)
1496 g_object_unref (summary->priv->index);
1498 summary->priv->index = index;
1502 * camel_folder_summary_get_index:
1503 * @summary: a #CamelFolderSummary object
1505 * Returns: a #CamelIndex used to index body content.
1510 camel_folder_summary_get_index (CamelFolderSummary *summary)
1512 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1514 return summary->priv->index;
1518 * camel_folder_summary_set_build_content:
1519 * @summary: a #CamelFolderSummary object
1520 * @state: to build or not to build the content
1522 * Set a flag to tell the summary to build the content info summary
1523 * (#CamelMessageInfo.content). The default is not to build content
1527 camel_folder_summary_set_build_content (CamelFolderSummary *summary,
1530 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1532 if (summary->priv->build_content == state)
1535 summary->priv->build_content = state;
1537 g_object_notify (G_OBJECT (summary), "build-content");
1541 * camel_folder_summary_get_build_content:
1542 * @summary: a #CamelFolderSummary object
1544 * Returns: Whether to build #CamelMessageInfo.content.
1549 camel_folder_summary_get_build_content (CamelFolderSummary *summary)
1551 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1553 return summary->priv->build_content;
1557 * camel_folder_summary_set_need_preview:
1562 camel_folder_summary_set_need_preview (CamelFolderSummary *summary,
1565 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1567 summary->priv->need_preview = preview;
1571 * camel_folder_summary_get_need_preview:
1576 camel_folder_summary_get_need_preview (CamelFolderSummary *summary)
1578 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1580 return summary->priv->need_preview;
1584 * camel_folder_summary_next_uid:
1585 * @summary: a #CamelFolderSummary object
1587 * Generate a new unique uid value as an integer. This
1588 * may be used to create a unique sequence of numbers.
1590 * Returns: the next unique uid value
1593 camel_folder_summary_next_uid (CamelFolderSummary *summary)
1597 camel_folder_summary_lock (summary);
1599 uid = summary->priv->nextuid++;
1600 camel_folder_summary_touch (summary);
1602 camel_folder_summary_unlock (summary);
1608 * camel_folder_summary_set_next_uid:
1609 * @summary: a #CamelFolderSummary object
1610 * @uid: The next minimum uid to assign. To avoid clashing
1611 * uid's, set this to the uid of a given messages + 1.
1613 * Set the next minimum uid available. This can be used to
1614 * ensure new uid's do not clash with existing uid's.
1617 camel_folder_summary_set_next_uid (CamelFolderSummary *summary,
1620 camel_folder_summary_lock (summary);
1622 summary->priv->nextuid = MAX (summary->priv->nextuid, uid);
1623 camel_folder_summary_touch (summary);
1625 camel_folder_summary_unlock (summary);
1629 * camel_folder_summary_get_next_uid:
1630 * @summary: a #CamelFolderSummary object
1632 * Returns: Next uid currently awaiting for assignment. The difference from
1633 * camel_folder_summary_next_uid() is that this function returns actual
1634 * value and doesn't increment it before returning.
1639 camel_folder_summary_get_next_uid (CamelFolderSummary *summary)
1643 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1645 camel_folder_summary_lock (summary);
1647 res = summary->priv->nextuid;
1649 camel_folder_summary_unlock (summary);
1655 * camel_folder_summary_next_uid_string:
1656 * @summary: a #CamelFolderSummary object
1658 * Retrieve the next uid, but as a formatted string.
1660 * Returns: the next uid as an unsigned integer string.
1661 * This string must be freed by the caller.
1664 camel_folder_summary_next_uid_string (CamelFolderSummary *summary)
1666 CamelFolderSummaryClass *class;
1668 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1670 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1671 g_return_val_if_fail (class->next_uid_string != NULL, NULL);
1673 return class->next_uid_string (summary);
1677 * camel_folder_summary_count:
1678 * @summary: a #CamelFolderSummary object
1680 * Get the number of summary items stored in this summary.
1682 * Returns: the number of items in the summary
1685 camel_folder_summary_count (CamelFolderSummary *summary)
1687 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1689 return g_hash_table_size (summary->priv->uids);
1693 * camel_folder_summary_check_uid
1694 * @summary: a #CamelFolderSummary object
1697 * Check if the uid is valid. This isn't very efficient, so it shouldn't be called iteratively.
1700 * Returns: if the uid is present in the summary or not (%TRUE or %FALSE)
1705 camel_folder_summary_check_uid (CamelFolderSummary *summary,
1710 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1711 g_return_val_if_fail (uid != NULL, FALSE);
1713 camel_folder_summary_lock (summary);
1715 ret = g_hash_table_lookup_extended (summary->priv->uids, uid, NULL, NULL);
1717 camel_folder_summary_unlock (summary);
1723 folder_summary_dupe_uids_to_array (gpointer key_uid,
1724 gpointer value_flags,
1727 g_ptr_array_add (user_data, (gpointer) camel_pstring_strdup (key_uid));
1731 * camel_folder_summary_get_array:
1732 * @summary: a #CamelFolderSummary object
1734 * Obtain a copy of the summary array. This is done atomically,
1735 * so cannot contain empty entries.
1737 * Free with camel_folder_summary_free_array()
1739 * Returns: a #GPtrArray of uids
1744 camel_folder_summary_get_array (CamelFolderSummary *summary)
1748 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1750 camel_folder_summary_lock (summary);
1752 res = g_ptr_array_sized_new (g_hash_table_size (summary->priv->uids));
1753 g_hash_table_foreach (summary->priv->uids, folder_summary_dupe_uids_to_array, res);
1755 camel_folder_summary_unlock (summary);
1761 * camel_folder_summary_free_array:
1762 * @array: a #GPtrArray returned from camel_folder_summary_get_array()
1764 * Free's array and its elements returned from camel_folder_summary_get_array().
1769 camel_folder_summary_free_array (GPtrArray *array)
1774 g_ptr_array_foreach (array, (GFunc) camel_pstring_free, NULL);
1775 g_ptr_array_free (array, TRUE);
1779 cfs_copy_uids_cb (gpointer key,
1783 const gchar *uid = key;
1784 GHashTable *copy_hash = user_data;
1786 g_hash_table_insert (copy_hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
1790 * camel_folder_summary_get_hash:
1791 * @summary: a #CamelFolderSummary object
1793 * Returns hash of current stored 'uids' in summary, where key is 'uid'
1794 * from the string pool, and value is 1. The returned pointer should
1795 * be freed with g_hash_table_destroy().
1797 * Note: When searching for values always use uids from the string pool.
1802 camel_folder_summary_get_hash (CamelFolderSummary *summary)
1806 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1808 camel_folder_summary_lock (summary);
1810 /* using direct hash because of strings being from the string pool */
1811 uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
1812 g_hash_table_foreach (summary->priv->uids, cfs_copy_uids_cb, uids);
1814 camel_folder_summary_unlock (summary);
1820 * camel_folder_summary_peek_loaded:
1825 camel_folder_summary_peek_loaded (CamelFolderSummary *summary,
1828 CamelMessageInfo *info;
1830 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1832 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1835 camel_message_info_ref (info);
1840 struct _db_pass_data {
1841 GHashTable *columns_hash;
1842 CamelFolderSummary *summary;
1843 gboolean add; /* or just insert to hashtable */
1846 static CamelMessageInfo *
1847 message_info_from_uid (CamelFolderSummary *summary,
1850 CamelMessageInfo *info;
1853 camel_folder_summary_lock (summary);
1855 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1859 CamelStore *parent_store;
1860 const gchar *folder_name;
1861 struct _db_pass_data data;
1863 folder_name = camel_folder_get_full_name (summary->priv->folder);
1865 if (is_in_memory_summary (summary)) {
1866 camel_folder_summary_unlock (summary);
1868 "%s: Tried to load uid '%s' "
1869 "from DB on in-memory summary of '%s'",
1870 G_STRFUNC, uid, folder_name);
1875 camel_folder_get_parent_store (summary->priv->folder);
1876 cdb = parent_store->cdb_r;
1878 data.columns_hash = NULL;
1879 data.summary = summary;
1882 ret = camel_db_read_message_info_record_with_uid (
1883 cdb, folder_name, uid, &data,
1884 camel_read_mir_callback, NULL);
1885 if (data.columns_hash)
1886 g_hash_table_destroy (data.columns_hash);
1889 camel_folder_summary_unlock (summary);
1893 /* We would have double reffed at camel_read_mir_callback */
1894 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1896 cfs_schedule_info_release_timer (summary);
1900 camel_message_info_ref (info);
1902 camel_folder_summary_unlock (summary);
1908 * camel_folder_summary_get:
1909 * @summary: a #CamelFolderSummary object
1912 * Retrieve a summary item by uid.
1914 * A referenced to the summary item is returned, which may be
1915 * ref'd or free'd as appropriate.
1917 * Returns: the summary item, or %NULL if the uid @uid is not available
1919 * See camel_folder_summary_get_info_flags().
1924 camel_folder_summary_get (CamelFolderSummary *summary,
1927 CamelFolderSummaryClass *class;
1929 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1930 g_return_val_if_fail (uid != NULL, NULL);
1932 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1933 g_return_val_if_fail (class->message_info_from_uid != NULL, NULL);
1935 return class->message_info_from_uid (summary, uid);
1939 * camel_folder_summary_get_info_flags:
1940 * @summary: a #CamelFolderSummary object
1943 * Retrieve CamelMessageInfo::flags for a message info with UID @uid.
1944 * This is much quicker than camel_folder_summary_get(), because it
1945 * doesn't require reading the message info from a disk.
1947 * Returns: the flags currently stored for message info with UID @uid,
1953 camel_folder_summary_get_info_flags (CamelFolderSummary *summary,
1956 gpointer ptr_uid = NULL, ptr_flags = NULL;
1958 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), (~0));
1959 g_return_val_if_fail (uid != NULL, (~0));
1961 camel_folder_summary_lock (summary);
1962 if (!g_hash_table_lookup_extended (summary->priv->uids, uid, &ptr_uid, &ptr_flags)) {
1963 camel_folder_summary_unlock (summary);
1967 camel_folder_summary_unlock (summary);
1969 return GPOINTER_TO_UINT (ptr_flags);
1972 static CamelMessageContentInfo *
1973 perform_content_info_load_from_db (CamelFolderSummary *summary,
1978 CamelMessageContentInfo *ci, *pci;
1981 ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_from_db (summary, mir);
1987 if (*part == ' ') part++;
1988 count = bdata_extract_digit (&part);
1991 for (i = 0; i < count; i++) {
1992 pci = perform_content_info_load_from_db (summary, mir);
1994 my_list_append ((struct _node **) &ci->childs, (struct _node *) pci);
1997 d (fprintf (stderr, "Summary file format messed up?"));
1998 camel_folder_summary_content_info_free (summary, ci);
2006 gather_dirty_uids (gpointer key,
2010 const gchar *uid = key;
2011 CamelMessageInfoBase *info = value;
2012 GHashTable *hash = user_data;
2015 g_hash_table_insert (hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2019 gather_changed_uids (gpointer key,
2023 const gchar *uid = key;
2024 guint32 flags = GPOINTER_TO_UINT (value);
2025 GHashTable *hash = user_data;
2027 if ((flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0)
2028 g_hash_table_insert (hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2032 * camel_folder_summary_get_changed:
2037 camel_folder_summary_get_changed (CamelFolderSummary *summary)
2040 GHashTable *hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
2042 camel_folder_summary_lock (summary);
2044 g_hash_table_foreach (summary->priv->loaded_infos, gather_dirty_uids, hash);
2045 g_hash_table_foreach (summary->priv->uids, gather_changed_uids, hash);
2047 res = g_ptr_array_sized_new (g_hash_table_size (hash));
2048 g_hash_table_foreach (hash, folder_summary_dupe_uids_to_array, res);
2050 camel_folder_summary_unlock (summary);
2052 g_hash_table_destroy (hash);
2058 count_changed_uids (gchar *key,
2059 CamelMessageInfoBase *info,
2067 cfs_count_dirty (CamelFolderSummary *summary)
2071 camel_folder_summary_lock (summary);
2072 g_hash_table_foreach (summary->priv->loaded_infos, (GHFunc) count_changed_uids, &count);
2073 camel_folder_summary_unlock (summary);
2079 remove_item (gchar *uid,
2080 CamelMessageInfoBase *info,
2081 GSList **to_remove_infos)
2083 if (info->refcount == 1 && !info->dirty && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) {
2084 *to_remove_infos = g_slist_prepend (*to_remove_infos, info);
2092 remove_cache (CamelSession *session,
2093 GCancellable *cancellable,
2094 CamelFolderSummary *summary,
2097 GSList *to_remove_infos = NULL;
2099 CAMEL_DB_RELEASE_SQLITE_MEMORY;
2101 if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2104 camel_folder_summary_lock (summary);
2106 g_hash_table_foreach_remove (summary->priv->loaded_infos, (GHRFunc) remove_item, &to_remove_infos);
2108 g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_unref, NULL);
2109 g_slist_free (to_remove_infos);
2111 camel_folder_summary_unlock (summary);
2113 summary->priv->cache_load_time = time (NULL);
2117 cfs_try_release_memory (CamelFolderSummary *summary)
2119 CamelStore *parent_store;
2120 CamelSession *session;
2122 /* If folder is freed or if the cache is nil then clean up */
2123 if (!summary->priv->folder ||
2124 !g_hash_table_size (summary->priv->loaded_infos) ||
2125 is_in_memory_summary (summary)) {
2126 summary->priv->cache_load_time = 0;
2127 summary->priv->timeout_handle = 0;
2128 g_object_unref (summary);
2133 if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2136 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2137 session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
2139 camel_session_submit_job (
2141 (CamelSessionCallback) remove_cache,
2142 g_object_ref (summary),
2143 (GDestroyNotify) g_object_unref);
2145 g_object_unref (session);
2151 cfs_schedule_info_release_timer (CamelFolderSummary *summary)
2153 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2155 if (is_in_memory_summary (summary))
2158 if (!summary->priv->timeout_handle) {
2159 static gboolean know_can_do = FALSE, can_do = TRUE;
2162 can_do = !g_getenv ("CAMEL_FREE_INFOS");
2166 /* FIXME[disk-summary] LRU please and not timeouts */
2168 summary->priv->timeout_handle = g_timeout_add_seconds (
2170 (GSourceFunc) cfs_try_release_memory,
2171 g_object_ref (summary));
2172 g_source_set_name_by_id (
2173 summary->priv->timeout_handle,
2174 "[camel] cfs_try_release_memory");
2178 /* update also cache load time to the actual, to not release something just loaded */
2179 summary->priv->cache_load_time = time (NULL);
2183 cfs_cache_size (CamelFolderSummary *summary)
2185 /* FIXME[disk-summary] this is a timely hack. fix it well */
2186 if (!CAMEL_IS_VEE_FOLDER (summary->priv->folder))
2187 return g_hash_table_size (summary->priv->loaded_infos);
2189 return g_hash_table_size (summary->priv->uids);
2192 /* Update preview of cached messages */
2195 msg_update_preview (const gchar *uid,
2197 CamelFolder *folder)
2199 CamelMessageInfoBase *info = (CamelMessageInfoBase *) camel_folder_summary_get (folder->summary, uid);
2200 CamelMimeMessage *msg;
2201 CamelStore *parent_store;
2202 const gchar *full_name;
2204 full_name = camel_folder_get_full_name (folder);
2205 parent_store = camel_folder_get_parent_store (folder);
2207 /* FIXME Pass a GCancellable */
2208 msg = camel_folder_get_message_sync (folder, uid, NULL, NULL);
2210 if (camel_mime_message_build_preview ((CamelMimePart *) msg, (CamelMessageInfo *) info) && info->preview) {
2211 if (!is_in_memory_summary (folder->summary))
2212 camel_db_write_preview_record (parent_store->cdb_w, full_name, info->uid, info->preview, NULL);
2215 camel_message_info_unref (info);
2219 pick_uids (const gchar *uid,
2220 CamelMessageInfoBase *mi,
2224 g_ptr_array_add (array, (gchar *) camel_pstring_strdup (uid));
2228 copy_all_uids_to_hash (gpointer uid,
2231 g_return_if_fail (uid != NULL);
2233 g_hash_table_insert (hash, (gchar *) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2237 fill_mi (const gchar *uid,
2239 CamelFolder *folder)
2241 CamelMessageInfoBase *info;
2243 info = g_hash_table_lookup (folder->summary->priv->loaded_infos, uid);
2244 if (info) /* We re assign the memory of msg */
2245 info->preview = (gchar *) msg;
2246 camel_pstring_free (uid); /* unref the uid */
2252 preview_update (CamelSession *session,
2253 GCancellable *cancellable,
2254 CamelFolder *folder,
2257 /* FIXME: Either lock & use or copy & use.*/
2258 GPtrArray *uids_uncached, *uids_array;
2259 GHashTable *preview_data, *uids_hash;
2260 CamelStore *parent_store;
2261 const gchar *full_name;
2262 gboolean is_in_memory = is_in_memory_summary (folder->summary);
2265 uids_array = camel_folder_summary_get_array (folder->summary);
2266 uids_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
2267 g_ptr_array_foreach (uids_array, copy_all_uids_to_hash, uids_hash);
2268 uids_uncached = camel_folder_get_uncached_uids (folder, uids_array, NULL);
2269 camel_folder_summary_free_array (uids_array);
2272 full_name = camel_folder_get_full_name (folder);
2273 parent_store = camel_folder_get_parent_store (folder);
2274 preview_data = is_in_memory ? NULL : camel_db_get_folder_preview (parent_store->cdb_r, full_name, NULL);
2276 g_hash_table_foreach_remove (preview_data, (GHRFunc) fill_mi, folder);
2277 g_hash_table_destroy (preview_data);
2280 camel_folder_summary_lock (folder->summary);
2281 g_hash_table_foreach (folder->summary->priv->loaded_infos, (GHFunc) pick_uids, uids_uncached);
2282 camel_folder_summary_unlock (folder->summary);
2284 for (i = 0; i < uids_uncached->len; i++) {
2285 g_hash_table_remove (uids_hash, uids_uncached->pdata[i]);
2288 camel_folder_lock (folder);
2290 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2291 g_hash_table_foreach (uids_hash, (GHFunc) msg_update_preview, folder);
2293 camel_db_end_transaction (parent_store->cdb_w, NULL);
2294 camel_folder_unlock (folder);
2295 camel_folder_free_uids (folder, uids_uncached);
2296 g_hash_table_destroy (uids_hash);
2302 cfs_reload_from_db (CamelFolderSummary *summary,
2306 CamelStore *parent_store;
2307 const gchar *folder_name;
2309 struct _db_pass_data data;
2311 /* FIXME[disk-summary] baseclass this, and vfolders we may have to
2313 d (printf ("\ncamel_folder_summary_reload_from_db called \n"));
2315 if (is_in_memory_summary (summary))
2318 folder_name = camel_folder_get_full_name (summary->priv->folder);
2319 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2320 cdb = parent_store->cdb_r;
2322 data.columns_hash = NULL;
2323 data.summary = summary;
2326 ret = camel_db_read_message_info_records (
2327 cdb, folder_name, (gpointer) &data,
2328 camel_read_mir_callback, NULL);
2330 if (data.columns_hash)
2331 g_hash_table_destroy (data.columns_hash);
2333 cfs_schedule_info_release_timer (summary);
2335 /* FIXME Convert this to a GTask, submitted through
2336 * camel_service_queue_task(). Then it won't
2337 * have to call camel_folder_lock/unlock(). */
2338 if (summary->priv->need_preview) {
2339 CamelSession *session;
2341 /* This may not be available in a case of this being called as part
2342 of CamelSession's dispose, because the CamelService uses GWeakRef
2343 object which is invalidates its content when it reaches the dispose. */
2344 session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
2346 camel_session_submit_job (
2348 (CamelSessionCallback) preview_update,
2349 g_object_ref (summary->priv->folder),
2350 (GDestroyNotify) g_object_unref);
2351 g_object_unref (session);
2355 return ret == 0 ? 0 : -1;
2359 * camel_folder_summary_add_preview:
2364 camel_folder_summary_add_preview (CamelFolderSummary *summary,
2365 CamelMessageInfo *info)
2367 camel_folder_summary_lock (summary);
2368 g_hash_table_insert (summary->priv->preview_updates, (gchar *) info->uid, ((CamelMessageInfoBase *) info)->preview);
2369 camel_folder_summary_touch (summary);
2370 camel_folder_summary_unlock (summary);
2374 * camel_folder_summary_prepare_fetch_all:
2375 * @summary: #CamelFolderSummary object
2376 * @error: return location for a #GError, or %NULL
2378 * Loads all infos into memory, if they are not yet and ensures
2379 * they will not be freed in next couple minutes. Call this function
2380 * before any mass operation or when all message infos will be needed,
2381 * for better performance.
2386 camel_folder_summary_prepare_fetch_all (CamelFolderSummary *summary,
2389 guint loaded, known;
2391 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2393 loaded = cfs_cache_size (summary);
2394 known = camel_folder_summary_count (summary);
2396 if (known - loaded > 50) {
2397 camel_folder_summary_lock (summary);
2398 cfs_reload_from_db (summary, error);
2399 camel_folder_summary_unlock (summary);
2402 /* update also cache load time, even when not loaded anything */
2403 summary->priv->cache_load_time = time (NULL);
2407 * camel_folder_summary_load_from_db:
2412 camel_folder_summary_load_from_db (CamelFolderSummary *summary,
2416 CamelStore *parent_store;
2417 const gchar *full_name;
2419 GError *local_error = NULL;
2421 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
2423 if (is_in_memory_summary (summary))
2426 camel_folder_summary_lock (summary);
2427 camel_folder_summary_save_to_db (summary, NULL);
2429 /* struct _db_pass_data data; */
2430 d (printf ("\ncamel_folder_summary_load_from_db called \n"));
2432 full_name = camel_folder_get_full_name (summary->priv->folder);
2433 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2434 if (!camel_folder_summary_header_load_from_db (summary, parent_store, full_name, error)) {
2435 camel_folder_summary_unlock (summary);
2439 cdb = parent_store->cdb_r;
2441 ret = camel_db_get_folder_uids (
2442 cdb, full_name, summary->sort_by, summary->collate,
2443 summary->priv->uids, &local_error);
2445 if (local_error != NULL && local_error->message != NULL &&
2446 strstr (local_error->message, "no such table") != NULL) {
2447 g_clear_error (&local_error);
2449 /* create table the first time it is accessed and missing */
2450 ret = camel_db_prepare_message_info_table (cdb, full_name, error);
2451 } else if (local_error != NULL)
2452 g_propagate_error (error, local_error);
2454 camel_folder_summary_unlock (summary);
2460 mir_from_cols (CamelMIRecord *mir,
2461 CamelFolderSummary *summary,
2462 GHashTable **columns_hash,
2469 for (i = 0; i < ncol; ++i) {
2470 if (!name[i] || !cols[i])
2473 switch (camel_db_get_column_ident (columns_hash, i, ncol, name)) {
2474 case CAMEL_DB_COLUMN_UID:
2475 mir->uid = (gchar *) camel_pstring_strdup (cols[i]);
2477 case CAMEL_DB_COLUMN_FLAGS:
2478 mir->flags = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2480 case CAMEL_DB_COLUMN_READ:
2481 mir->read = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2483 case CAMEL_DB_COLUMN_DELETED:
2484 mir->deleted = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2486 case CAMEL_DB_COLUMN_REPLIED:
2487 mir->replied = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2489 case CAMEL_DB_COLUMN_IMPORTANT:
2490 mir->important = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2492 case CAMEL_DB_COLUMN_JUNK:
2493 mir->junk = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2495 case CAMEL_DB_COLUMN_ATTACHMENT:
2496 mir->attachment = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2498 case CAMEL_DB_COLUMN_SIZE:
2499 mir->size = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2501 case CAMEL_DB_COLUMN_DSENT:
2502 mir->dsent = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2504 case CAMEL_DB_COLUMN_DRECEIVED:
2505 mir->dreceived = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2507 case CAMEL_DB_COLUMN_SUBJECT:
2508 mir->subject = (gchar *) camel_pstring_strdup (cols[i]);
2510 case CAMEL_DB_COLUMN_MAIL_FROM:
2511 mir->from = (gchar *) camel_pstring_strdup (cols[i]);
2513 case CAMEL_DB_COLUMN_MAIL_TO:
2514 mir->to = (gchar *) camel_pstring_strdup (cols[i]);
2516 case CAMEL_DB_COLUMN_MAIL_CC:
2517 mir->cc = (gchar *) camel_pstring_strdup (cols[i]);
2519 case CAMEL_DB_COLUMN_MLIST:
2520 mir->mlist = (gchar *) camel_pstring_strdup (cols[i]);
2522 case CAMEL_DB_COLUMN_FOLLOWUP_FLAG:
2523 mir->followup_flag = (gchar *) camel_pstring_strdup (cols[i]);
2525 case CAMEL_DB_COLUMN_FOLLOWUP_COMPLETED_ON:
2526 mir->followup_completed_on = (gchar *) camel_pstring_strdup (cols[i]);
2528 case CAMEL_DB_COLUMN_FOLLOWUP_DUE_BY:
2529 mir->followup_due_by = (gchar *) camel_pstring_strdup (cols[i]);
2531 case CAMEL_DB_COLUMN_PART:
2532 mir->part = g_strdup (cols[i]);
2534 case CAMEL_DB_COLUMN_LABELS:
2535 mir->labels = g_strdup (cols[i]);
2537 case CAMEL_DB_COLUMN_USERTAGS:
2538 mir->usertags = g_strdup (cols[i]);
2540 case CAMEL_DB_COLUMN_CINFO:
2541 mir->cinfo = g_strdup (cols[i]);
2543 case CAMEL_DB_COLUMN_BDATA:
2544 mir->bdata = g_strdup (cols[i]);
2546 case CAMEL_DB_COLUMN_BODYSTRUCTURE:
2547 /* Evolution itself doesn't yet use this, ignoring */
2548 /* mir->bodystructure = g_strdup (cols[i]); */
2551 g_warn_if_reached ();
2558 camel_read_mir_callback (gpointer ref,
2563 struct _db_pass_data *data = (struct _db_pass_data *) ref;
2564 CamelFolderSummary *summary = data->summary;
2566 CamelMessageInfo *info;
2569 mir = g_new0 (CamelMIRecord , 1);
2570 mir_from_cols (mir, summary, &data->columns_hash, ncol, cols, name);
2572 camel_folder_summary_lock (summary);
2573 if (!mir->uid || g_hash_table_lookup (summary->priv->loaded_infos, mir->uid)) {
2574 /* Unlock and better return */
2575 camel_folder_summary_unlock (summary);
2576 camel_db_camel_mir_free (mir);
2579 camel_folder_summary_unlock (summary);
2581 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_from_db (summary, mir);
2585 if (summary->priv->build_content) {
2588 /* FIXME: this should be done differently, how i don't know */
2589 ((CamelMessageInfoBase *) info)->content = perform_content_info_load_from_db (summary, mir);
2590 if (((CamelMessageInfoBase *) info)->content == NULL) {
2591 camel_message_info_unref (info);
2597 camel_db_camel_mir_free (mir);
2602 /* Just now we are reading from the DB, it can't be dirty. */
2603 ((CamelMessageInfoBase *) info)->dirty = FALSE;
2605 camel_folder_summary_add (summary, info);
2607 camel_folder_summary_insert (summary, info, TRUE);
2610 g_warning ("Loading messageinfo from db failed");
2614 camel_db_camel_mir_free (mir);
2619 /* saves the content descriptions, recursively */
2621 perform_content_info_save_to_db (CamelFolderSummary *summary,
2622 CamelMessageContentInfo *ci,
2623 CamelMIRecord *record)
2625 CamelMessageContentInfo *part;
2628 if (!CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_to_db (summary, ci, record))
2631 oldr = record->cinfo;
2632 record->cinfo = g_strdup_printf ("%s %d", oldr, my_list_size ((struct _node **) &ci->childs));
2637 if (perform_content_info_save_to_db (summary, part, record) == -1)
2646 save_to_db_cb (gpointer key,
2650 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) value;
2651 CamelFolderSummary *summary = (CamelFolderSummary *) mi->summary;
2652 CamelStore *parent_store;
2653 const gchar *full_name;
2656 GError **error = data;
2658 full_name = camel_folder_get_full_name (summary->priv->folder);
2659 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2660 cdb = parent_store->cdb_w;
2665 mir = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_to_db (summary, (CamelMessageInfo *) mi);
2667 if (mir && summary->priv->build_content) {
2668 if (!perform_content_info_save_to_db (summary, ((CamelMessageInfoBase *) mi)->content, mir)) {
2669 g_warning ("unable to save mir+cinfo for uid: %s\n", mir->uid);
2670 camel_db_camel_mir_free (mir);
2671 /* FIXME: Add exception here */
2676 g_return_if_fail (mir != NULL);
2678 if (camel_db_write_message_info_record (cdb, full_name, mir, error) != 0) {
2679 camel_db_camel_mir_free (mir);
2683 /* Reset the dirty flag which decides if the changes are synced to the DB or not.
2684 The FOLDER_FLAGGED should be used to check if the changes are synced to the server.
2685 So, dont unset the FOLDER_FLAGGED flag */
2688 camel_db_camel_mir_free (mir);
2692 save_message_infos_to_db (CamelFolderSummary *summary,
2695 CamelStore *parent_store;
2697 const gchar *full_name;
2699 if (is_in_memory_summary (summary))
2702 full_name = camel_folder_get_full_name (summary->priv->folder);
2703 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2704 cdb = parent_store->cdb_w;
2706 if (camel_db_prepare_message_info_table (cdb, full_name, error) != 0)
2709 camel_folder_summary_lock (summary);
2711 /* Push MessageInfo-es */
2712 camel_db_begin_transaction (cdb, NULL);
2713 g_hash_table_foreach (summary->priv->loaded_infos, save_to_db_cb, error);
2714 camel_db_end_transaction (cdb, NULL);
2716 camel_folder_summary_unlock (summary);
2717 cfs_schedule_info_release_timer (summary);
2723 msg_save_preview (const gchar *uid,
2725 CamelFolder *folder)
2727 CamelStore *parent_store;
2728 const gchar *full_name;
2730 full_name = camel_folder_get_full_name (folder);
2731 parent_store = camel_folder_get_parent_store (folder);
2733 camel_db_write_preview_record (
2734 parent_store->cdb_w, full_name, uid, (gchar *) value, NULL);
2738 * camel_folder_summary_save_to_db:
2743 camel_folder_summary_save_to_db (CamelFolderSummary *summary,
2746 CamelStore *parent_store;
2748 CamelFIRecord *record;
2751 g_return_val_if_fail (summary != NULL, FALSE);
2753 if (!(summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) ||
2754 is_in_memory_summary (summary))
2757 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2758 cdb = parent_store->cdb_w;
2760 camel_folder_summary_lock (summary);
2762 d (printf ("\ncamel_folder_summary_save_to_db called \n"));
2763 if (summary->priv->need_preview && g_hash_table_size (summary->priv->preview_updates)) {
2764 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2765 g_hash_table_foreach (summary->priv->preview_updates, (GHFunc) msg_save_preview, summary->priv->folder);
2766 g_hash_table_remove_all (summary->priv->preview_updates);
2767 camel_db_end_transaction (parent_store->cdb_w, NULL);
2770 summary->flags &= ~CAMEL_FOLDER_SUMMARY_DIRTY;
2772 count = cfs_count_dirty (summary);
2774 gboolean res = camel_folder_summary_header_save_to_db (summary, error);
2775 camel_folder_summary_unlock (summary);
2779 ret = save_message_infos_to_db (summary, error);
2781 /* Failed, so lets reset the flag */
2782 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2783 camel_folder_summary_unlock (summary);
2787 /* XXX So... if an error is set, how do we even reach this point
2788 * given the above error check? Oye vey this logic is nasty. */
2789 if (error != NULL && *error != NULL &&
2790 strstr ((*error)->message, "26 columns but 28 values") != NULL) {
2791 const gchar *full_name;
2793 full_name = camel_folder_get_full_name (summary->priv->folder);
2794 g_warning ("Fixing up a broken summary migration on %s\n", full_name);
2796 /* Begin everything again. */
2797 camel_db_begin_transaction (cdb, NULL);
2798 camel_db_reset_folder_version (cdb, full_name, 0, NULL);
2799 camel_db_end_transaction (cdb, NULL);
2801 ret = save_message_infos_to_db (summary, error);
2803 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2804 camel_folder_summary_unlock (summary);
2809 record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2811 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2812 camel_folder_summary_unlock (summary);
2816 camel_db_begin_transaction (cdb, NULL);
2817 ret = camel_db_write_folder_info_record (cdb, record, error);
2818 g_free (record->folder_name);
2819 g_free (record->bdata);
2823 camel_db_abort_transaction (cdb, NULL);
2824 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2825 camel_folder_summary_unlock (summary);
2829 camel_db_end_transaction (cdb, NULL);
2830 camel_folder_summary_unlock (summary);
2836 * camel_folder_summary_header_save_to_db:
2841 camel_folder_summary_header_save_to_db (CamelFolderSummary *summary,
2844 CamelStore *parent_store;
2845 CamelFIRecord *record;
2849 if (is_in_memory_summary (summary))
2852 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2853 cdb = parent_store->cdb_w;
2854 camel_folder_summary_lock (summary);
2856 d (printf ("\ncamel_folder_summary_header_save_to_db called \n"));
2858 record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2860 camel_folder_summary_unlock (summary);
2864 camel_db_begin_transaction (cdb, NULL);
2865 ret = camel_db_write_folder_info_record (cdb, record, error);
2866 g_free (record->folder_name);
2867 g_free (record->bdata);
2871 camel_db_abort_transaction (cdb, NULL);
2872 camel_folder_summary_unlock (summary);
2876 camel_db_end_transaction (cdb, NULL);
2877 camel_folder_summary_unlock (summary);
2883 * camel_folder_summary_header_load_from_db:
2888 camel_folder_summary_header_load_from_db (CamelFolderSummary *summary,
2890 const gchar *folder_name,
2894 CamelFIRecord *record;
2895 gboolean ret = FALSE;
2897 d (printf ("\ncamel_folder_summary_header_load_from_db called \n"));
2899 if (is_in_memory_summary (summary))
2902 camel_folder_summary_lock (summary);
2903 camel_folder_summary_save_to_db (summary, NULL);
2907 record = g_new0 (CamelFIRecord, 1);
2908 camel_db_read_folder_info_record (cdb, folder_name, record, error);
2910 ret = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_from_db (summary, record);
2912 camel_folder_summary_unlock (summary);
2914 g_free (record->folder_name);
2915 g_free (record->bdata);
2922 summary_assign_uid (CamelFolderSummary *summary,
2923 CamelMessageInfo *info)
2926 CamelMessageInfo *mi;
2928 uid = camel_message_info_uid (info);
2930 if (uid == NULL || uid[0] == 0) {
2931 camel_pstring_free (info->uid);
2932 uid = info->uid = (gchar *) camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2935 camel_folder_summary_lock (summary);
2937 while ((mi = g_hash_table_lookup (summary->priv->loaded_infos, uid))) {
2938 camel_folder_summary_unlock (summary);
2943 d (printf ("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid (info)));
2945 camel_pstring_free (info->uid);
2946 uid = info->uid = camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2947 camel_message_info_set_flags (info, CAMEL_MESSAGE_FOLDER_FLAGGED, CAMEL_MESSAGE_FOLDER_FLAGGED);
2949 camel_folder_summary_lock (summary);
2952 camel_folder_summary_unlock (summary);
2958 * camel_folder_summary_add:
2959 * @summary: a #CamelFolderSummary object
2960 * @info: a #CamelMessageInfo
2962 * Adds a new @info record to the summary. If @info->uid is %NULL,
2963 * then a new uid is automatically re-assigned by calling
2964 * camel_folder_summary_next_uid_string().
2966 * The @info record should have been generated by calling one of the
2967 * info_new_*() functions, as it will be free'd based on the summary
2968 * class. And MUST NOT be allocated directly using malloc.
2971 camel_folder_summary_add (CamelFolderSummary *summary,
2972 CamelMessageInfo *info)
2974 CamelMessageInfoBase *base_info;
2976 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2981 camel_folder_summary_lock (summary);
2982 if (!summary_assign_uid (summary, info)) {
2983 camel_folder_summary_unlock (summary);
2987 base_info = (CamelMessageInfoBase *) info;
2988 folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
2989 base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
2990 base_info->dirty = TRUE;
2992 g_hash_table_insert (
2993 summary->priv->uids,
2994 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
2995 GUINT_TO_POINTER (camel_message_info_flags (info)));
2997 /* Summary always holds a ref for the loaded infos */
2998 g_hash_table_insert (summary->priv->loaded_infos, (gpointer) camel_message_info_uid (info), info);
3000 camel_folder_summary_touch (summary);
3002 camel_folder_summary_unlock (summary);
3006 * camel_folder_summary_insert:
3011 camel_folder_summary_insert (CamelFolderSummary *summary,
3012 CamelMessageInfo *info,
3015 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
3020 camel_folder_summary_lock (summary);
3023 CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
3025 folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
3026 base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3027 base_info->dirty = TRUE;
3029 g_hash_table_insert (
3030 summary->priv->uids,
3031 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
3032 GUINT_TO_POINTER (camel_message_info_flags (info)));
3034 camel_folder_summary_touch (summary);
3037 /* Summary always holds a ref for the loaded infos */
3038 g_hash_table_insert (summary->priv->loaded_infos, (gchar *) camel_message_info_uid (info), info);
3040 camel_folder_summary_unlock (summary);
3044 * camel_folder_summary_info_new_from_header:
3045 * @summary: a #CamelFolderSummary object
3046 * @headers: rfc822 headers
3048 * Create a new info record from a header.
3050 * Returns: the newly allocated record which must be unreferenced with
3051 * camel_message_info_unref()
3054 camel_folder_summary_info_new_from_header (CamelFolderSummary *summary,
3055 struct _camel_header_raw *h)
3057 CamelFolderSummaryClass *class;
3059 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
3061 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3062 g_return_val_if_fail (class->message_info_new_from_header != NULL, NULL);
3064 return class->message_info_new_from_header (summary, h);
3068 * camel_folder_summary_info_new_from_parser:
3069 * @summary: a #CamelFolderSummary object
3070 * @parser: a #CamelMimeParser object
3072 * Create a new info record from a parser. If the parser cannot
3073 * determine a uid, then none will be assigned.
3075 * If indexing is enabled, and the parser cannot determine a new uid, then
3076 * one is automatically assigned.
3078 * If indexing is enabled, then the content will be indexed based
3079 * on this new uid. In this case, the message info MUST be
3080 * added using :add().
3082 * Once complete, the parser will be positioned at the end of
3085 * Returns: the newly allocated record which must be unreferenced with
3086 * camel_message_info_unref()
3089 camel_folder_summary_info_new_from_parser (CamelFolderSummary *summary,
3090 CamelMimeParser *mp)
3092 CamelMessageInfo *info = NULL;
3095 CamelFolderSummaryPrivate *p = summary->priv;
3097 CamelIndexName *name = NULL;
3099 /* should this check the parser is in the right state, or assume it is?? */
3101 start = camel_mime_parser_tell (mp);
3102 if (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_EOF) {
3103 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_parser (summary, mp);
3105 camel_mime_parser_unstep (mp);
3107 /* assign a unique uid, this is slightly 'wrong' as we do not really
3108 * know if we are going to store this in the summary, but no matter */
3110 summary_assign_uid (summary, info);
3112 g_rec_mutex_lock (&summary->priv->filter_lock);
3115 if (p->filter_index == NULL)
3116 p->filter_index = camel_mime_filter_index_new (p->index);
3117 camel_index_delete_name (p->index, camel_message_info_uid (info));
3118 name = camel_index_add_name (p->index, camel_message_info_uid (info));
3119 camel_mime_filter_index_set_name (CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3122 /* always scan the content info, even if we dont save it */
3123 ((CamelMessageInfoBase *) info)->content = summary_build_content_info (summary, info, mp);
3125 if (name && p->index) {
3126 camel_index_write_name (p->index, name);
3127 g_object_unref (name);
3128 camel_mime_filter_index_set_name (
3129 CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3132 g_rec_mutex_unlock (&summary->priv->filter_lock);
3134 ((CamelMessageInfoBase *) info)->size = camel_mime_parser_tell (mp) - start;
3140 * camel_folder_summary_info_new_from_message:
3141 * @summary: a #CamelFolderSummary object
3142 * @message: a #CamelMimeMessage object
3143 * @bodystructure: a bodystructure or NULL
3145 * Create a summary item from a message.
3147 * Returns: the newly allocated record which must be unreferenced with
3148 * camel_message_info_unref()
3151 camel_folder_summary_info_new_from_message (CamelFolderSummary *summary,
3152 CamelMimeMessage *msg,
3153 const gchar *bodystructure)
3155 CamelMessageInfo *info;
3156 CamelFolderSummaryPrivate *p = summary->priv;
3157 CamelIndexName *name = NULL;
3159 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_message (summary, msg, bodystructure);
3161 /* assign a unique uid, this is slightly 'wrong' as we do not really
3162 * know if we are going to store this in the summary, but we need it set for indexing */
3164 summary_assign_uid (summary, info);
3166 g_rec_mutex_lock (&summary->priv->filter_lock);
3169 if (p->filter_index == NULL)
3170 p->filter_index = camel_mime_filter_index_new (p->index);
3171 camel_index_delete_name (p->index, camel_message_info_uid (info));
3172 name = camel_index_add_name (p->index, camel_message_info_uid (info));
3173 camel_mime_filter_index_set_name (
3174 CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3176 if (p->filter_stream == NULL) {
3177 CamelStream *null = camel_stream_null_new ();
3179 p->filter_stream = camel_stream_filter_new (null);
3180 g_object_unref (null);
3184 ((CamelMessageInfoBase *) info)->content = summary_build_content_info_message (summary, info, (CamelMimePart *) msg);
3187 camel_index_write_name (p->index, name);
3188 g_object_unref (name);
3189 camel_mime_filter_index_set_name (
3190 CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3193 g_rec_mutex_unlock (&summary->priv->filter_lock);
3199 * camel_folder_summary_content_info_free:
3200 * @summary: a #CamelFolderSummary object
3201 * @ci: a #CamelMessageContentInfo
3203 * Free the content info @ci, and all associated memory.
3206 camel_folder_summary_content_info_free (CamelFolderSummary *summary,
3207 CamelMessageContentInfo *ci)
3209 CamelMessageContentInfo *pw, *pn;
3212 CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_free (summary, ci);
3215 camel_folder_summary_content_info_free (summary, pw);
3221 * camel_folder_summary_touch:
3222 * @summary: a #CamelFolderSummary object
3224 * Mark the summary as changed, so that a save will force it to be
3225 * written back to disk.
3228 camel_folder_summary_touch (CamelFolderSummary *summary)
3230 camel_folder_summary_lock (summary);
3231 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
3232 camel_folder_summary_unlock (summary);
3236 * camel_folder_summary_clear:
3237 * @summary: a #CamelFolderSummary object
3239 * Empty the summary contents.
3242 camel_folder_summary_clear (CamelFolderSummary *summary,
3245 GObject *summary_object;
3246 CamelStore *parent_store;
3248 const gchar *folder_name;
3251 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3253 camel_folder_summary_lock (summary);
3254 if (camel_folder_summary_count (summary) == 0) {
3255 camel_folder_summary_unlock (summary);
3259 g_hash_table_remove_all (summary->priv->uids);
3260 remove_all_loaded (summary);
3261 g_hash_table_remove_all (summary->priv->loaded_infos);
3263 summary->priv->saved_count = 0;
3264 summary->priv->unread_count = 0;
3265 summary->priv->deleted_count = 0;
3266 summary->priv->junk_count = 0;
3267 summary->priv->junk_not_deleted_count = 0;
3268 summary->priv->visible_count = 0;
3270 camel_folder_summary_touch (summary);
3272 folder_name = camel_folder_get_full_name (summary->priv->folder);
3273 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3274 cdb = parent_store->cdb_w;
3276 if (!is_in_memory_summary (summary))
3277 res = camel_db_clear_folder_summary (cdb, folder_name, error) == 0;
3281 summary_object = G_OBJECT (summary);
3282 g_object_freeze_notify (summary_object);
3283 g_object_notify (summary_object, "saved-count");
3284 g_object_notify (summary_object, "unread-count");
3285 g_object_notify (summary_object, "deleted-count");
3286 g_object_notify (summary_object, "junk-count");
3287 g_object_notify (summary_object, "junk-not-deleted-count");
3288 g_object_notify (summary_object, "visible-count");
3289 g_object_thaw_notify (summary_object);
3291 camel_folder_summary_unlock (summary);
3297 * camel_folder_summary_remove:
3298 * @summary: a #CamelFolderSummary object
3299 * @info: a #CamelMessageInfo
3301 * Remove a specific @info record from the summary.
3303 * Returns: Whether the @info was found and removed from the @summary.
3306 camel_folder_summary_remove (CamelFolderSummary *summary,
3307 CamelMessageInfo *info)
3309 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3310 g_return_val_if_fail (info != NULL, FALSE);
3312 if (camel_folder_summary_remove_uid (summary, camel_message_info_uid (info))) {
3313 camel_message_info_unref (info);
3321 * camel_folder_summary_remove_uid:
3322 * @summary: a #CamelFolderSummary object
3325 * Remove a specific info record from the summary, by @uid.
3327 * Returns: Whether the @uid was found and removed from the @summary.
3330 camel_folder_summary_remove_uid (CamelFolderSummary *summary,
3333 gpointer ptr_uid = NULL, ptr_flags = NULL;
3334 CamelStore *parent_store;
3335 const gchar *full_name;
3336 const gchar *uid_copy;
3337 gboolean res = TRUE;
3339 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3340 g_return_val_if_fail (uid != NULL, FALSE);
3342 camel_folder_summary_lock (summary);
3343 if (!g_hash_table_lookup_extended (summary->priv->uids, uid, &ptr_uid, &ptr_flags)) {
3344 camel_folder_summary_unlock (summary);
3348 folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3350 uid_copy = camel_pstring_strdup (uid);
3351 g_hash_table_remove (summary->priv->uids, uid_copy);
3352 g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3354 if (!is_in_memory_summary (summary)) {
3355 full_name = camel_folder_get_full_name (summary->priv->folder);
3356 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3357 if (camel_db_delete_uid (parent_store->cdb_w, full_name, uid_copy, NULL) != 0)
3361 camel_pstring_free (uid_copy);
3363 camel_folder_summary_touch (summary);
3364 camel_folder_summary_unlock (summary);
3370 * camel_folder_summary_remove_uids:
3371 * @summary: a #CamelFolderSummary object
3372 * @uids: a GList of uids
3374 * Remove a specific info record from the summary, by @uid.
3376 * Returns: Whether the @uid was found and removed from the @summary.
3381 camel_folder_summary_remove_uids (CamelFolderSummary *summary,
3384 CamelStore *parent_store;
3385 const gchar *full_name;
3387 gboolean res = TRUE;
3389 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3390 g_return_val_if_fail (uids != NULL, FALSE);
3392 g_object_freeze_notify (G_OBJECT (summary));
3393 camel_folder_summary_lock (summary);
3395 for (l = g_list_first (uids); l; l = g_list_next (l)) {
3396 gpointer ptr_uid = NULL, ptr_flags = NULL;
3397 if (g_hash_table_lookup_extended (summary->priv->uids, l->data, &ptr_uid, &ptr_flags)) {
3398 const gchar *uid_copy = camel_pstring_strdup (l->data);
3399 CamelMessageInfo *mi;
3401 folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3402 g_hash_table_remove (summary->priv->uids, uid_copy);
3404 mi = g_hash_table_lookup (summary->priv->loaded_infos, uid_copy);
3405 g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3408 camel_message_info_unref (mi);
3409 camel_pstring_free (uid_copy);
3413 if (!is_in_memory_summary (summary)) {
3414 full_name = camel_folder_get_full_name (summary->priv->folder);
3415 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3416 if (camel_db_delete_uids (parent_store->cdb_w, full_name, uids, NULL) != 0)
3420 camel_folder_summary_touch (summary);
3421 camel_folder_summary_unlock (summary);
3422 g_object_thaw_notify (G_OBJECT (summary));
3427 static struct _node *
3428 my_list_append (struct _node **list,
3431 struct _node *ln = *list;
3446 my_list_size (struct _node **list)
3449 struct _node *ln = (struct _node *) list;
3457 /* are these even useful for anything??? */
3458 static CamelMessageInfo *
3459 message_info_new_from_parser (CamelFolderSummary *summary,
3460 CamelMimeParser *mp)
3462 CamelMessageInfo *mi = NULL;
3465 state = camel_mime_parser_state (mp);
3467 case CAMEL_MIME_PARSER_STATE_HEADER:
3468 case CAMEL_MIME_PARSER_STATE_MESSAGE:
3469 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3470 mi = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3473 g_error ("Invalid parser state");
3479 static CamelMessageContentInfo *
3480 content_info_new_from_parser (CamelFolderSummary *summary,
3481 CamelMimeParser *mp)
3483 CamelMessageContentInfo *ci = NULL;
3485 switch (camel_mime_parser_state (mp)) {
3486 case CAMEL_MIME_PARSER_STATE_HEADER:
3487 case CAMEL_MIME_PARSER_STATE_MESSAGE:
3488 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3489 ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3492 camel_content_type_unref (ci->type);
3493 ci->type = camel_mime_parser_content_type (mp);
3494 camel_content_type_ref (ci->type);
3498 g_error ("Invalid parser state");
3504 static CamelMessageInfo *
3505 message_info_new_from_message (CamelFolderSummary *summary,
3506 CamelMimeMessage *msg,
3507 const gchar *bodystructure)
3509 CamelMessageInfo *mi;
3511 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->message_info_new_from_header (summary, ((CamelMimePart *) msg)->headers);
3512 ((CamelMessageInfoBase *) mi)->bodystructure = g_strdup (bodystructure);
3517 static CamelMessageContentInfo *
3518 content_info_new_from_message (CamelFolderSummary *summary,
3521 CamelMessageContentInfo *ci;
3523 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->content_info_new_from_header (summary, mp->headers);
3529 summary_format_address (struct _camel_header_raw *h,
3531 const gchar *charset)
3533 struct _camel_header_address *addr;
3536 if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3539 while (isspace ((unsigned) *text))
3542 text = camel_header_unfold (text);
3544 if ((addr = camel_header_address_decode (text, charset))) {
3545 str = camel_header_address_list_format (addr);
3546 camel_header_address_list_clear (&addr);
3556 summary_format_string (struct _camel_header_raw *h,
3558 const gchar *charset)
3562 if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3565 while (isspace ((unsigned) *text))
3568 text = camel_header_unfold (text);
3569 str = camel_header_decode_string (text, charset);
3576 * camel_folder_summary_content_info_new:
3577 * @summary: a #CamelFolderSummary object
3579 * Allocate a new #CamelMessageContentInfo, suitable for adding
3582 * Returns: a newly allocated #CamelMessageContentInfo
3584 CamelMessageContentInfo *
3585 camel_folder_summary_content_info_new (CamelFolderSummary *summary)
3587 CamelFolderSummaryClass *class;
3589 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3590 g_return_val_if_fail (class->content_info_size > 0, NULL);
3592 return g_slice_alloc0 (class->content_info_size);
3595 static CamelMessageInfo *
3596 message_info_new_from_header (CamelFolderSummary *summary,
3597 struct _camel_header_raw *h)
3599 const gchar *received, *date, *content, *charset = NULL;
3600 struct _camel_header_references *refs, *irt, *scan;
3601 gchar *subject, *from, *to, *cc, *mlist;
3602 CamelContentType *ct = NULL;
3603 CamelMessageInfoBase *mi;
3609 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
3610 digest = g_alloca (length);
3612 mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
3614 if ((content = camel_header_raw_find (&h, "Content-Type", NULL))
3615 && (ct = camel_content_type_decode (content))
3616 && (charset = camel_content_type_param (ct, "charset"))
3617 && (g_ascii_strcasecmp (charset, "us-ascii") == 0))
3620 charset = charset ? camel_iconv_charset_name (charset) : NULL;
3622 subject = summary_format_string (h, "subject", charset);
3623 from = summary_format_address (h, "from", charset);
3624 to = summary_format_address (h, "to", charset);
3625 cc = summary_format_address (h, "cc", charset);
3626 mlist = camel_header_raw_check_mailing_list (&h);
3629 camel_content_type_unref (ct);
3631 mi->subject = camel_pstring_add (subject, TRUE);
3632 mi->from = camel_pstring_add (from, TRUE);
3633 mi->to = camel_pstring_add (to, TRUE);
3634 mi->cc = camel_pstring_add (cc, TRUE);
3635 mi->mlist = camel_pstring_add (mlist, TRUE);
3637 mi->user_flags = NULL;
3638 mi->user_tags = NULL;
3640 if ((date = camel_header_raw_find (&h, "date", NULL)))
3641 mi->date_sent = camel_header_decode_date (date, NULL);
3645 received = camel_header_raw_find (&h, "received", NULL);
3647 received = strrchr (received, ';');
3649 mi->date_received = camel_header_decode_date (received + 1, NULL);
3651 mi->date_received = 0;
3653 msgid = camel_header_msgid_decode (camel_header_raw_find (&h, "message-id", NULL));
3655 GChecksum *checksum;
3657 checksum = g_checksum_new (G_CHECKSUM_MD5);
3658 g_checksum_update (checksum, (guchar *) msgid, -1);
3659 g_checksum_get_digest (checksum, digest, &length);
3660 g_checksum_free (checksum);
3662 memcpy (mi->message_id.id.hash, digest, sizeof (mi->message_id.id.hash));
3666 /* decode our references and in-reply-to headers */
3667 refs = camel_header_references_decode (camel_header_raw_find (&h, "references", NULL));
3668 irt = camel_header_references_inreplyto_decode (camel_header_raw_find (&h, "in-reply-to", NULL));
3671 /* The References field is populated from the "References" and/or "In-Reply-To"
3672 * headers. If both headers exist, take the first thing in the In-Reply-To header
3673 * that looks like a Message-ID, and append it to the References header. */
3681 count = camel_header_references_list_size (&refs);
3682 mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
3686 GChecksum *checksum;
3688 checksum = g_checksum_new (G_CHECKSUM_MD5);
3689 g_checksum_update (checksum, (guchar *) scan->id, -1);
3690 g_checksum_get_digest (checksum, digest, &length);
3691 g_checksum_free (checksum);
3693 memcpy (mi->references->references[count].id.hash, digest, sizeof (mi->message_id.id.hash));
3697 mi->references->size = count;
3698 camel_header_references_list_clear (&refs);
3701 return (CamelMessageInfo *) mi;
3705 message_info_free (CamelFolderSummary *summary,
3706 CamelMessageInfo *info)
3708 CamelFolderSummaryClass *class;
3709 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
3713 camel_folder_summary_lock (summary);
3714 if (g_hash_table_lookup (summary->priv->loaded_infos, mi->uid) == mi) {
3715 g_hash_table_remove (summary->priv->loaded_infos, mi->uid);
3717 camel_folder_summary_unlock (summary);
3719 camel_pstring_free (mi->uid);
3721 camel_pstring_free (mi->subject);
3722 camel_pstring_free (mi->from);
3723 camel_pstring_free (mi->to);
3724 camel_pstring_free (mi->cc);
3725 camel_pstring_free (mi->mlist);
3726 g_free (mi->bodystructure);
3727 g_free (mi->references);
3728 g_free (mi->preview);
3729 camel_flag_list_free (&mi->user_flags);
3730 camel_tag_list_free (&mi->user_tags);
3732 camel_header_param_list_free (mi->headers);
3735 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3736 g_slice_free1 (class->message_info_size, mi);
3738 g_slice_free (CamelMessageInfoBase, mi);
3741 static CamelMessageContentInfo *
3742 content_info_new_from_header (CamelFolderSummary *summary,
3743 struct _camel_header_raw *h)
3745 CamelMessageContentInfo *ci;
3746 const gchar *charset;
3748 ci = camel_folder_summary_content_info_new (summary);
3750 charset = camel_iconv_locale_charset ();
3751 ci->id = camel_header_msgid_decode (camel_header_raw_find (&h, "content-id", NULL));
3752 ci->description = camel_header_decode_string (camel_header_raw_find (&h, "content-description", NULL), charset);
3753 ci->encoding = camel_content_transfer_encoding_decode (camel_header_raw_find (&h, "content-transfer-encoding", NULL));
3754 ci->type = camel_content_type_decode (camel_header_raw_find (&h, "content-type", NULL));
3760 content_info_free (CamelFolderSummary *summary,
3761 CamelMessageContentInfo *ci)
3763 CamelFolderSummaryClass *class;
3765 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3767 camel_content_type_unref (ci->type);
3769 g_free (ci->description);
3770 g_free (ci->encoding);
3771 g_slice_free1 (class->content_info_size, ci);
3775 next_uid_string (CamelFolderSummary *summary)
3777 return g_strdup_printf ("%u", camel_folder_summary_next_uid (summary));
3782 Now this is where all the "smarts" happen, where the content info is built,
3783 and any indexing and what not is performed
3786 /* must have filter_lock before calling this function */
3787 static CamelMessageContentInfo *
3788 summary_build_content_info (CamelFolderSummary *summary,
3789 CamelMessageInfo *msginfo,
3790 CamelMimeParser *mp)
3795 CamelMessageContentInfo *info = NULL;
3796 CamelContentType *ct;
3797 gint enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
3798 CamelFolderSummaryPrivate *p = summary->priv;
3799 CamelMimeFilter *mfc;
3800 CamelMessageContentInfo *part;
3801 const gchar *calendar_header;
3803 d (printf ("building content info\n"));
3805 /* start of this part */
3806 state = camel_mime_parser_step (mp, &buffer, &len);
3808 if (summary->priv->build_content)
3809 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_parser (summary, mp);
3812 case CAMEL_MIME_PARSER_STATE_HEADER:
3813 /* check content type for indexing, then read body */
3814 ct = camel_mime_parser_content_type (mp);
3815 /* update attachments flag as we go */
3816 if (camel_content_type_is (ct, "application", "pgp-signature")
3818 || camel_content_type_is (ct, "application", "x-pkcs7-signature")
3819 || camel_content_type_is (ct, "application", "pkcs7-signature")
3822 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3824 calendar_header = camel_mime_parser_header (mp, "Content-class", NULL);
3825 if (calendar_header && g_ascii_strcasecmp (calendar_header, "urn:content-classes:calendarmessage") != 0)
3826 calendar_header = NULL;
3828 if (!calendar_header)
3829 calendar_header = camel_mime_parser_header (mp, "X-Calendar-Attachment", NULL);
3831 if (calendar_header || camel_content_type_is (ct, "text", "calendar"))
3832 camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
3834 if (p->index && camel_content_type_is (ct, "text", "*")) {
3836 const gchar *charset;
3838 d (printf ("generating index:\n"));
3840 encoding = camel_content_transfer_encoding_decode (camel_mime_parser_header (mp, "content-transfer-encoding", NULL));
3842 if (!g_ascii_strcasecmp (encoding, "base64")) {
3843 d (printf (" decoding base64\n"));
3844 if (p->filter_64 == NULL)
3845 p->filter_64 = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
3847 camel_mime_filter_reset (p->filter_64);
3848 enc_id = camel_mime_parser_filter_add (mp, p->filter_64);
3849 } else if (!g_ascii_strcasecmp (encoding, "quoted-printable")) {
3850 d (printf (" decoding quoted-printable\n"));
3851 if (p->filter_qp == NULL)
3852 p->filter_qp = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_DEC);
3854 camel_mime_filter_reset (p->filter_qp);
3855 enc_id = camel_mime_parser_filter_add (mp, p->filter_qp);
3856 } else if (!g_ascii_strcasecmp (encoding, "x-uuencode")) {
3857 d (printf (" decoding x-uuencode\n"));
3858 if (p->filter_uu == NULL)
3859 p->filter_uu = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_UU_DEC);
3861 camel_mime_filter_reset (p->filter_uu);
3862 enc_id = camel_mime_parser_filter_add (mp, p->filter_uu);
3864 d (printf (" ignoring encoding %s\n", encoding));
3869 charset = camel_content_type_param (ct, "charset");
3871 && !(g_ascii_strcasecmp (charset, "us-ascii") == 0
3872 || g_ascii_strcasecmp (charset, "utf-8") == 0)) {
3873 d (printf (" Adding conversion filter from %s to UTF-8\n", charset));
3874 mfc = g_hash_table_lookup (p->filter_charset, charset);
3876 mfc = camel_mime_filter_charset_new (charset, "UTF-8");
3878 g_hash_table_insert (p->filter_charset, g_strdup (charset), mfc);
3880 camel_mime_filter_reset ((CamelMimeFilter *) mfc);
3883 chr_id = camel_mime_parser_filter_add (mp, mfc);
3885 w (g_warning ("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset));
3889 /* we do charset conversions before this filter, which isn't strictly correct,
3890 * but works in most cases */
3891 if (camel_content_type_is (ct, "text", "html")) {
3892 if (p->filter_html == NULL)
3893 p->filter_html = camel_mime_filter_html_new ();
3895 camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
3896 html_id = camel_mime_parser_filter_add (mp, (CamelMimeFilter *) p->filter_html);
3899 /* and this filter actually does the indexing */
3900 idx_id = camel_mime_parser_filter_add (mp, p->filter_index);
3902 /* and scan/index everything */
3903 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
3905 /* and remove the filters */
3906 camel_mime_parser_filter_remove (mp, enc_id);
3907 camel_mime_parser_filter_remove (mp, chr_id);
3908 camel_mime_parser_filter_remove (mp, html_id);
3909 camel_mime_parser_filter_remove (mp, idx_id);
3911 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3912 d (printf ("Summarising multipart\n"));
3913 /* update attachments flag as we go */
3914 ct = camel_mime_parser_content_type (mp);
3915 if (camel_content_type_is (ct, "multipart", "mixed"))
3916 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3917 if (camel_content_type_is (ct, "multipart", "signed")
3918 || camel_content_type_is (ct, "multipart", "encrypted"))
3919 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3921 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
3922 camel_mime_parser_unstep (mp);
3923 part = summary_build_content_info (summary, msginfo, mp);
3925 part->parent = info;
3926 my_list_append ((struct _node **) &info->childs, (struct _node *) part);
3930 case CAMEL_MIME_PARSER_STATE_MESSAGE:
3931 d (printf ("Summarising message\n"));
3932 /* update attachments flag as we go */
3933 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3935 part = summary_build_content_info (summary, msginfo, mp);
3937 part->parent = info;
3938 my_list_append ((struct _node **) &info->childs, (struct _node *) part);
3940 state = camel_mime_parser_step (mp, &buffer, &len);
3941 if (state != CAMEL_MIME_PARSER_STATE_MESSAGE_END) {
3942 g_error ("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
3943 camel_mime_parser_unstep (mp);
3948 d (printf ("finished building content info\n"));
3953 /* build the content-info, from a message */
3954 /* this needs the filter lock since it uses filters to perform indexing */
3955 static CamelMessageContentInfo *
3956 summary_build_content_info_message (CamelFolderSummary *summary,
3957 CamelMessageInfo *msginfo,
3958 CamelMimePart *object)
3960 CamelDataWrapper *containee;
3962 CamelFolderSummaryPrivate *p = summary->priv;
3963 CamelMessageContentInfo *info = NULL, *child;
3964 CamelContentType *ct;
3965 const struct _camel_header_raw *header;
3967 if (summary->priv->build_content)
3968 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_message (summary, object);
3970 containee = camel_medium_get_content (CAMEL_MEDIUM (object));
3972 if (containee == NULL)
3975 /* TODO: I find it odd that get_part and get_content do not
3976 * add a reference, probably need fixing for multithreading */
3978 /* check for attachments */
3979 ct = ((CamelDataWrapper *) containee)->mime_type;
3980 if (camel_content_type_is (ct, "multipart", "*")) {
3981 if (camel_content_type_is (ct, "multipart", "mixed"))
3982 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3983 if (camel_content_type_is (ct, "multipart", "signed")
3984 || camel_content_type_is (ct, "multipart", "encrypted"))
3985 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3986 } else if (camel_content_type_is (ct, "application", "pgp-signature")
3988 || camel_content_type_is (ct, "application", "x-pkcs7-signature")
3989 || camel_content_type_is (ct, "application", "pkcs7-signature")
3992 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3995 for (header = object->headers; header; header = header->next) {
3996 const gchar *value = header->value;
3998 /* skip preceding spaces in the value */
3999 while (value && *value && isspace (*value))
4002 if (header->name && value && (
4003 (g_ascii_strcasecmp (header->name, "Content-class") == 0 && g_ascii_strcasecmp (value, "urn:content-classes:calendarmessage") == 0) ||
4004 (g_ascii_strcasecmp (header->name, "X-Calendar-Attachment") == 0)))
4008 if (header || camel_content_type_is (ct, "text", "calendar"))
4009 camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
4011 /* using the object types is more accurate than using the mime/types */
4012 if (CAMEL_IS_MULTIPART (containee)) {
4013 parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
4015 for (i = 0; i < parts; i++) {
4016 CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
4018 child = summary_build_content_info_message (summary, msginfo, part);
4020 child->parent = info;
4021 my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4024 } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
4025 /* for messages we only look at its contents */
4026 child = summary_build_content_info_message (summary, msginfo, (CamelMimePart *) containee);
4028 child->parent = info;
4029 my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4031 } else if (p->filter_stream
4032 && camel_content_type_is (ct, "text", "*")) {
4033 gint html_id = -1, idx_id = -1;
4035 /* pre-attach html filter if required, otherwise just index filter */
4036 if (camel_content_type_is (ct, "text", "html")) {
4037 if (p->filter_html == NULL)
4038 p->filter_html = camel_mime_filter_html_new ();
4040 camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
4041 html_id = camel_stream_filter_add (
4042 CAMEL_STREAM_FILTER (p->filter_stream),
4043 (CamelMimeFilter *) p->filter_html);
4045 idx_id = camel_stream_filter_add (
4046 CAMEL_STREAM_FILTER (p->filter_stream),
4049 /* FIXME Pass a GCancellable and GError here. */
4050 camel_data_wrapper_decode_to_stream_sync (
4051 containee, p->filter_stream, NULL, NULL);
4052 camel_stream_flush (p->filter_stream, NULL, NULL);
4054 camel_stream_filter_remove (
4055 CAMEL_STREAM_FILTER (p->filter_stream), idx_id);
4056 camel_stream_filter_remove (
4057 CAMEL_STREAM_FILTER (p->filter_stream), html_id);
4065 * @list: the address of a #CamelFlag list
4066 * @name: name of the flag to get
4068 * Find the state of the flag @name in @list.
4070 * Returns: the state of the flag (%TRUE or %FALSE)
4073 camel_flag_get (CamelFlag **list,
4079 if (!strcmp (flag->name, name))
4088 * @list: the address of a #CamelFlag list
4089 * @name: name of the flag to set or change
4090 * @value: the value to set on the flag
4092 * Set the state of a flag @name in the list @list to @value.
4094 * Returns: %TRUE if the value of the flag has been changed or %FALSE
4098 camel_flag_set (CamelFlag **list,
4102 CamelFlag *flag, *tmp;
4108 /* this 'trick' works because flag->next is the first element */
4109 flag = (CamelFlag *) list;
4110 while (flag->next) {
4112 if (!strcmp (flag->next->name, name)) {
4114 flag->next = tmp->next;
4123 tmp_len = sizeof (*tmp) + strlen (name);
4124 tmp = g_malloc (tmp_len);
4125 g_strlcpy (tmp->name, name, strlen (name) + 1);
4133 * camel_flag_list_size:
4134 * @list: the address of a #CamelFlag list
4136 * Get the length of the flag list.
4138 * Returns: the number of flags in the list
4141 camel_flag_list_size (CamelFlag **list)
4155 * camel_flag_list_free:
4156 * @list: the address of a #CamelFlag list
4158 * Free the memory associated with the flag list @list.
4161 camel_flag_list_free (CamelFlag **list)
4163 CamelFlag *flag, *tmp;
4174 * camel_flag_list_copy:
4175 * @to: the address of the #CamelFlag list to copy to
4176 * @from: the address of the #CamelFlag list to copy from
4180 * Returns: %TRUE if @to is changed or %FALSE otherwise
4183 camel_flag_list_copy (CamelFlag **to,
4186 CamelFlag *flag, *tmp;
4187 gboolean changed = FALSE;
4189 if (*to == NULL && from == NULL)
4192 /* Remove any now-missing flags */
4193 flag = (CamelFlag *) to;
4194 while (flag->next) {
4196 if (!camel_flag_get (from, tmp->name)) {
4199 flag->next = tmp->next;
4206 /* Add any new non-empty flags */
4210 changed |= camel_flag_set (to, flag->name, TRUE);
4219 * @list: the address of a #CamelTag list
4220 * @name: name of the tag to get
4222 * Find the flag @name in @list and get the value.
4224 * Returns: the value of the flag or %NULL if unset
4227 camel_tag_get (CamelTag **list,
4234 if (!strcmp (tag->name, name))
4235 return (const gchar *) tag->value;
4243 * @list: the address of a #CamelTag list
4244 * @name: name of the tag to set
4245 * @value: value to set on the tag
4247 * Set the tag @name in the tag list @list to @value.
4249 * Returns: %TRUE if the value on the tag changed or %FALSE otherwise
4252 camel_tag_set (CamelTag **list,
4256 CamelTag *tag, *tmp;
4258 /* this 'trick' works because tag->next is the first element */
4259 tag = (CamelTag *) list;
4262 if (!strcmp (tmp->name, name)) {
4263 if (value == NULL) { /* clear it? */
4264 tag->next = tmp->next;
4265 g_free (tmp->value);
4268 } else if (strcmp (tmp->value, value)) { /* has it changed? */
4269 g_free (tmp->value);
4270 tmp->value = g_strdup (value);
4279 tmp = g_malloc (sizeof (*tmp) + strlen (name));
4280 g_strlcpy (tmp->name, name, strlen (name) + 1);
4281 tmp->value = g_strdup (value);
4290 * camel_tag_list_size:
4291 * @list: the address of a #CamelTag list
4293 * Get the number of tags present in the tag list @list.
4295 * Returns: the number of tags
4298 camel_tag_list_size (CamelTag **list)
4312 rem_tag (gchar *key,
4316 camel_tag_set (to, key, NULL);
4320 * camel_tag_list_copy:
4321 * @to: the address of the #CamelTag list to copy to
4322 * @from: the address of the #CamelTag list to copy from
4326 * Returns: %TRUE if @to is changed or %FALSE otherwise
4329 camel_tag_list_copy (CamelTag **to,
4332 gint changed = FALSE;
4336 if (*to == NULL && from == NULL)
4339 left = g_hash_table_new (g_str_hash, g_str_equal);
4342 g_hash_table_insert (left, tag->name, tag);
4348 changed |= camel_tag_set (to, tag->name, tag->value);
4349 g_hash_table_remove (left, tag->name);
4353 if (g_hash_table_size (left) > 0) {
4354 g_hash_table_foreach (left, (GHFunc) rem_tag, to);
4357 g_hash_table_destroy (left);
4363 * camel_tag_list_free:
4364 * @list: the address of a #CamelTag list
4366 * Free the tag list @list.
4369 camel_tag_list_free (CamelTag **list)
4371 CamelTag *tag, *tmp;
4375 g_free (tag->value);
4382 static struct flag_names_t {
4386 { "answered", CAMEL_MESSAGE_ANSWERED },
4387 { "deleted", CAMEL_MESSAGE_DELETED },
4388 { "draft", CAMEL_MESSAGE_DRAFT },
4389 { "flagged", CAMEL_MESSAGE_FLAGGED },
4390 { "seen", CAMEL_MESSAGE_SEEN },
4391 { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
4392 { "junk", CAMEL_MESSAGE_JUNK },
4393 { "notjunk", CAMEL_MESSAGE_NOTJUNK },
4394 { "secure", CAMEL_MESSAGE_SECURE },
4399 * camel_system_flag:
4400 * @name: name of a system flag
4402 * Returns: the integer value of the system flag string
4405 camel_system_flag (const gchar *name)
4407 struct flag_names_t *flag;
4409 g_return_val_if_fail (name != NULL, 0);
4411 for (flag = flag_names; flag->name; flag++)
4412 if (!g_ascii_strcasecmp (name, flag->name))
4419 * camel_system_flag_get:
4420 * @flags: bitwise system flags
4421 * @name: name of the flag to check for
4423 * Find the state of the flag @name in @flags.
4425 * Returns: %TRUE if the named flag is set or %FALSE otherwise
4428 camel_system_flag_get (CamelMessageFlags flags,
4431 g_return_val_if_fail (name != NULL, FALSE);
4433 return flags & camel_system_flag (name);
4437 * camel_message_info_new:
4438 * @summary: a #CamelFolderSummary object or %NULL
4440 * Create a new #CamelMessageInfo.
4442 * Returns: a new #CamelMessageInfo
4445 camel_message_info_new (CamelFolderSummary *summary)
4447 CamelFolderSummaryClass *class;
4448 CamelMessageInfo *info;
4449 gsize message_info_size;
4451 if (summary != NULL) {
4452 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
4453 g_return_val_if_fail (class->message_info_size > 0, NULL);
4454 message_info_size = class->message_info_size;
4456 message_info_size = sizeof (CamelMessageInfoBase);
4459 info = g_slice_alloc0 (message_info_size);
4461 info->summary = summary;
4463 /* We assume that mi is always dirty unless freshly read or just saved*/
4464 ((CamelMessageInfoBase *) info)->dirty = TRUE;
4470 * camel_message_info_ref:
4471 * @info: a #CamelMessageInfo
4473 * Reference an info.
4476 camel_message_info_ref (gpointer o)
4478 CamelMessageInfo *mi = o;
4480 g_return_val_if_fail (mi != NULL, NULL);
4481 g_return_val_if_fail (mi->refcount > 0, NULL);
4483 g_atomic_int_inc (&mi->refcount);
4489 * camel_message_info_new_from_header:
4490 * @summary: a #CamelFolderSummary object or %NULL
4491 * @header: raw header
4493 * Create a new #CamelMessageInfo pre-populated with info from
4496 * Returns: a new #CamelMessageInfo
4499 camel_message_info_new_from_header (CamelFolderSummary *summary,
4500 struct _camel_header_raw *header)
4502 if (summary != NULL)
4503 return CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->
4504 message_info_new_from_header (summary, header);
4506 return message_info_new_from_header (NULL, header);
4510 * camel_message_info_unref:
4511 * @info: a #CamelMessageInfo
4513 * Unref's and potentially frees a #CamelMessageInfo and its contents.
4516 camel_message_info_unref (gpointer o)
4518 CamelMessageInfo *mi = o;
4520 g_return_if_fail (mi != NULL);
4521 g_return_if_fail (mi->refcount > 0);
4523 if (g_atomic_int_dec_and_test (&mi->refcount)) {
4524 if (mi->summary != NULL) {
4525 CamelFolderSummaryClass *class;
4527 /* FIXME This is kinda busted, should really
4528 * be handled by message_info_free(). */
4529 if (mi->summary->priv->build_content
4530 && ((CamelMessageInfoBase *) mi)->content) {
4531 camel_folder_summary_content_info_free (
4533 ((CamelMessageInfoBase *) mi)->content);
4534 ((CamelMessageInfoBase *) mi)->content = NULL;
4537 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary);
4538 g_return_if_fail (class->message_info_free != NULL);
4540 class->message_info_free (mi->summary, mi);
4542 message_info_free (NULL, mi);
4548 * camel_message_info_clone:
4549 * @info: a #CamelMessageInfo
4551 * Duplicate a #CamelMessageInfo.
4553 * Returns: the duplicated #CamelMessageInfo
4556 camel_message_info_clone (gconstpointer o)
4558 const CamelMessageInfo *mi = o;
4561 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->message_info_clone (mi->summary, mi);
4563 return message_info_clone (NULL, mi);
4567 * camel_message_info_ptr:
4568 * @info: a #CamelMessageInfo
4571 * Generic accessor method for getting pointer data.
4573 * Returns: the pointer data
4576 camel_message_info_ptr (const CamelMessageInfo *info,
4580 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_ptr (info, id);
4582 return info_ptr (info, id);
4586 * camel_message_info_uint32:
4587 * @info: a #CamelMessageInfo
4590 * Generic accessor method for getting 32bit gint data.
4592 * Returns: the gint data
4595 camel_message_info_uint32 (const CamelMessageInfo *info,
4599 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_uint32 (info, id);
4601 return info_uint32 (info, id);
4605 * camel_message_info_time:
4606 * @info: a #CamelMessageInfo
4609 * Generic accessor method for getting time_t data.
4611 * Returns: the time_t data
4614 camel_message_info_time (const CamelMessageInfo *info,
4618 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_time (info, id);
4620 return info_time (info, id);
4624 * camel_message_info_user_flag:
4625 * @info: a #CamelMessageInfo
4626 * @id: user flag to get
4628 * Get the state of a user flag named @id.
4630 * Returns: the state of the user flag
4633 camel_message_info_user_flag (const CamelMessageInfo *info,
4637 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_user_flag (info, id);
4639 return info_user_flag (info, id);
4643 * camel_message_info_user_tag:
4644 * @info: a #CamelMessageInfo
4645 * @id: user tag to get
4647 * Get the value of a user tag named @id.
4649 * Returns: the value of the user tag
4652 camel_message_info_user_tag (const CamelMessageInfo *info,
4656 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_user_tag (info, id);
4658 return info_user_tag (info, id);
4662 * camel_message_info_set_flags:
4663 * @info: a #CamelMessageInfo
4664 * @flags: mask of flags to change
4665 * @set: state the flags should be changed to
4667 * Change the state of the system flags on the #CamelMessageInfo
4669 * Returns: %TRUE if any of the flags changed or %FALSE otherwise
4672 camel_message_info_set_flags (CamelMessageInfo *info,
4673 CamelMessageFlags flags,
4677 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_flags (info, flags, set);
4679 return info_set_flags (info, flags, set);
4683 * camel_message_info_set_user_flag:
4684 * @info: a #CamelMessageInfo
4685 * @id: name of the user flag to set
4686 * @state: state to set the flag to
4688 * Set the state of a user flag on a #CamelMessageInfo.
4690 * Returns: %TRUE if the state changed or %FALSE otherwise
4693 camel_message_info_set_user_flag (CamelMessageInfo *info,
4698 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_user_flag (info, id, state);
4700 return info_set_user_flag (info, id, state);
4704 * camel_message_info_set_user_tag:
4705 * @info: a #CamelMessageInfo
4706 * @id: name of the user tag to set
4707 * @val: value to set
4709 * Set the value of a user tag on a #CamelMessageInfo.
4711 * Returns: %TRUE if the value changed or %FALSE otherwise
4714 camel_message_info_set_user_tag (CamelMessageInfo *info,
4719 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_user_tag (info, id, val);
4721 return info_set_user_tag (info, id, val);
4725 camel_content_info_dump (CamelMessageContentInfo *ci,
4730 p = alloca (depth * 4 + 1);
4731 memset (p, ' ', depth * 4);
4735 printf ("%s<empty>\n", p);
4741 "%scontent-type: %s/%s\n",
4742 p, ci->type->type ? ci->type->type : "(null)",
4743 ci->type->subtype ? ci->type->subtype : "(null)");
4745 printf ("%scontent-type: <unset>\n", p);
4747 "%scontent-transfer-encoding: %s\n",
4748 p, ci->encoding ? ci->encoding : "(null)");
4750 "%scontent-description: %s\n",
4751 p, ci->description ? ci->description : "(null)");
4752 printf ("%ssize: %lu\n", p, (gulong) ci->size);
4755 camel_content_info_dump (ci, depth + 1);
4761 camel_message_info_dump (CamelMessageInfo *info)
4764 printf ("No message?\n");
4768 printf ("Subject: %s\n", camel_message_info_subject (info));
4769 printf ("To: %s\n", camel_message_info_to (info));
4770 printf ("Cc: %s\n", camel_message_info_cc (info));
4771 printf ("mailing list: %s\n", camel_message_info_mlist (info));
4772 printf ("From: %s\n", camel_message_info_from (info));
4773 printf ("UID: %s\n", camel_message_info_uid (info));
4774 printf ("Flags: %04x\n", camel_message_info_flags (info));
4775 camel_content_info_dump (((CamelMessageInfoBase *) info)->content, 0);
4779 * camel_folder_summary_lock:
4780 * @summary: a #CamelFolderSummary
4782 * Locks @summary. Unlock it with camel_folder_summary_unlock().
4787 camel_folder_summary_lock (CamelFolderSummary *summary)
4789 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4791 g_rec_mutex_lock (&summary->priv->summary_lock);
4795 * camel_folder_summary_unlock:
4796 * @summary: a #CamelFolderSummary
4798 * Unlocks @summary, previously locked with camel_folder_summary_lock().
4803 camel_folder_summary_unlock (CamelFolderSummary *summary)
4805 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4807 g_rec_mutex_unlock (&summary->priv->summary_lock);
4811 bdata_extract_digit (/* const */ gchar **part)
4813 if (!part || !*part || !**part)
4822 return strtoul (*part, part, 10);
4825 /* expecting "digit-value", where digit is length of the value */
4827 bdata_extract_string (/* const */ gchar **part)
4832 len = bdata_extract_digit (part);
4834 /* might be a '-' sign */
4835 if (part && *part && **part)
4838 if (len <= 0 || !part || !*part || !**part)
4839 return g_strdup ("");
4842 return g_strdup ("");
4844 has_len = strlen (*part);
4848 val = g_strndup (*part, len);