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 program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
29 #include <sys/types.h>
35 #include <glib/gi18n-lib.h>
36 #include <glib/gstdio.h>
39 #include "camel-debug.h"
40 #include "camel-file-utils.h"
41 #include "camel-folder-summary.h"
42 #include "camel-folder.h"
43 #include "camel-iconv.h"
44 #include "camel-mime-filter-basic.h"
45 #include "camel-mime-filter-charset.h"
46 #include "camel-mime-filter-html.h"
47 #include "camel-mime-filter-index.h"
48 #include "camel-mime-filter.h"
49 #include "camel-mime-message.h"
50 #include "camel-multipart.h"
51 #include "camel-session.h"
52 #include "camel-stream-filter.h"
53 #include "camel-stream-mem.h"
54 #include "camel-stream-null.h"
55 #include "camel-string-utils.h"
56 #include "camel-store.h"
57 #include "camel-vee-folder.h"
58 #include "camel-vtrash-folder.h"
59 #include "camel-mime-part-utils.h"
61 #define CAMEL_FOLDER_SUMMARY_GET_PRIVATE(obj) \
62 (G_TYPE_INSTANCE_GET_PRIVATE \
63 ((obj), CAMEL_TYPE_FOLDER_SUMMARY, CamelFolderSummaryPrivate))
65 /* Make 5 minutes as default cache drop */
66 #define SUMMARY_CACHE_DROP 300
67 #define dd(x) if (camel_debug("sync")) x
69 struct _CamelFolderSummaryPrivate {
70 GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */
72 struct _CamelMimeFilter *filter_index;
73 struct _CamelMimeFilter *filter_64;
74 struct _CamelMimeFilter *filter_qp;
75 struct _CamelMimeFilter *filter_uu;
76 struct _CamelMimeFilter *filter_save;
77 struct _CamelMimeFilter *filter_html;
79 struct _CamelStream *filter_stream;
81 struct _CamelIndex *index;
83 GStaticRecMutex summary_lock; /* for the summary hashtable/array */
84 GStaticRecMutex io_lock; /* load/save lock, for access to saved_count, etc */
85 GStaticRecMutex filter_lock; /* for accessing any of the filtering/indexing stuff, since we share them */
86 GStaticRecMutex alloc_lock; /* for setting up and using allocators */
87 GStaticRecMutex ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */
89 gboolean need_preview;
90 GHashTable *preview_updates;
92 guint32 nextuid; /* next uid? */
93 guint32 saved_count; /* how many were saved/loaded */
94 guint32 unread_count; /* handy totals */
95 guint32 deleted_count;
97 guint32 junk_not_deleted_count;
98 guint32 visible_count;
100 gboolean build_content; /* do we try and parse/index the content, or not? */
102 GHashTable *uids; /* uids of all known message infos; the 'value' are used flags for the message info */
103 GHashTable *loaded_infos; /* uid->CamelMessageInfo *, those currently in memory */
105 struct _CamelFolder *folder; /* parent folder, for events */
106 time_t cache_load_time;
107 guint timeout_handle;
110 static GStaticMutex info_lock = G_STATIC_MUTEX_INIT;
112 /* this lock is ONLY for the standalone messageinfo stuff */
113 #define GLOBAL_INFO_LOCK(i) g_static_mutex_lock(&info_lock)
114 #define GLOBAL_INFO_UNLOCK(i) g_static_mutex_unlock(&info_lock)
116 /* this should probably be conditional on it existing */
120 #define io(x) /* io debug */
123 #define CAMEL_FOLDER_SUMMARY_VERSION (14)
125 /* trivial lists, just because ... */
130 static void cfs_schedule_info_release_timer (CamelFolderSummary *summary);
132 static struct _node *my_list_append (struct _node **list, struct _node *n);
133 static gint my_list_size (struct _node **list);
135 static CamelMessageInfo * message_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
136 static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
137 static CamelMessageInfo * message_info_new_from_message (CamelFolderSummary *summary, CamelMimeMessage *msg, const gchar *bodystructure);
138 static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);
140 static CamelMessageContentInfo * content_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
141 static CamelMessageContentInfo * content_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
142 static CamelMessageContentInfo * content_info_new_from_message (CamelFolderSummary *summary, CamelMimePart *mp);
143 static void content_info_free (CamelFolderSummary *, CamelMessageContentInfo *);
145 static gint save_message_infos_to_db (CamelFolderSummary *summary, gboolean fresh_mir, GError **error);
146 static gint camel_read_mir_callback (gpointer ref, gint ncol, gchar ** cols, gchar ** name);
148 static gchar *next_uid_string (CamelFolderSummary *summary);
150 static CamelMessageContentInfo * summary_build_content_info (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimeParser *mp);
151 static CamelMessageContentInfo * summary_build_content_info_message (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimePart *object);
153 static CamelMessageInfo * message_info_from_uid (CamelFolderSummary *summary, const gchar *uid);
162 PROP_JUNK_NOT_DELETED_COUNT,
168 G_DEFINE_TYPE (CamelFolderSummary, camel_folder_summary, CAMEL_TYPE_OBJECT)
171 remove_each_item (gpointer uid,
175 GSList **to_remove_infos = user_data;
177 *to_remove_infos = g_slist_prepend (*to_remove_infos, mi);
183 remove_all_loaded (CamelFolderSummary *summary)
185 GSList *to_remove_infos = NULL;
187 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
189 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
191 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
192 g_hash_table_foreach_remove (summary->priv->loaded_infos, remove_each_item, &to_remove_infos);
193 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
195 g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_free, NULL);
196 g_slist_free (to_remove_infos);
198 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
202 free_o_name (gpointer key,
206 g_object_unref (value);
211 folder_summary_dispose (GObject *object)
213 CamelFolderSummaryPrivate *priv;
215 priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (object);
217 if (priv->timeout_handle) {
218 /* this should not happen, because the release timer
219 * holds a reference on object */
220 g_source_remove (priv->timeout_handle);
221 priv->timeout_handle = 0;
224 if (priv->filter_index != NULL) {
225 g_object_unref (priv->filter_index);
226 priv->filter_index = NULL;
229 if (priv->filter_64 != NULL) {
230 g_object_unref (priv->filter_64);
231 priv->filter_64 = NULL;
234 if (priv->filter_qp != NULL) {
235 g_object_unref (priv->filter_qp);
236 priv->filter_qp = NULL;
239 if (priv->filter_uu != NULL) {
240 g_object_unref (priv->filter_uu);
241 priv->filter_uu = NULL;
244 if (priv->filter_save != NULL) {
245 g_object_unref (priv->filter_save);
246 priv->filter_save = NULL;
249 if (priv->filter_html != NULL) {
250 g_object_unref (priv->filter_html);
251 priv->filter_html = NULL;
254 if (priv->filter_stream != NULL) {
255 g_object_unref (priv->filter_stream);
256 priv->filter_stream = NULL;
259 if (priv->index != NULL) {
260 g_object_unref (priv->index);
265 g_object_weak_unref (G_OBJECT (priv->folder), (GWeakNotify) g_nullify_pointer, &priv->folder);
269 /* Chain up to parent's dispose() method. */
270 G_OBJECT_CLASS (camel_folder_summary_parent_class)->dispose (object);
274 folder_summary_finalize (GObject *object)
276 CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (object);
277 CamelFolderSummaryPrivate *priv = summary->priv;
279 g_hash_table_destroy (priv->uids);
280 remove_all_loaded (summary);
281 g_hash_table_destroy (priv->loaded_infos);
283 g_hash_table_foreach (priv->filter_charset, free_o_name, NULL);
284 g_hash_table_destroy (priv->filter_charset);
286 g_hash_table_destroy (priv->preview_updates);
288 g_static_rec_mutex_free (&priv->summary_lock);
289 g_static_rec_mutex_free (&priv->io_lock);
290 g_static_rec_mutex_free (&priv->filter_lock);
291 g_static_rec_mutex_free (&priv->alloc_lock);
292 g_static_rec_mutex_free (&priv->ref_lock);
294 /* Chain up to parent's finalize() method. */
295 G_OBJECT_CLASS (camel_folder_summary_parent_class)->finalize (object);
299 folder_summary_set_folder (CamelFolderSummary *summary,
302 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
303 g_return_if_fail (summary->priv->folder == NULL);
304 /* folder can be NULL in certain cases, see maildir-store */
306 summary->priv->folder = folder;
308 g_object_weak_ref (G_OBJECT (folder), (GWeakNotify) g_nullify_pointer, &summary->priv->folder);
312 folder_summary_set_property (GObject *object,
317 switch (property_id) {
319 folder_summary_set_folder (
320 CAMEL_FOLDER_SUMMARY (object),
321 CAMEL_FOLDER (g_value_get_object (value)));
324 case PROP_BUILD_CONTENT:
325 camel_folder_summary_set_build_content (
326 CAMEL_FOLDER_SUMMARY (object),
327 g_value_get_boolean (value));
330 case PROP_NEED_PREVIEW:
331 camel_folder_summary_set_need_preview (
332 CAMEL_FOLDER_SUMMARY (object),
333 g_value_get_boolean (value));
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
341 folder_summary_get_property (GObject *object,
346 switch (property_id) {
349 value, camel_folder_summary_get_folder (
350 CAMEL_FOLDER_SUMMARY (object)));
353 case PROP_SAVED_COUNT:
355 value, camel_folder_summary_get_saved_count (
356 CAMEL_FOLDER_SUMMARY (object)));
359 case PROP_UNREAD_COUNT:
361 value, camel_folder_summary_get_unread_count (
362 CAMEL_FOLDER_SUMMARY (object)));
365 case PROP_DELETED_COUNT:
367 value, camel_folder_summary_get_deleted_count (
368 CAMEL_FOLDER_SUMMARY (object)));
371 case PROP_JUNK_COUNT:
373 value, camel_folder_summary_get_junk_count (
374 CAMEL_FOLDER_SUMMARY (object)));
377 case PROP_JUNK_NOT_DELETED_COUNT:
379 value, camel_folder_summary_get_junk_not_deleted_count (
380 CAMEL_FOLDER_SUMMARY (object)));
383 case PROP_VISIBLE_COUNT:
385 value, camel_folder_summary_get_visible_count (
386 CAMEL_FOLDER_SUMMARY (object)));
389 case PROP_BUILD_CONTENT:
390 g_value_set_boolean (
391 value, camel_folder_summary_get_build_content (
392 CAMEL_FOLDER_SUMMARY (object)));
395 case PROP_NEED_PREVIEW:
396 g_value_set_boolean (
397 value, camel_folder_summary_get_need_preview (
398 CAMEL_FOLDER_SUMMARY (object)));
402 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
406 is_in_memory_summary (CamelFolderSummary *summary)
408 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
410 return (summary->flags & CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY) != 0;
413 #define UPDATE_COUNTS_ADD (1)
414 #define UPDATE_COUNTS_SUB (2)
415 #define UPDATE_COUNTS_ADD_WITHOUT_TOTAL (3)
416 #define UPDATE_COUNTS_SUB_WITHOUT_TOTAL (4)
419 folder_summary_update_counts_by_flags (CamelFolderSummary *summary,
423 gint unread = 0, deleted = 0, junk = 0;
424 gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
425 gboolean subtract = op_type == UPDATE_COUNTS_SUB || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
426 gboolean without_total = op_type == UPDATE_COUNTS_ADD_WITHOUT_TOTAL || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
427 gboolean changed = FALSE;
428 GObject *summary_object;
430 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
432 summary_object = G_OBJECT (summary);
434 if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
435 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
437 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
438 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
441 if (!(flags & CAMEL_MESSAGE_SEEN))
442 unread = subtract ? -1 : 1;
444 if (flags & CAMEL_MESSAGE_DELETED)
445 deleted = subtract ? -1 : 1;
447 if (flags & CAMEL_MESSAGE_JUNK)
448 junk = subtract ? -1 : 1;
450 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));
452 g_object_freeze_notify (summary_object);
455 summary->priv->deleted_count += deleted;
456 g_object_notify (summary_object, "deleted-count");
461 summary->priv->junk_count += junk;
462 g_object_notify (summary_object, "junk-count");
466 if (junk && !deleted) {
467 summary->priv->junk_not_deleted_count += junk;
468 g_object_notify (summary_object, "junk-not-deleted-count");
472 if (!junk && !deleted) {
473 summary->priv->visible_count += subtract ? -1 : 1;
474 g_object_notify (summary_object, "visible-count");
478 if (junk && !is_junk_folder)
480 if (deleted && !is_trash_folder)
484 summary->priv->unread_count += unread;
485 g_object_notify (summary_object, "unread-count");
489 if (!without_total) {
490 summary->priv->saved_count += subtract ? -1 : 1;
491 g_object_notify (summary_object, "saved-count");
496 camel_folder_summary_touch (summary);
498 g_object_thaw_notify (summary_object);
500 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));
506 summary_header_from_db (CamelFolderSummary *summary,
507 CamelFIRecord *record)
509 io (printf ("Loading header from db \n"));
511 summary->version = record->version;
513 /* We may not worry, as we are setting a new standard here */
515 /* Legacy version check, before version 12 we have no upgrade knowledge */
516 if ((summary->version > 0xff) && (summary->version & 0xff) < 12) {
517 io (printf ("Summary header version mismatch"));
522 if (!(summary->version < 0x100 && summary->version >= 13))
523 io (printf ("Loading legacy summary\n"));
525 io (printf ("loading new-format summary\n"));
528 summary->flags = record->flags;
529 summary->priv->nextuid = record->nextuid;
530 summary->time = record->time;
531 summary->priv->saved_count = record->saved_count;
533 summary->priv->unread_count = record->unread_count;
534 summary->priv->deleted_count = record->deleted_count;
535 summary->priv->junk_count = record->junk_count;
536 summary->priv->visible_count = record->visible_count;
537 summary->priv->junk_not_deleted_count = record->jnd_count;
542 static CamelFIRecord *
543 summary_header_to_db (CamelFolderSummary *summary,
546 CamelFIRecord * record = g_new0 (CamelFIRecord, 1);
547 CamelStore *parent_store;
549 const gchar *table_name;
551 /* Though we are going to read, we do this during write,
552 * so lets use it that way. */
553 table_name = camel_folder_get_full_name (summary->priv->folder);
554 parent_store = camel_folder_get_parent_store (summary->priv->folder);
555 db = parent_store->cdb_w;
557 io (printf ("Savining header to db\n"));
559 record->folder_name = g_strdup (table_name);
561 /* we always write out the current version */
562 record->version = CAMEL_FOLDER_SUMMARY_VERSION;
563 record->flags = summary->flags;
564 record->nextuid = summary->priv->nextuid;
565 record->time = summary->time;
567 if (!is_in_memory_summary (summary)) {
568 /* FIXME: Ever heard of Constructors and initializing ? */
569 if (camel_db_count_total_message_info (db, table_name, &(record->saved_count), NULL))
570 record->saved_count = 0;
571 if (camel_db_count_junk_message_info (db, table_name, &(record->junk_count), NULL))
572 record->junk_count = 0;
573 if (camel_db_count_deleted_message_info (db, table_name, &(record->deleted_count), NULL))
574 record->deleted_count = 0;
575 if (camel_db_count_unread_message_info (db, table_name, &(record->unread_count), NULL))
576 record->unread_count = 0;
577 if (camel_db_count_visible_message_info (db, table_name, &(record->visible_count), NULL))
578 record->visible_count = 0;
579 if (camel_db_count_junk_not_deleted_message_info (db, table_name, &(record->jnd_count), NULL))
580 record->jnd_count = 0;
583 summary->priv->unread_count = record->unread_count;
584 summary->priv->deleted_count = record->deleted_count;
585 summary->priv->junk_count = record->junk_count;
586 summary->priv->visible_count = record->visible_count;
587 summary->priv->junk_not_deleted_count = record->jnd_count;
592 static CamelMessageInfo *
593 message_info_from_db (CamelFolderSummary *summary,
594 CamelMIRecord *record)
596 CamelMessageInfoBase *mi;
601 mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
603 io (printf ("Loading message info from db\n"));
605 mi->flags = record->flags;
606 mi->size = record->size;
607 mi->date_sent = record->dsent;
608 mi->date_received = record->dreceived;
610 mi->uid = (gchar *) camel_pstring_strdup (record->uid);
611 mi->subject = (gchar *) camel_pstring_add (record->subject, FALSE);
612 mi->from = (gchar *) camel_pstring_add (record->from, FALSE);
613 mi->to = (gchar *) camel_pstring_add (record->to, FALSE);
614 mi->cc = (gchar *) camel_pstring_add (record->cc, FALSE);
615 mi->mlist = (gchar *) camel_pstring_add (record->mlist, FALSE);
617 /* Evolution itself doesn't yet use this, so we ignore it (saving some memory) */
618 mi->bodystructure = NULL;
620 /* Extract Message id & References */
624 mi->message_id.id.part.hi = bdata_extract_digit (&part);
625 mi->message_id.id.part.lo = bdata_extract_digit (&part);
626 count = bdata_extract_digit (&part);
629 mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
630 mi->references->size = count;
631 for (i = 0; i < count; i++) {
632 mi->references->references[i].id.part.hi = bdata_extract_digit (&part);
633 mi->references->references[i].id.part.lo = bdata_extract_digit (&part);
636 mi->references = NULL;
640 /* Extract User flags/labels */
641 part = record->labels;
644 for (i = 0; part[i]; i++) {
646 if (part[i] == ' ') {
648 camel_flag_set (&mi->user_flags, label, TRUE);
649 label = &(part[i + 1]);
652 camel_flag_set (&mi->user_flags, label, TRUE);
655 /* Extract User tags */
656 part = record->usertags;
657 count = bdata_extract_digit (&part);
658 for (i = 0; i < count; i++) {
661 name = bdata_extract_string (&part);
662 value = bdata_extract_string (&part);
663 camel_tag_set (&mi->user_tags, name, value);
669 return (CamelMessageInfo *) mi;
672 static CamelMIRecord *
673 message_info_to_db (CamelFolderSummary *summary,
674 CamelMessageInfo *info)
676 CamelMIRecord *record = g_new0 (CamelMIRecord, 1);
677 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
683 /* Assume that we dont have to take care of DB Safeness. It will be done while doing the DB transaction */
684 record->uid = (gchar *) camel_pstring_strdup (camel_message_info_uid (mi));
685 record->flags = mi->flags;
687 record->read = ((mi->flags & (CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_JUNK))) ? 1 : 0;
688 record->deleted = mi->flags & CAMEL_MESSAGE_DELETED ? 1 : 0;
689 record->replied = mi->flags & CAMEL_MESSAGE_ANSWERED ? 1 : 0;
690 record->important = mi->flags & CAMEL_MESSAGE_FLAGGED ? 1 : 0;
691 record->junk = mi->flags & CAMEL_MESSAGE_JUNK ? 1 : 0;
692 record->dirty = mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED ? 1 : 0;
693 record->attachment = mi->flags & CAMEL_MESSAGE_ATTACHMENTS ? 1 : 0;
695 record->size = mi->size;
696 record->dsent = mi->date_sent;
697 record->dreceived = mi->date_received;
699 record->subject = (gchar *) camel_pstring_strdup (camel_message_info_subject (mi));
700 record->from = (gchar *) camel_pstring_strdup (camel_message_info_from (mi));
701 record->to = (gchar *) camel_pstring_strdup (camel_message_info_to (mi));
702 record->cc = (gchar *) camel_pstring_strdup (camel_message_info_cc (mi));
703 record->mlist = (gchar *) camel_pstring_strdup (camel_message_info_mlist (mi));
705 record->followup_flag = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "follow-up"));
706 record->followup_completed_on = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "completed-on"));
707 record->followup_due_by = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "due-by"));
709 record->bodystructure = mi->bodystructure ? g_strdup (mi->bodystructure) : NULL;
711 tmp = g_string_new (NULL);
712 if (mi->references) {
713 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);
714 for (i = 0; i < mi->references->size; i++)
715 g_string_append_printf (tmp, " %lu %lu", (gulong) mi->references->references[i].id.part.hi, (gulong) mi->references->references[i].id.part.lo);
717 g_string_append_printf (tmp, "%lu %lu %lu", (gulong) mi->message_id.id.part.hi, (gulong) mi->message_id.id.part.lo, (gulong) 0);
719 record->part = tmp->str;
720 g_string_free (tmp, FALSE);
722 tmp = g_string_new (NULL);
723 flag = mi->user_flags;
725 g_string_append_printf (tmp, "%s ", flag->name);
729 /* Strip off the last space */
733 record->labels = tmp->str;
734 g_string_free (tmp, FALSE);
736 tmp = g_string_new (NULL);
737 count = camel_tag_list_size (&mi->user_tags);
738 g_string_append_printf (tmp, "%lu", (gulong) count);
741 /* FIXME: Should we handle empty tags? Can it be empty? If it potential crasher ahead*/
742 g_string_append_printf (tmp, " %lu-%s %lu-%s", (gulong) strlen (tag->name), tag->name, (gulong) strlen (tag->value), tag->value);
745 record->usertags = tmp->str;
746 g_string_free (tmp, FALSE);
751 static CamelMessageContentInfo *
752 content_info_from_db (CamelFolderSummary *summary,
753 CamelMIRecord *record)
755 CamelMessageContentInfo *ci;
756 gchar *type, *subtype;
758 CamelContentType *ct;
759 gchar *part = record->cinfo;
761 io (printf ("Loading content info from db\n"));
766 ci = camel_folder_summary_content_info_new (summary);
767 if (*part == ' ') part++; /* Move off the space in the record */
769 type = bdata_extract_string (&part);
770 subtype = bdata_extract_string (&part);
771 ct = camel_content_type_new (type, subtype);
772 g_free (type); /* can this be removed? */
774 count = bdata_extract_digit (&part);
776 for (i = 0; i < count; i++) {
778 name = bdata_extract_string (&part);
779 value = bdata_extract_string (&part);
781 camel_content_type_set_param (ct, name, value);
782 /* TODO: do this so we dont have to double alloc/free */
788 /* FIXME[disk-summary] move all these to camel pstring */
789 ci->id = bdata_extract_string (&part);
790 ci->description = bdata_extract_string (&part);
791 ci->encoding = bdata_extract_string (&part);
792 ci->size = bdata_extract_digit (&part);
794 record->cinfo = part; /* Keep moving the cursor in the record */
802 content_info_to_db (CamelFolderSummary *summary,
803 CamelMessageContentInfo *ci,
804 CamelMIRecord *record)
806 CamelContentType *ct;
807 struct _camel_header_param *hp;
808 GString *str = g_string_new (NULL);
811 io (printf ("Saving content info to db\n"));
816 g_string_append_printf (str, " %d-%s", (gint) strlen (ct->type), ct->type);
818 g_string_append_printf (str, " 0-");
820 g_string_append_printf (str, " %d-%s", (gint) strlen (ct->subtype), ct->subtype);
822 g_string_append_printf (str, " 0-");
823 g_string_append_printf (str, " %d", my_list_size ((struct _node **) &ct->params));
827 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->name), hp->name);
829 g_string_append_printf (str, " 0-");
831 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->value), hp->value);
833 g_string_append_printf (str, " 0-");
837 g_string_append_printf (str, " %d-", 0);
838 g_string_append_printf (str, " %d-", 0);
839 g_string_append_printf (str, " %d", 0);
843 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->id), ci->id);
845 g_string_append_printf (str, " 0-");
847 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->description), ci->description);
849 g_string_append_printf (str, " 0-");
851 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->encoding), ci->encoding);
853 g_string_append_printf (str, " 0-");
854 g_string_append_printf (str, " %u", ci->size);
857 oldr = record->cinfo;
858 record->cinfo = g_strconcat (oldr, str->str, NULL);
859 g_free (oldr); g_string_free (str, TRUE);
861 record->cinfo = str->str;
862 g_string_free (str, FALSE);
869 * camel_folder_summary_replace_flags:
870 * @summary: a #CamelFolderSummary
871 * @info: a #CamelMessageInfo
873 * Updates internal counts based on the flags in @info.
875 * Returns: Whether any count changed
880 camel_folder_summary_replace_flags (CamelFolderSummary *summary,
881 CamelMessageInfo *info)
883 guint32 old_flags, new_flags, added_flags, removed_flags;
884 gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
885 GObject *summary_object;
886 gboolean changed = FALSE;
888 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
889 g_return_val_if_fail (info != NULL, FALSE);
891 if (!camel_message_info_uid (info) ||
892 !camel_folder_summary_check_uid (summary, camel_message_info_uid (info)))
895 summary_object = G_OBJECT (summary);
897 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
898 g_object_freeze_notify (summary_object);
900 old_flags = GPOINTER_TO_UINT (g_hash_table_lookup (summary->priv->uids, camel_message_info_uid (info)));
901 new_flags = camel_message_info_flags (info);
903 if ((old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED) == (new_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED)) {
904 g_object_thaw_notify (summary_object);
905 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
909 if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
910 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
912 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
913 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
916 added_flags = new_flags & (~(old_flags & new_flags));
917 removed_flags = old_flags & (~(old_flags & new_flags));
919 if ((old_flags & CAMEL_MESSAGE_SEEN) == (new_flags & CAMEL_MESSAGE_SEEN)) {
920 /* unread count is different from others, it asks for nonexistence
921 * of the flag, thus if it wasn't changed, then simply set it
922 * in added/removed, thus there are no false notifications
923 * on unread counts */
924 added_flags |= CAMEL_MESSAGE_SEEN;
925 removed_flags |= CAMEL_MESSAGE_SEEN;
926 } else if ((!is_junk_folder && (new_flags & CAMEL_MESSAGE_JUNK) != 0 &&
927 (old_flags & CAMEL_MESSAGE_JUNK) == (new_flags & CAMEL_MESSAGE_JUNK)) ||
928 (!is_trash_folder && (new_flags & CAMEL_MESSAGE_DELETED) != 0 &&
929 (old_flags & CAMEL_MESSAGE_DELETED) == (new_flags & CAMEL_MESSAGE_DELETED))) {
930 /* The message was set read or unread, but it is a junk or deleted message,
931 * in a non-Junk/non-Trash folder, thus it doesn't influence an unread count
932 * there, thus pretend unread didn't change */
933 added_flags |= CAMEL_MESSAGE_SEEN;
934 removed_flags |= CAMEL_MESSAGE_SEEN;
937 /* decrement counts with removed flags */
938 changed = folder_summary_update_counts_by_flags (summary, removed_flags, UPDATE_COUNTS_SUB_WITHOUT_TOTAL) || changed;
939 /* increment counts with added flags */
940 changed = folder_summary_update_counts_by_flags (summary, added_flags, UPDATE_COUNTS_ADD_WITHOUT_TOTAL) || changed;
942 /* update current flags on the summary */
943 g_hash_table_insert (summary->priv->uids,
944 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
945 GUINT_TO_POINTER (new_flags));
947 g_object_thaw_notify (summary_object);
948 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
953 static CamelMessageInfo *
954 message_info_clone (CamelFolderSummary *summary,
955 const CamelMessageInfo *mi)
957 CamelMessageInfoBase *to, *from = (CamelMessageInfoBase *) mi;
961 to = (CamelMessageInfoBase *) camel_message_info_new (summary);
963 to->flags = from->flags;
964 to->size = from->size;
965 to->date_sent = from->date_sent;
966 to->date_received = from->date_received;
969 /* NB: We don't clone the uid */
971 to->subject = camel_pstring_strdup (from->subject);
972 to->from = camel_pstring_strdup (from->from);
973 to->to = camel_pstring_strdup (from->to);
974 to->cc = camel_pstring_strdup (from->cc);
975 to->mlist = camel_pstring_strdup (from->mlist);
976 memcpy (&to->message_id, &from->message_id, sizeof (to->message_id));
977 to->preview = g_strdup (from->preview);
978 if (from->references) {
979 gint len = sizeof (*from->references) + ((from->references->size - 1) * sizeof (from->references->references[0]));
981 to->references = g_malloc (len);
982 memcpy (to->references, from->references, len);
985 flag = from->user_flags;
987 camel_flag_set (&to->user_flags, flag->name, TRUE);
991 tag = from->user_tags;
993 camel_tag_set (&to->user_tags, tag->name, tag->value);
998 /* FIXME: copy content-infos */
1001 return (CamelMessageInfo *) to;
1004 static gconstpointer
1005 info_ptr (const CamelMessageInfo *mi,
1009 case CAMEL_MESSAGE_INFO_SUBJECT:
1010 return ((const CamelMessageInfoBase *) mi)->subject;
1011 case CAMEL_MESSAGE_INFO_FROM:
1012 return ((const CamelMessageInfoBase *) mi)->from;
1013 case CAMEL_MESSAGE_INFO_TO:
1014 return ((const CamelMessageInfoBase *) mi)->to;
1015 case CAMEL_MESSAGE_INFO_CC:
1016 return ((const CamelMessageInfoBase *) mi)->cc;
1017 case CAMEL_MESSAGE_INFO_MLIST:
1018 return ((const CamelMessageInfoBase *) mi)->mlist;
1019 case CAMEL_MESSAGE_INFO_MESSAGE_ID:
1020 return &((const CamelMessageInfoBase *) mi)->message_id;
1021 case CAMEL_MESSAGE_INFO_REFERENCES:
1022 return ((const CamelMessageInfoBase *) mi)->references;
1023 case CAMEL_MESSAGE_INFO_USER_FLAGS:
1024 return ((const CamelMessageInfoBase *) mi)->user_flags;
1025 case CAMEL_MESSAGE_INFO_USER_TAGS:
1026 return ((const CamelMessageInfoBase *) mi)->user_tags;
1027 case CAMEL_MESSAGE_INFO_HEADERS:
1028 return ((const CamelMessageInfoBase *) mi)->headers;
1029 case CAMEL_MESSAGE_INFO_CONTENT:
1030 return ((const CamelMessageInfoBase *) mi)->content;
1031 case CAMEL_MESSAGE_INFO_PREVIEW:
1032 return ((const CamelMessageInfoBase *) mi)->preview;
1034 g_return_val_if_reached (NULL);
1039 info_uint32 (const CamelMessageInfo *mi,
1043 case CAMEL_MESSAGE_INFO_FLAGS:
1044 return ((const CamelMessageInfoBase *) mi)->flags;
1045 case CAMEL_MESSAGE_INFO_SIZE:
1046 return ((const CamelMessageInfoBase *) mi)->size;
1048 g_return_val_if_reached (0);
1053 info_time (const CamelMessageInfo *mi,
1057 case CAMEL_MESSAGE_INFO_DATE_SENT:
1058 return ((const CamelMessageInfoBase *) mi)->date_sent;
1059 case CAMEL_MESSAGE_INFO_DATE_RECEIVED:
1060 return ((const CamelMessageInfoBase *) mi)->date_received;
1062 g_return_val_if_reached (0);
1067 info_user_flag (const CamelMessageInfo *mi,
1070 return camel_flag_get (&((CamelMessageInfoBase *) mi)->user_flags, id);
1073 static const gchar *
1074 info_user_tag (const CamelMessageInfo *mi,
1077 return camel_tag_get (&((CamelMessageInfoBase *) mi)->user_tags, id);
1081 info_set_user_flag (CamelMessageInfo *info,
1085 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1088 res = camel_flag_set (&mi->user_flags, name, value);
1090 if (mi->summary && res && mi->summary->priv->folder && mi->uid
1091 && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1092 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1094 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1096 camel_folder_summary_touch (mi->summary);
1097 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1098 camel_folder_changed (mi->summary->priv->folder, changes);
1099 camel_folder_change_info_free (changes);
1106 info_set_user_tag (CamelMessageInfo *info,
1110 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1113 res = camel_tag_set (&mi->user_tags, name, value);
1115 if (mi->summary && res && mi->summary->priv->folder && mi->uid
1116 && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1117 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1119 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1121 camel_folder_summary_touch (mi->summary);
1122 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1123 camel_folder_changed (mi->summary->priv->folder, changes);
1124 camel_folder_change_info_free (changes);
1131 info_set_flags (CamelMessageInfo *info,
1136 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1137 gboolean counts_changed = FALSE;
1139 old = camel_message_info_flags (info);
1140 mi->flags = (old & ~flags) | (set & flags);
1141 if (old != mi->flags) {
1142 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1145 camel_folder_summary_touch (mi->summary);
1149 camel_folder_summary_lock (mi->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1150 g_object_freeze_notify (G_OBJECT (mi->summary));
1151 counts_changed = camel_folder_summary_replace_flags (mi->summary, info);
1154 if (!counts_changed && ((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK))) {
1156 g_object_thaw_notify (G_OBJECT (mi->summary));
1157 camel_folder_summary_unlock (mi->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1163 g_object_thaw_notify (G_OBJECT (mi->summary));
1164 camel_folder_summary_unlock (mi->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1167 if (mi->summary && mi->summary->priv->folder && mi->uid) {
1168 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1170 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1171 camel_folder_changed (mi->summary->priv->folder, changes);
1172 camel_folder_change_info_free (changes);
1179 camel_folder_summary_class_init (CamelFolderSummaryClass *class)
1181 GObjectClass *object_class;
1183 g_type_class_add_private (class, sizeof (CamelFolderSummaryPrivate));
1185 object_class = G_OBJECT_CLASS (class);
1186 object_class->set_property = folder_summary_set_property;
1187 object_class->get_property = folder_summary_get_property;
1188 object_class->dispose = folder_summary_dispose;
1189 object_class->finalize = folder_summary_finalize;
1191 class->message_info_size = sizeof (CamelMessageInfoBase);
1192 class->content_info_size = sizeof (CamelMessageContentInfo);
1194 class->summary_header_from_db = summary_header_from_db;
1195 class->summary_header_to_db = summary_header_to_db;
1196 class->message_info_from_db = message_info_from_db;
1197 class->message_info_to_db = message_info_to_db;
1198 class->content_info_from_db = content_info_from_db;
1199 class->content_info_to_db = content_info_to_db;
1201 class->message_info_new_from_header = message_info_new_from_header;
1202 class->message_info_new_from_parser = message_info_new_from_parser;
1203 class->message_info_new_from_message = message_info_new_from_message;
1204 class->message_info_free = message_info_free;
1205 class->message_info_clone = message_info_clone;
1206 class->message_info_from_uid = message_info_from_uid;
1208 class->content_info_new_from_header = content_info_new_from_header;
1209 class->content_info_new_from_parser = content_info_new_from_parser;
1210 class->content_info_new_from_message = content_info_new_from_message;
1211 class->content_info_free = content_info_free;
1213 class->next_uid_string = next_uid_string;
1215 class->info_ptr = info_ptr;
1216 class->info_uint32 = info_uint32;
1217 class->info_time = info_time;
1218 class->info_user_flag = info_user_flag;
1219 class->info_user_tag = info_user_tag;
1221 class->info_set_user_flag = info_set_user_flag;
1222 class->info_set_user_tag = info_set_user_tag;
1224 class->info_set_flags = info_set_flags;
1227 * CamelFolderSummary:folder
1229 * The #CamelFolder to which the folder summary belongs.
1231 g_object_class_install_property (
1234 g_param_spec_object (
1237 "The folder to which the folder summary belongs",
1240 G_PARAM_CONSTRUCT_ONLY));
1243 * CamelFolderSummary:saved-count
1245 * How many infos is saved in a summary.
1247 g_object_class_install_property (
1253 "How many infos is savef in a summary",
1255 0, G_PARAM_READABLE));
1258 * CamelFolderSummary:unread-count
1260 * How many unread infos is saved in a summary.
1262 g_object_class_install_property (
1268 "How many unread infos is saved in a summary",
1270 0, G_PARAM_READABLE));
1273 * CamelFolderSummary:deleted-count
1275 * How many deleted infos is saved in a summary.
1277 g_object_class_install_property (
1283 "How many deleted infos is saved in a summary",
1285 0, G_PARAM_READABLE));
1288 * CamelFolderSummary:junk-count
1290 * How many junk infos is saved in a summary.
1292 g_object_class_install_property (
1298 "How many junk infos is saved in a summary",
1300 0, G_PARAM_READABLE));
1303 * CamelFolderSummary:junk-not-deleted-count
1305 * How many junk and not deleted infos is saved in a summary.
1307 g_object_class_install_property (
1309 PROP_JUNK_NOT_DELETED_COUNT,
1311 "junk-not-deleted-count",
1312 "Junk not deleted count",
1313 "How many junk and not deleted infos is saved in a summary",
1315 0, G_PARAM_READABLE));
1318 * CamelFolderSummary:visible-count
1320 * How many visible (not deleted and not junk) infos is saved in a summary.
1322 g_object_class_install_property (
1328 "How many visible (not deleted and not junk) infos is saved in a summary",
1330 0, G_PARAM_READABLE));
1333 * CamelFolderSummary:build-content
1335 * Whether to build CamelMessageInfo.content.
1337 g_object_class_install_property (
1340 g_param_spec_boolean (
1343 "Whether to build CamelMessageInfo.content",
1345 G_PARAM_READWRITE));
1348 * CamelFolderSummary:need-preview
1351 g_object_class_install_property (
1354 g_param_spec_boolean (
1359 G_PARAM_READWRITE));
1363 camel_folder_summary_init (CamelFolderSummary *summary)
1365 summary->priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (summary);
1367 summary->version = CAMEL_FOLDER_SUMMARY_VERSION;
1371 summary->priv->filter_charset = g_hash_table_new (
1372 camel_strcase_hash, camel_strcase_equal);
1374 summary->priv->need_preview = FALSE;
1375 summary->priv->preview_updates = g_hash_table_new (g_str_hash, g_str_equal);
1377 summary->priv->nextuid = 1;
1378 summary->priv->uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
1379 summary->priv->loaded_infos = g_hash_table_new (g_str_hash, g_str_equal);
1381 g_static_rec_mutex_init (&summary->priv->summary_lock);
1382 g_static_rec_mutex_init (&summary->priv->io_lock);
1383 g_static_rec_mutex_init (&summary->priv->filter_lock);
1384 g_static_rec_mutex_init (&summary->priv->alloc_lock);
1385 g_static_rec_mutex_init (&summary->priv->ref_lock);
1387 summary->priv->cache_load_time = 0;
1388 summary->priv->timeout_handle = 0;
1392 * camel_folder_summary_new:
1393 * @folder: parent #CamelFolder object
1395 * Create a new #CamelFolderSummary object.
1397 * Returns: a new #CamelFolderSummary object
1399 CamelFolderSummary *
1400 camel_folder_summary_new (CamelFolder *folder)
1402 return g_object_new (CAMEL_TYPE_FOLDER_SUMMARY, "folder", folder, NULL);
1406 * camel_folder_summary_get_index:
1407 * @summary: a #CamelFolderSummary object
1409 * Returns: a #CamelFolder to which the summary if associated.
1414 camel_folder_summary_get_folder (CamelFolderSummary *summary)
1416 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1418 return summary->priv->folder;
1422 * camel_folder_summary_get_saved_count:
1423 * @summary: a #CamelFolderSummary object
1425 * Returns: Count of saved infos.
1430 camel_folder_summary_get_saved_count (CamelFolderSummary *summary)
1432 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1434 return summary->priv->saved_count;
1438 * camel_folder_summary_get_unread_count:
1439 * @summary: a #CamelFolderSummary object
1441 * Returns: Count of unread infos.
1446 camel_folder_summary_get_unread_count (CamelFolderSummary *summary)
1448 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1450 return summary->priv->unread_count;
1454 * camel_folder_summary_get_deleted_count:
1455 * @summary: a #CamelFolderSummary object
1457 * Returns: Count of deleted infos.
1462 camel_folder_summary_get_deleted_count (CamelFolderSummary *summary)
1464 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1466 return summary->priv->deleted_count;
1470 * camel_folder_summary_get_junk_count:
1471 * @summary: a #CamelFolderSummary object
1473 * Returns: Count of junk infos.
1478 camel_folder_summary_get_junk_count (CamelFolderSummary *summary)
1480 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1482 return summary->priv->junk_count;
1486 * camel_folder_summary_get_junk_not_deleted_count:
1487 * @summary: a #CamelFolderSummary object
1489 * Returns: Count of junk and not deleted infos.
1494 camel_folder_summary_get_junk_not_deleted_count (CamelFolderSummary *summary)
1496 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1498 return summary->priv->junk_not_deleted_count;
1502 * camel_folder_summary_get_visible_count:
1503 * @summary: a #CamelFolderSummary object
1505 * Returns: Count of visible (not junk and not deleted) infos.
1510 camel_folder_summary_get_visible_count (CamelFolderSummary *summary)
1512 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1514 return summary->priv->visible_count;
1518 * camel_folder_summary_set_index:
1519 * @summary: a #CamelFolderSummary object
1520 * @index: a #CamelIndex
1522 * Set the index used to index body content. If the index is %NULL, or
1523 * not set (the default), no indexing of body content will take place.
1525 * Unlike earlier behaviour, build_content need not be set to perform indexing.
1528 camel_folder_summary_set_index (CamelFolderSummary *summary,
1531 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1534 g_object_ref (index);
1536 if (summary->priv->index != NULL)
1537 g_object_unref (summary->priv->index);
1539 summary->priv->index = index;
1543 * camel_folder_summary_get_index:
1544 * @summary: a #CamelFolderSummary object
1546 * Returns: a #CamelIndex used to index body content.
1551 camel_folder_summary_get_index (CamelFolderSummary *summary)
1553 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1555 return summary->priv->index;
1559 * camel_folder_summary_set_build_content:
1560 * @summary: a #CamelFolderSummary object
1561 * @state: to build or not to build the content
1563 * Set a flag to tell the summary to build the content info summary
1564 * (#CamelMessageInfo.content). The default is not to build content
1568 camel_folder_summary_set_build_content (CamelFolderSummary *summary,
1571 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1573 if (summary->priv->build_content == state)
1576 summary->priv->build_content = state;
1578 g_object_notify (G_OBJECT (summary), "build-content");
1582 * camel_folder_summary_get_build_content:
1583 * @summary: a #CamelFolderSummary object
1585 * Returns: Whether to build #CamelMessageInfo.content.
1590 camel_folder_summary_get_build_content (CamelFolderSummary *summary)
1592 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1594 return summary->priv->build_content;
1598 * camel_folder_summary_set_need_preview:
1603 camel_folder_summary_set_need_preview (CamelFolderSummary *summary,
1606 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1608 summary->priv->need_preview = preview;
1612 * camel_folder_summary_get_need_preview:
1617 camel_folder_summary_get_need_preview (CamelFolderSummary *summary)
1619 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1621 return summary->priv->need_preview;
1625 * camel_folder_summary_next_uid:
1626 * @summary: a #CamelFolderSummary object
1628 * Generate a new unique uid value as an integer. This
1629 * may be used to create a unique sequence of numbers.
1631 * Returns: the next unique uid value
1634 camel_folder_summary_next_uid (CamelFolderSummary *summary)
1638 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1640 uid = summary->priv->nextuid++;
1641 camel_folder_summary_touch (summary);
1643 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1649 * camel_folder_summary_set_next_uid:
1650 * @summary: a #CamelFolderSummary object
1651 * @uid: The next minimum uid to assign. To avoid clashing
1652 * uid's, set this to the uid of a given messages + 1.
1654 * Set the next minimum uid available. This can be used to
1655 * ensure new uid's do not clash with existing uid's.
1658 camel_folder_summary_set_next_uid (CamelFolderSummary *summary,
1661 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1663 summary->priv->nextuid = MAX (summary->priv->nextuid, uid);
1664 camel_folder_summary_touch (summary);
1666 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1670 * camel_folder_summary_get_next_uid:
1671 * @summary: a #CamelFolderSummary object
1673 * Returns: Next uid currently awaiting for assignment. The difference from
1674 * camel_folder_summary_next_uid() is that this function returns actual
1675 * value and doesn't increment it before returning.
1680 camel_folder_summary_get_next_uid (CamelFolderSummary *summary)
1684 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1686 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1688 res = summary->priv->nextuid;
1690 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1696 * camel_folder_summary_next_uid_string:
1697 * @summary: a #CamelFolderSummary object
1699 * Retrieve the next uid, but as a formatted string.
1701 * Returns: the next uid as an unsigned integer string.
1702 * This string must be freed by the caller.
1705 camel_folder_summary_next_uid_string (CamelFolderSummary *summary)
1707 CamelFolderSummaryClass *class;
1709 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1711 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1712 g_return_val_if_fail (class->next_uid_string != NULL, NULL);
1714 return class->next_uid_string (summary);
1718 * camel_folder_summary_count:
1719 * @summary: a #CamelFolderSummary object
1721 * Get the number of summary items stored in this summary.
1723 * Returns: the number of items in the summary
1726 camel_folder_summary_count (CamelFolderSummary *summary)
1728 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1730 return g_hash_table_size (summary->priv->uids);
1734 * camel_folder_summary_check_uid
1735 * @summary: a #CamelFolderSummary object
1738 * Check if the uid is valid. This isn't very efficient, so it shouldn't be called iteratively.
1741 * Returns: if the uid is present in the summary or not (%TRUE or %FALSE)
1746 camel_folder_summary_check_uid (CamelFolderSummary *summary,
1751 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1752 g_return_val_if_fail (uid != NULL, FALSE);
1754 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1756 ret = g_hash_table_lookup_extended (summary->priv->uids, uid, NULL, NULL);
1758 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1764 folder_summary_dupe_uids_to_array (gpointer key_uid,
1765 gpointer value_flags,
1768 g_ptr_array_add (user_data, (gpointer) camel_pstring_strdup (key_uid));
1772 * camel_folder_summary_get_array:
1773 * @summary: a #CamelFolderSummary object
1775 * Obtain a copy of the summary array. This is done atomically,
1776 * so cannot contain empty entries.
1778 * Free with camel_folder_summary_free_array()
1780 * Returns: a #GPtrArray of uids
1785 camel_folder_summary_get_array (CamelFolderSummary *summary)
1789 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1791 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1793 res = g_ptr_array_sized_new (g_hash_table_size (summary->priv->uids));
1794 g_hash_table_foreach (summary->priv->uids, folder_summary_dupe_uids_to_array, res);
1796 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1802 * camel_folder_summary_free_array:
1803 * @array: a #GPtrArray returned from camel_folder_summary_get_array()
1805 * Free's array and its elements returned from camel_folder_summary_get_array().
1810 camel_folder_summary_free_array (GPtrArray *array)
1815 g_ptr_array_foreach (array, (GFunc) camel_pstring_free, NULL);
1816 g_ptr_array_free (array, TRUE);
1820 cfs_copy_uids_cb (gpointer key,
1824 const gchar *uid = key;
1825 GHashTable *copy_hash = user_data;
1827 g_hash_table_insert (copy_hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
1831 * camel_folder_summary_get_hash:
1832 * @summary: a #CamelFolderSummary object
1834 * Returns hash of current stored 'uids' in summary, where key is 'uid'
1835 * from the string pool, and value is 1. The returned pointer should
1836 * be freed with g_hash_table_destroy().
1838 * Note: When searching for values always use uids from the string pool.
1843 camel_folder_summary_get_hash (CamelFolderSummary *summary)
1847 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1849 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1851 /* using direct hash because of strings being from the string pool */
1852 uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
1853 g_hash_table_foreach (summary->priv->uids, cfs_copy_uids_cb, uids);
1855 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1861 * camel_folder_summary_peek_loaded:
1866 camel_folder_summary_peek_loaded (CamelFolderSummary *summary,
1869 CamelMessageInfo *info;
1871 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1873 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1876 camel_message_info_ref (info);
1881 struct _db_pass_data {
1882 GHashTable *columns_hash;
1883 CamelFolderSummary *summary;
1884 gboolean add; /* or just insert to hashtable */
1887 static CamelMessageInfo *
1888 message_info_from_uid (CamelFolderSummary *summary,
1891 CamelMessageInfo *info;
1894 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1896 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1900 CamelStore *parent_store;
1901 const gchar *folder_name;
1902 struct _db_pass_data data;
1904 folder_name = camel_folder_get_full_name (summary->priv->folder);
1906 if (is_in_memory_summary (summary)) {
1907 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1908 g_warning ("%s: Tried to load uid '%s' from DB on in-memory summary of '%s'",
1909 G_STRFUNC, uid, folder_name);
1913 parent_store = camel_folder_get_parent_store (summary->priv->folder);
1914 cdb = parent_store->cdb_r;
1916 data.columns_hash = NULL;
1917 data.summary = summary;
1920 ret = camel_db_read_message_info_record_with_uid (
1921 cdb, folder_name, uid, &data,
1922 camel_read_mir_callback, NULL);
1923 if (data.columns_hash)
1924 g_hash_table_destroy (data.columns_hash);
1927 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1931 /* We would have double reffed at camel_read_mir_callback */
1932 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1934 cfs_schedule_info_release_timer (summary);
1938 camel_message_info_ref (info);
1940 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
1946 * camel_folder_summary_get:
1947 * @summary: a #CamelFolderSummary object
1950 * Retrieve a summary item by uid.
1952 * A referenced to the summary item is returned, which may be
1953 * ref'd or free'd as appropriate.
1955 * Returns: the summary item, or %NULL if the uid @uid is not available
1960 camel_folder_summary_get (CamelFolderSummary *summary,
1963 CamelFolderSummaryClass *class;
1965 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1966 g_return_val_if_fail (uid != NULL, NULL);
1968 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1969 g_return_val_if_fail (class->message_info_from_uid != NULL, NULL);
1971 return class->message_info_from_uid (summary, uid);
1974 static CamelMessageContentInfo *
1975 perform_content_info_load_from_db (CamelFolderSummary *summary,
1980 CamelMessageContentInfo *ci, *pci;
1983 ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_from_db (summary, mir);
1989 if (*part == ' ') part++;
1990 count = bdata_extract_digit (&part);
1993 for (i = 0; i < count; i++) {
1994 pci = perform_content_info_load_from_db (summary, mir);
1996 my_list_append ((struct _node **) &ci->childs, (struct _node *) pci);
1999 d (fprintf (stderr, "Summary file format messed up?"));
2000 camel_folder_summary_content_info_free (summary, ci);
2008 append_changed_uids (gchar *key,
2009 CamelMessageInfoBase *info,
2012 if (info->dirty || info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED)
2013 g_ptr_array_add (array, (gpointer) camel_pstring_strdup ((camel_message_info_uid (info))));
2017 * camel_folder_summary_get_changed:
2022 camel_folder_summary_get_changed (CamelFolderSummary *summary)
2024 GPtrArray *res = g_ptr_array_new ();
2026 /* FIXME[disk-summary] sucks, this function returns from memory.
2027 * We need to have collate or something to get the modified ones
2028 * from DB and merge */
2030 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2031 g_hash_table_foreach (summary->priv->loaded_infos, (GHFunc) append_changed_uids, res);
2032 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2038 count_changed_uids (gchar *key,
2039 CamelMessageInfoBase *info,
2047 cfs_count_dirty (CamelFolderSummary *summary)
2051 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2052 g_hash_table_foreach (summary->priv->loaded_infos, (GHFunc) count_changed_uids, &count);
2053 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2059 remove_item (gchar *uid,
2060 CamelMessageInfoBase *info,
2061 GSList **to_remove_infos)
2063 if (info->refcount == 1 && !info->dirty && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) {
2064 *to_remove_infos = g_slist_prepend (*to_remove_infos, info);
2072 remove_cache (CamelSession *session,
2073 GCancellable *cancellable,
2074 CamelFolderSummary *summary,
2077 GSList *to_remove_infos = NULL;
2079 CAMEL_DB_RELEASE_SQLITE_MEMORY;
2081 if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2084 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2086 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
2087 g_hash_table_foreach_remove (summary->priv->loaded_infos, (GHRFunc) remove_item, &to_remove_infos);
2088 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
2090 g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_free, NULL);
2091 g_slist_free (to_remove_infos);
2093 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2095 summary->priv->cache_load_time = time (NULL);
2099 cfs_try_release_memory (CamelFolderSummary *summary)
2101 CamelStore *parent_store;
2102 CamelSession *session;
2104 /* If folder is freed or if the cache is nil then clean up */
2105 if (!summary->priv->folder ||
2106 !g_hash_table_size (summary->priv->loaded_infos) ||
2107 is_in_memory_summary (summary)) {
2108 summary->priv->cache_load_time = 0;
2109 summary->priv->timeout_handle = 0;
2110 g_object_unref (summary);
2115 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2116 session = camel_service_get_session (CAMEL_SERVICE (parent_store));
2118 if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2121 camel_session_submit_job (
2123 (CamelSessionCallback) remove_cache,
2124 g_object_ref (summary),
2125 (GDestroyNotify) g_object_unref);
2131 cfs_schedule_info_release_timer (CamelFolderSummary *summary)
2133 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2135 if (is_in_memory_summary (summary))
2138 if (!summary->priv->timeout_handle) {
2139 static gboolean know_can_do = FALSE, can_do = TRUE;
2142 can_do = !g_getenv ("CAMEL_FREE_INFOS");
2146 /* FIXME[disk-summary] LRU please and not timeouts */
2148 summary->priv->timeout_handle = g_timeout_add_seconds (SUMMARY_CACHE_DROP,
2149 (GSourceFunc) cfs_try_release_memory, g_object_ref (summary));
2153 /* update also cache load time to the actual, to not release something just loaded */
2154 summary->priv->cache_load_time = time (NULL);
2158 cfs_cache_size (CamelFolderSummary *summary)
2160 /* FIXME[disk-summary] this is a timely hack. fix it well */
2161 if (!CAMEL_IS_VEE_FOLDER (summary->priv->folder))
2162 return g_hash_table_size (summary->priv->loaded_infos);
2164 return g_hash_table_size (summary->priv->uids);
2167 /* Update preview of cached messages */
2170 msg_update_preview (const gchar *uid,
2172 CamelFolder *folder)
2174 CamelMessageInfoBase *info = (CamelMessageInfoBase *) camel_folder_summary_get (folder->summary, uid);
2175 CamelMimeMessage *msg;
2176 CamelStore *parent_store;
2177 const gchar *full_name;
2179 full_name = camel_folder_get_full_name (folder);
2180 parent_store = camel_folder_get_parent_store (folder);
2182 /* FIXME Pass a GCancellable */
2183 msg = camel_folder_get_message_sync (folder, uid, NULL, NULL);
2185 if (camel_mime_message_build_preview ((CamelMimePart *) msg, (CamelMessageInfo *) info) && info->preview) {
2186 if (!is_in_memory_summary (folder->summary))
2187 camel_db_write_preview_record (parent_store->cdb_w, full_name, info->uid, info->preview, NULL);
2190 camel_message_info_free (info);
2194 pick_uids (const gchar *uid,
2195 CamelMessageInfoBase *mi,
2199 g_ptr_array_add (array, (gchar *) camel_pstring_strdup (uid));
2203 copy_all_uids_to_hash (gpointer uid,
2206 g_return_if_fail (uid != NULL);
2208 g_hash_table_insert (hash, (gchar *) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2212 fill_mi (const gchar *uid,
2214 CamelFolder *folder)
2216 CamelMessageInfoBase *info;
2218 info = g_hash_table_lookup (folder->summary->priv->loaded_infos, uid);
2219 if (info) /* We re assign the memory of msg */
2220 info->preview = (gchar *) msg;
2221 camel_pstring_free (uid); /* unref the uid */
2227 preview_update (CamelSession *session,
2228 GCancellable *cancellable,
2229 CamelFolder *folder,
2232 /* FIXME: Either lock & use or copy & use.*/
2233 GPtrArray *uids_uncached, *uids_array;
2234 GHashTable *preview_data, *uids_hash;
2235 CamelStore *parent_store;
2236 const gchar *full_name;
2237 gboolean is_in_memory = is_in_memory_summary (folder->summary);
2240 uids_array = camel_folder_summary_get_array (folder->summary);
2241 uids_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
2242 g_ptr_array_foreach (uids_array, copy_all_uids_to_hash, uids_hash);
2243 uids_uncached = camel_folder_get_uncached_uids (folder, uids_array, NULL);
2244 camel_folder_summary_free_array (uids_array);
2247 full_name = camel_folder_get_full_name (folder);
2248 parent_store = camel_folder_get_parent_store (folder);
2249 preview_data = is_in_memory ? NULL : camel_db_get_folder_preview (parent_store->cdb_r, full_name, NULL);
2251 g_hash_table_foreach_remove (preview_data, (GHRFunc) fill_mi, folder);
2252 g_hash_table_destroy (preview_data);
2255 camel_folder_summary_lock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2256 g_hash_table_foreach (folder->summary->priv->loaded_infos, (GHFunc) pick_uids, uids_uncached);
2257 camel_folder_summary_unlock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2259 for (i = 0; i < uids_uncached->len; i++) {
2260 g_hash_table_remove (uids_hash, uids_uncached->pdata[i]);
2263 camel_folder_lock (folder, CAMEL_FOLDER_REC_LOCK);
2265 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2266 g_hash_table_foreach (uids_hash, (GHFunc) msg_update_preview, folder);
2268 camel_db_end_transaction (parent_store->cdb_w, NULL);
2269 camel_folder_unlock (folder, CAMEL_FOLDER_REC_LOCK);
2270 camel_folder_free_uids (folder, uids_uncached);
2271 g_hash_table_destroy (uids_hash);
2277 cfs_reload_from_db (CamelFolderSummary *summary,
2281 CamelSession *session;
2282 CamelStore *parent_store;
2283 const gchar *folder_name;
2285 struct _db_pass_data data;
2287 /* FIXME[disk-summary] baseclass this, and vfolders we may have to
2289 d (printf ("\ncamel_folder_summary_reload_from_db called \n"));
2291 if (is_in_memory_summary (summary))
2294 folder_name = camel_folder_get_full_name (summary->priv->folder);
2295 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2296 session = camel_service_get_session (CAMEL_SERVICE (parent_store));
2297 cdb = parent_store->cdb_r;
2299 data.columns_hash = NULL;
2300 data.summary = summary;
2303 ret = camel_db_read_message_info_records (
2304 cdb, folder_name, (gpointer) &data,
2305 camel_read_mir_callback, NULL);
2307 if (data.columns_hash)
2308 g_hash_table_destroy (data.columns_hash);
2310 cfs_schedule_info_release_timer (summary);
2312 if (summary->priv->need_preview)
2313 camel_session_submit_job (
2315 (CamelSessionCallback) preview_update,
2316 g_object_ref (summary->priv->folder),
2317 (GDestroyNotify) g_object_unref);
2319 return ret == 0 ? 0 : -1;
2323 * camel_folder_summary_add_preview:
2328 camel_folder_summary_add_preview (CamelFolderSummary *summary,
2329 CamelMessageInfo *info)
2331 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2332 g_hash_table_insert (summary->priv->preview_updates, (gchar *) info->uid, ((CamelMessageInfoBase *) info)->preview);
2333 camel_folder_summary_touch (summary);
2334 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2338 * camel_folder_summary_prepare_fetch_all:
2339 * @summary: #CamelFolderSummary object
2340 * @error: return location for a #GError, or %NULL
2342 * Loads all infos into memory, if they are not yet and ensures
2343 * they will not be freed in next couple minutes. Call this function
2344 * before any mass operation or when all message infos will be needed,
2345 * for better performance.
2350 camel_folder_summary_prepare_fetch_all (CamelFolderSummary *summary,
2353 guint loaded, known;
2355 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2357 loaded = cfs_cache_size (summary);
2358 known = camel_folder_summary_count (summary);
2360 if (known - loaded > 50) {
2361 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2362 cfs_reload_from_db (summary, error);
2363 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2366 /* update also cache load time, even when not loaded anything */
2367 summary->priv->cache_load_time = time (NULL);
2371 * camel_folder_summary_load_from_db:
2376 camel_folder_summary_load_from_db (CamelFolderSummary *summary,
2380 CamelStore *parent_store;
2381 const gchar *full_name;
2383 GError *local_error = NULL;
2385 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
2387 if (is_in_memory_summary (summary))
2390 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2391 camel_folder_summary_save_to_db (summary, NULL);
2393 /* struct _db_pass_data data; */
2394 d (printf ("\ncamel_folder_summary_load_from_db called \n"));
2396 full_name = camel_folder_get_full_name (summary->priv->folder);
2397 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2398 if (!camel_folder_summary_header_load_from_db (summary, parent_store, full_name, error)) {
2399 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2403 cdb = parent_store->cdb_r;
2405 ret = camel_db_get_folder_uids (
2406 cdb, full_name, summary->sort_by, summary->collate,
2407 summary->priv->uids, &local_error);
2409 if (local_error != NULL && local_error->message != NULL &&
2410 strstr (local_error->message, "no such table") != NULL) {
2411 g_clear_error (&local_error);
2413 /* create table the first time it is accessed and missing */
2414 ret = camel_db_prepare_message_info_table (cdb, full_name, error);
2415 } else if (local_error != NULL)
2416 g_propagate_error (error, local_error);
2418 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2424 mir_from_cols (CamelMIRecord *mir,
2425 CamelFolderSummary *summary,
2426 GHashTable **columns_hash,
2433 for (i = 0; i < ncol; ++i) {
2434 if (!name[i] || !cols[i])
2437 switch (camel_db_get_column_ident (columns_hash, i, ncol, name)) {
2438 case CAMEL_DB_COLUMN_UID:
2439 mir->uid = (gchar *) camel_pstring_strdup (cols[i]);
2441 case CAMEL_DB_COLUMN_FLAGS:
2442 mir->flags = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2444 case CAMEL_DB_COLUMN_READ:
2445 mir->read = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2447 case CAMEL_DB_COLUMN_DELETED:
2448 mir->deleted = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2450 case CAMEL_DB_COLUMN_REPLIED:
2451 mir->replied = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2453 case CAMEL_DB_COLUMN_IMPORTANT:
2454 mir->important = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2456 case CAMEL_DB_COLUMN_JUNK:
2457 mir->junk = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2459 case CAMEL_DB_COLUMN_ATTACHMENT:
2460 mir->attachment = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2462 case CAMEL_DB_COLUMN_SIZE:
2463 mir->size = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2465 case CAMEL_DB_COLUMN_DSENT:
2466 mir->dsent = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2468 case CAMEL_DB_COLUMN_DRECEIVED:
2469 mir->dreceived = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2471 case CAMEL_DB_COLUMN_SUBJECT:
2472 mir->subject = (gchar *) camel_pstring_strdup (cols[i]);
2474 case CAMEL_DB_COLUMN_MAIL_FROM:
2475 mir->from = (gchar *) camel_pstring_strdup (cols[i]);
2477 case CAMEL_DB_COLUMN_MAIL_TO:
2478 mir->to = (gchar *) camel_pstring_strdup (cols[i]);
2480 case CAMEL_DB_COLUMN_MAIL_CC:
2481 mir->cc = (gchar *) camel_pstring_strdup (cols[i]);
2483 case CAMEL_DB_COLUMN_MLIST:
2484 mir->mlist = (gchar *) camel_pstring_strdup (cols[i]);
2486 case CAMEL_DB_COLUMN_FOLLOWUP_FLAG:
2487 mir->followup_flag = (gchar *) camel_pstring_strdup (cols[i]);
2489 case CAMEL_DB_COLUMN_FOLLOWUP_COMPLETED_ON:
2490 mir->followup_completed_on = (gchar *) camel_pstring_strdup (cols[i]);
2492 case CAMEL_DB_COLUMN_FOLLOWUP_DUE_BY:
2493 mir->followup_due_by = (gchar *) camel_pstring_strdup (cols[i]);
2495 case CAMEL_DB_COLUMN_PART:
2496 mir->part = g_strdup (cols[i]);
2498 case CAMEL_DB_COLUMN_LABELS:
2499 mir->labels = g_strdup (cols[i]);
2501 case CAMEL_DB_COLUMN_USERTAGS:
2502 mir->usertags = g_strdup (cols[i]);
2504 case CAMEL_DB_COLUMN_CINFO:
2505 mir->cinfo = g_strdup (cols[i]);
2507 case CAMEL_DB_COLUMN_BDATA:
2508 mir->bdata = g_strdup (cols[i]);
2510 case CAMEL_DB_COLUMN_BODYSTRUCTURE:
2511 /* Evolution itself doesn't yet use this, ignoring */
2512 /* mir->bodystructure = g_strdup (cols[i]); */
2515 g_warn_if_reached ();
2522 camel_read_mir_callback (gpointer ref,
2527 struct _db_pass_data *data = (struct _db_pass_data *) ref;
2528 CamelFolderSummary *summary = data->summary;
2530 CamelMessageInfo *info;
2533 mir = g_new0 (CamelMIRecord , 1);
2534 mir_from_cols (mir, summary, &data->columns_hash, ncol, cols, name);
2536 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2537 if (!mir->uid || g_hash_table_lookup (summary->priv->loaded_infos, mir->uid)) {
2538 /* Unlock and better return */
2539 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2540 camel_db_camel_mir_free (mir);
2543 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2545 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_from_db (summary, mir);
2549 if (summary->priv->build_content) {
2552 /* FIXME: this should be done differently, how i don't know */
2553 ((CamelMessageInfoBase *) info)->content = perform_content_info_load_from_db (summary, mir);
2554 if (((CamelMessageInfoBase *) info)->content == NULL) {
2555 camel_message_info_free (info);
2561 camel_db_camel_mir_free (mir);
2566 /* Just now we are reading from the DB, it can't be dirty. */
2567 ((CamelMessageInfoBase *) info)->dirty = FALSE;
2569 camel_folder_summary_add (summary, info);
2571 camel_folder_summary_insert (summary, info, TRUE);
2574 g_warning ("Loading messageinfo from db failed");
2578 camel_db_camel_mir_free (mir);
2583 /* saves the content descriptions, recursively */
2585 perform_content_info_save_to_db (CamelFolderSummary *summary,
2586 CamelMessageContentInfo *ci,
2587 CamelMIRecord *record)
2589 CamelMessageContentInfo *part;
2592 if (!CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_to_db (summary, ci, record))
2595 oldr = record->cinfo;
2596 record->cinfo = g_strdup_printf ("%s %d", oldr, my_list_size ((struct _node **) &ci->childs));
2601 if (perform_content_info_save_to_db (summary, part, record) == -1)
2616 save_to_db_cb (gpointer key,
2620 SaveToDBArgs *args = (SaveToDBArgs *) data;
2621 GError **error = args->error;
2622 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) value;
2623 CamelFolderSummary *summary = (CamelFolderSummary *) mi->summary;
2624 CamelStore *parent_store;
2625 const gchar *full_name;
2629 full_name = camel_folder_get_full_name (summary->priv->folder);
2630 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2631 cdb = parent_store->cdb_w;
2633 if (!args->migration && !mi->dirty)
2636 mir = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_to_db (summary, (CamelMessageInfo *) mi);
2638 if (mir && summary->priv->build_content) {
2639 if (!perform_content_info_save_to_db (summary, ((CamelMessageInfoBase *) mi)->content, mir)) {
2640 g_warning ("unable to save mir+cinfo for uid: %s\n", mir->uid);
2641 camel_db_camel_mir_free (mir);
2642 /* FIXME: Add exception here */
2647 g_return_if_fail (mir != NULL);
2649 if (!args->migration) {
2650 if (camel_db_write_message_info_record (cdb, full_name, mir, error) != 0) {
2651 camel_db_camel_mir_free (mir);
2655 if (camel_db_write_fresh_message_info_record (cdb, CAMEL_DB_IN_MEMORY_TABLE, mir, error) != 0) {
2656 camel_db_camel_mir_free (mir);
2660 if (args->progress > CAMEL_DB_IN_MEMORY_TABLE_LIMIT) {
2661 g_print ("BULK INsert limit reached \n");
2662 camel_db_flush_in_memory_transactions (cdb, full_name, error);
2663 camel_db_start_in_memory_transactions (cdb, error);
2670 /* Reset the dirty flag which decides if the changes are synced to the DB or not.
2671 The FOLDER_FLAGGED should be used to check if the changes are synced to the server.
2672 So, dont unset the FOLDER_FLAGGED flag */
2675 camel_db_camel_mir_free (mir);
2679 save_message_infos_to_db (CamelFolderSummary *summary,
2680 gboolean fresh_mirs,
2683 CamelStore *parent_store;
2685 const gchar *full_name;
2688 if (is_in_memory_summary (summary))
2692 args.migration = fresh_mirs;
2695 full_name = camel_folder_get_full_name (summary->priv->folder);
2696 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2697 cdb = parent_store->cdb_w;
2699 if (camel_db_prepare_message_info_table (cdb, full_name, error) != 0)
2702 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2704 /* Push MessageInfo-es */
2705 camel_db_begin_transaction (cdb, NULL);
2706 g_hash_table_foreach (summary->priv->loaded_infos, save_to_db_cb, &args);
2707 camel_db_end_transaction (cdb, NULL);
2709 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2710 cfs_schedule_info_release_timer (summary);
2716 msg_save_preview (const gchar *uid,
2718 CamelFolder *folder)
2720 CamelStore *parent_store;
2721 const gchar *full_name;
2723 full_name = camel_folder_get_full_name (folder);
2724 parent_store = camel_folder_get_parent_store (folder);
2726 camel_db_write_preview_record (
2727 parent_store->cdb_w, full_name, uid, (gchar *) value, NULL);
2731 * camel_folder_summary_save_to_db:
2736 camel_folder_summary_save_to_db (CamelFolderSummary *summary,
2739 CamelStore *parent_store;
2741 CamelFIRecord *record;
2744 g_return_val_if_fail (summary != NULL, FALSE);
2746 if (!(summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) ||
2747 is_in_memory_summary (summary))
2750 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2751 cdb = parent_store->cdb_w;
2753 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2755 d (printf ("\ncamel_folder_summary_save_to_db called \n"));
2756 if (summary->priv->need_preview && g_hash_table_size (summary->priv->preview_updates)) {
2757 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2758 g_hash_table_foreach (summary->priv->preview_updates, (GHFunc) msg_save_preview, summary->priv->folder);
2759 g_hash_table_remove_all (summary->priv->preview_updates);
2760 camel_db_end_transaction (parent_store->cdb_w, NULL);
2763 summary->flags &= ~CAMEL_FOLDER_SUMMARY_DIRTY;
2765 count = cfs_count_dirty (summary);
2767 gboolean res = camel_folder_summary_header_save_to_db (summary, error);
2768 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2772 ret = save_message_infos_to_db (summary, FALSE, error);
2774 /* Failed, so lets reset the flag */
2775 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2776 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2780 /* XXX So... if an error is set, how do we even reach this point
2781 * given the above error check? Oye vey this logic is nasty. */
2782 if (error != NULL && *error != NULL &&
2783 strstr ((*error)->message, "26 columns but 28 values") != NULL) {
2784 const gchar *full_name;
2786 full_name = camel_folder_get_full_name (summary->priv->folder);
2787 g_warning ("Fixing up a broken summary migration on %s\n", full_name);
2789 /* Begin everything again. */
2790 camel_db_begin_transaction (cdb, NULL);
2791 camel_db_reset_folder_version (cdb, full_name, 0, NULL);
2792 camel_db_end_transaction (cdb, NULL);
2794 ret = save_message_infos_to_db (summary, FALSE, error);
2796 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2797 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2802 record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2804 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2805 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2809 camel_db_begin_transaction (cdb, NULL);
2810 ret = camel_db_write_folder_info_record (cdb, record, error);
2811 g_free (record->folder_name);
2812 g_free (record->bdata);
2816 camel_db_abort_transaction (cdb, NULL);
2817 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2818 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2822 camel_db_end_transaction (cdb, NULL);
2823 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2829 * camel_folder_summary_header_save_to_db:
2834 camel_folder_summary_header_save_to_db (CamelFolderSummary *summary,
2837 CamelStore *parent_store;
2838 CamelFIRecord *record;
2842 if (is_in_memory_summary (summary))
2845 parent_store = camel_folder_get_parent_store (summary->priv->folder);
2846 cdb = parent_store->cdb_w;
2847 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2849 d (printf ("\ncamel_folder_summary_header_save_to_db called \n"));
2851 record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2853 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2857 camel_db_begin_transaction (cdb, NULL);
2858 ret = camel_db_write_folder_info_record (cdb, record, error);
2859 g_free (record->folder_name);
2860 g_free (record->bdata);
2864 camel_db_abort_transaction (cdb, NULL);
2865 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2869 camel_db_end_transaction (cdb, NULL);
2870 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2876 * camel_folder_summary_header_load_from_db:
2881 camel_folder_summary_header_load_from_db (CamelFolderSummary *summary,
2883 const gchar *folder_name,
2887 CamelFIRecord *record;
2888 gboolean ret = FALSE;
2890 d (printf ("\ncamel_folder_summary_header_load_from_db called \n"));
2892 if (is_in_memory_summary (summary))
2895 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2896 camel_folder_summary_save_to_db (summary, NULL);
2900 record = g_new0 (CamelFIRecord, 1);
2901 camel_db_read_folder_info_record (cdb, folder_name, record, error);
2903 ret = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_from_db (summary, record);
2905 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2907 g_free (record->folder_name);
2908 g_free (record->bdata);
2915 summary_assign_uid (CamelFolderSummary *summary,
2916 CamelMessageInfo *info)
2919 CamelMessageInfo *mi;
2921 uid = camel_message_info_uid (info);
2923 if (uid == NULL || uid[0] == 0) {
2924 camel_pstring_free (info->uid);
2925 uid = info->uid = (gchar *) camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2928 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2930 while ((mi = g_hash_table_lookup (summary->priv->loaded_infos, uid))) {
2931 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2936 d (printf ("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid (info)));
2938 camel_pstring_free (info->uid);
2939 uid = info->uid = camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2940 camel_message_info_set_flags (info, CAMEL_MESSAGE_FOLDER_FLAGGED, CAMEL_MESSAGE_FOLDER_FLAGGED);
2942 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2945 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2951 * camel_folder_summary_add:
2952 * @summary: a #CamelFolderSummary object
2953 * @info: a #CamelMessageInfo
2955 * Adds a new @info record to the summary. If @info->uid is %NULL,
2956 * then a new uid is automatically re-assigned by calling
2957 * camel_folder_summary_next_uid_string().
2959 * The @info record should have been generated by calling one of the
2960 * info_new_*() functions, as it will be free'd based on the summary
2961 * class. And MUST NOT be allocated directly using malloc.
2964 camel_folder_summary_add (CamelFolderSummary *summary,
2965 CamelMessageInfo *info)
2967 CamelMessageInfoBase *base_info;
2969 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2974 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2975 if (!summary_assign_uid (summary, info)) {
2976 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2980 base_info = (CamelMessageInfoBase *) info;
2981 folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
2982 base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
2983 base_info->dirty = TRUE;
2985 g_hash_table_insert (summary->priv->uids,
2986 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
2987 GUINT_TO_POINTER (camel_message_info_flags (info)));
2989 /* Summary always holds a ref for the loaded infos */
2990 g_hash_table_insert (summary->priv->loaded_infos, (gpointer) camel_message_info_uid (info), info);
2992 camel_folder_summary_touch (summary);
2994 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
2998 * camel_folder_summary_insert:
3003 camel_folder_summary_insert (CamelFolderSummary *summary,
3004 CamelMessageInfo *info,
3007 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
3012 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3015 CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
3017 folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
3018 base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3019 base_info->dirty = TRUE;
3021 g_hash_table_insert (summary->priv->uids,
3022 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
3023 GUINT_TO_POINTER (camel_message_info_flags (info)));
3025 camel_folder_summary_touch (summary);
3028 /* Summary always holds a ref for the loaded infos */
3029 g_hash_table_insert (summary->priv->loaded_infos, (gchar *) camel_message_info_uid (info), info);
3031 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3035 * camel_folder_summary_add_from_header:
3036 * @summary: a #CamelFolderSummary object
3037 * @headers: rfc822 headers
3039 * Build a new info record based on a set of headers, and add it to
3042 * Note that this function should not be used if build_content_info
3043 * has been specified for this summary.
3045 * Returns: the newly added record
3048 camel_folder_summary_add_from_header (CamelFolderSummary *summary,
3049 struct _camel_header_raw *h)
3051 CamelMessageInfo *info;
3053 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
3055 info = camel_folder_summary_info_new_from_header (summary, h);
3057 camel_folder_summary_add (summary, info);
3063 * camel_folder_summary_add_from_parser:
3064 * @summary: a #CamelFolderSummary object
3065 * @parser: a #CamelMimeParser object
3067 * Build a new info record based on the current position of a #CamelMimeParser.
3069 * The parser should be positioned before the start of the message to summarise.
3070 * This function may be used if build_contnet_info or an index has been
3071 * specified for the summary.
3073 * Returns: the newly added record
3076 camel_folder_summary_add_from_parser (CamelFolderSummary *summary,
3077 CamelMimeParser *mp)
3079 CamelMessageInfo *info;
3081 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
3082 g_return_val_if_fail (CAMEL_IS_MIME_PARSER (mp), NULL);
3084 info = camel_folder_summary_info_new_from_parser (summary, mp);
3086 camel_folder_summary_add (summary, info);
3092 * camel_folder_summary_add_from_message:
3093 * @summary: a #CamelFolderSummary object
3094 * @message: a #CamelMimeMessage object
3096 * Add a summary item from an existing message.
3098 * Returns: the newly added record
3101 camel_folder_summary_add_from_message (CamelFolderSummary *summary,
3102 CamelMimeMessage *msg)
3104 CamelMessageInfo *info = camel_folder_summary_info_new_from_message (summary, msg, NULL);
3106 camel_folder_summary_add (summary, info);
3112 * camel_folder_summary_info_new_from_header:
3113 * @summary: a #CamelFolderSummary object
3114 * @headers: rfc822 headers
3116 * Create a new info record from a header.
3118 * Returns: the newly allocated record which must be freed with
3119 * camel_message_info_free()
3122 camel_folder_summary_info_new_from_header (CamelFolderSummary *summary,
3123 struct _camel_header_raw *h)
3125 CamelFolderSummaryClass *class;
3127 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
3129 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3130 g_return_val_if_fail (class->message_info_new_from_header != NULL, NULL);
3132 return class->message_info_new_from_header (summary, h);
3136 * camel_folder_summary_info_new_from_parser:
3137 * @summary: a #CamelFolderSummary object
3138 * @parser: a #CamelMimeParser object
3140 * Create a new info record from a parser. If the parser cannot
3141 * determine a uid, then none will be assigned.
3143 * If indexing is enabled, and the parser cannot determine a new uid, then
3144 * one is automatically assigned.
3146 * If indexing is enabled, then the content will be indexed based
3147 * on this new uid. In this case, the message info MUST be
3148 * added using :add().
3150 * Once complete, the parser will be positioned at the end of
3153 * Returns: the newly allocated record which must be freed with
3154 * camel_message_info_free()
3157 camel_folder_summary_info_new_from_parser (CamelFolderSummary *summary,
3158 CamelMimeParser *mp)
3160 CamelMessageInfo *info = NULL;
3163 CamelFolderSummaryPrivate *p = summary->priv;
3165 CamelIndexName *name = NULL;
3167 /* should this check the parser is in the right state, or assume it is?? */
3169 start = camel_mime_parser_tell (mp);
3170 if (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_EOF) {
3171 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_parser (summary, mp);
3173 camel_mime_parser_unstep (mp);
3175 /* assign a unique uid, this is slightly 'wrong' as we do not really
3176 * know if we are going to store this in the summary, but no matter */
3178 summary_assign_uid (summary, info);
3180 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_FILTER_LOCK);
3183 if (p->filter_index == NULL)
3184 p->filter_index = camel_mime_filter_index_new (p->index);
3185 camel_index_delete_name (p->index, camel_message_info_uid (info));
3186 name = camel_index_add_name (p->index, camel_message_info_uid (info));
3187 camel_mime_filter_index_set_name (CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3190 /* always scan the content info, even if we dont save it */
3191 ((CamelMessageInfoBase *) info)->content = summary_build_content_info (summary, info, mp);
3193 if (name && p->index) {
3194 camel_index_write_name (p->index, name);
3195 g_object_unref (name);
3196 camel_mime_filter_index_set_name (
3197 CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3200 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_FILTER_LOCK);
3202 ((CamelMessageInfoBase *) info)->size = camel_mime_parser_tell (mp) - start;
3208 * camel_folder_summary_info_new_from_message:
3209 * @summary: a #CamelFodlerSummary object
3210 * @message: a #CamelMimeMessage object
3211 * @bodystructure: a bodystructure or NULL
3213 * Create a summary item from a message.
3215 * Returns: the newly allocated record which must be freed using
3216 * camel_message_info_free()
3219 camel_folder_summary_info_new_from_message (CamelFolderSummary *summary,
3220 CamelMimeMessage *msg,
3221 const gchar *bodystructure)
3223 CamelMessageInfo *info;
3224 CamelFolderSummaryPrivate *p = summary->priv;
3225 CamelIndexName *name = NULL;
3227 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_message (summary, msg, bodystructure);
3229 /* assign a unique uid, this is slightly 'wrong' as we do not really
3230 * know if we are going to store this in the summary, but we need it set for indexing */
3232 summary_assign_uid (summary, info);
3234 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_FILTER_LOCK);
3237 if (p->filter_index == NULL)
3238 p->filter_index = camel_mime_filter_index_new (p->index);
3239 camel_index_delete_name (p->index, camel_message_info_uid (info));
3240 name = camel_index_add_name (p->index, camel_message_info_uid (info));
3241 camel_mime_filter_index_set_name (
3242 CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3244 if (p->filter_stream == NULL) {
3245 CamelStream *null = camel_stream_null_new ();
3247 p->filter_stream = camel_stream_filter_new (null);
3248 g_object_unref (null);
3252 ((CamelMessageInfoBase *) info)->content = summary_build_content_info_message (summary, info, (CamelMimePart *) msg);
3255 camel_index_write_name (p->index, name);
3256 g_object_unref (name);
3257 camel_mime_filter_index_set_name (
3258 CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3261 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_FILTER_LOCK);
3267 * camel_folder_summary_content_info_free:
3268 * @summary: a #CamelFolderSummary object
3269 * @ci: a #CamelMessageContentInfo
3271 * Free the content info @ci, and all associated memory.
3274 camel_folder_summary_content_info_free (CamelFolderSummary *summary,
3275 CamelMessageContentInfo *ci)
3277 CamelMessageContentInfo *pw, *pn;
3280 CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_free (summary, ci);
3283 camel_folder_summary_content_info_free (summary, pw);
3289 * camel_folder_summary_touch:
3290 * @summary: a #CamelFolderSummary object
3292 * Mark the summary as changed, so that a save will force it to be
3293 * written back to disk.
3296 camel_folder_summary_touch (CamelFolderSummary *summary)
3298 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3299 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
3300 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3304 * camel_folder_summary_clear:
3305 * @summary: a #CamelFolderSummary object
3307 * Empty the summary contents.
3310 camel_folder_summary_clear (CamelFolderSummary *summary,
3313 GObject *summary_object;
3314 CamelStore *parent_store;
3316 const gchar *folder_name;
3319 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3321 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3322 if (camel_folder_summary_count (summary) == 0) {
3323 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3327 g_hash_table_remove_all (summary->priv->uids);
3328 remove_all_loaded (summary);
3329 g_hash_table_remove_all (summary->priv->loaded_infos);
3331 summary->priv->saved_count = 0;
3332 summary->priv->unread_count = 0;
3333 summary->priv->deleted_count = 0;
3334 summary->priv->junk_count = 0;
3335 summary->priv->junk_not_deleted_count = 0;
3336 summary->priv->visible_count = 0;
3338 camel_folder_summary_touch (summary);
3340 folder_name = camel_folder_get_full_name (summary->priv->folder);
3341 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3342 cdb = parent_store->cdb_w;
3344 if (!is_in_memory_summary (summary))
3345 res = camel_db_clear_folder_summary (cdb, folder_name, error) == 0;
3349 summary_object = G_OBJECT (summary);
3350 g_object_freeze_notify (summary_object);
3351 g_object_notify (summary_object, "saved-count");
3352 g_object_notify (summary_object, "unread-count");
3353 g_object_notify (summary_object, "deleted-count");
3354 g_object_notify (summary_object, "junk-count");
3355 g_object_notify (summary_object, "junk-not-deleted-count");
3356 g_object_notify (summary_object, "visible-count");
3357 g_object_thaw_notify (summary_object);
3359 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3365 * camel_folder_summary_remove:
3366 * @summary: a #CamelFolderSummary object
3367 * @info: a #CamelMessageInfo
3369 * Remove a specific @info record from the summary.
3371 * Returns: Whether the @info was found and removed from the @summary.
3374 camel_folder_summary_remove (CamelFolderSummary *summary,
3375 CamelMessageInfo *info)
3377 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3378 g_return_val_if_fail (info != NULL, FALSE);
3380 if (camel_folder_summary_remove_uid (summary, camel_message_info_uid (info))) {
3381 camel_message_info_free (info);
3389 * camel_folder_summary_remove_uid:
3390 * @summary: a #CamelFolderSummary object
3393 * Remove a specific info record from the summary, by @uid.
3395 * Returns: Whether the @uid was found and removed from the @summary.
3398 camel_folder_summary_remove_uid (CamelFolderSummary *summary,
3401 gpointer ptr_uid = NULL, ptr_flags = NULL;
3402 CamelStore *parent_store;
3403 const gchar *full_name;
3404 const gchar *uid_copy;
3405 gboolean res = TRUE;
3407 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3408 g_return_val_if_fail (uid != NULL, FALSE);
3410 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3411 if (!g_hash_table_lookup_extended (summary->priv->uids, uid, &ptr_uid, &ptr_flags)) {
3412 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3416 folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3418 uid_copy = camel_pstring_strdup (uid);
3419 g_hash_table_remove (summary->priv->uids, uid_copy);
3420 g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3422 if (!is_in_memory_summary (summary)) {
3423 full_name = camel_folder_get_full_name (summary->priv->folder);
3424 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3425 if (camel_db_delete_uid (parent_store->cdb_w, full_name, uid_copy, NULL) != 0)
3429 camel_pstring_free (uid_copy);
3431 camel_folder_summary_touch (summary);
3432 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3438 * camel_folder_summary_remove_uids:
3439 * @summary: a #CamelFolderSummary object
3440 * @uids: a GList of uids
3442 * Remove a specific info record from the summary, by @uid.
3444 * Returns: Whether the @uid was found and removed from the @summary.
3449 camel_folder_summary_remove_uids (CamelFolderSummary *summary,
3452 CamelStore *parent_store;
3453 const gchar *full_name;
3455 gboolean res = TRUE;
3457 g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3458 g_return_val_if_fail (uids != NULL, FALSE);
3460 g_object_freeze_notify (G_OBJECT (summary));
3461 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3463 for (l = g_list_first (uids); l; l = g_list_next (l)) {
3464 gpointer ptr_uid = NULL, ptr_flags = NULL;
3465 if (g_hash_table_lookup_extended (summary->priv->uids, l->data, &ptr_uid, &ptr_flags)) {
3466 const gchar *uid_copy = camel_pstring_strdup (l->data);
3467 CamelMessageInfo *mi;
3469 folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3470 g_hash_table_remove (summary->priv->uids, uid_copy);
3472 mi = g_hash_table_lookup (summary->priv->loaded_infos, uid_copy);
3473 g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3476 camel_message_info_free (mi);
3477 camel_pstring_free (uid_copy);
3481 if (is_in_memory_summary (summary)) {
3482 full_name = camel_folder_get_full_name (summary->priv->folder);
3483 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3484 if (camel_db_delete_uids (parent_store->cdb_w, full_name, uids, NULL) != 0)
3488 camel_folder_summary_touch (summary);
3489 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3490 g_object_thaw_notify (G_OBJECT (summary));
3495 static struct _node *
3496 my_list_append (struct _node **list,
3499 struct _node *ln = *list;
3514 my_list_size (struct _node **list)
3517 struct _node *ln = (struct _node *) list;
3525 /* are these even useful for anything??? */
3526 static CamelMessageInfo *
3527 message_info_new_from_parser (CamelFolderSummary *summary,
3528 CamelMimeParser *mp)
3530 CamelMessageInfo *mi = NULL;
3533 state = camel_mime_parser_state (mp);
3535 case CAMEL_MIME_PARSER_STATE_HEADER:
3536 case CAMEL_MIME_PARSER_STATE_MESSAGE:
3537 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3538 mi = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3541 g_error ("Invalid parser state");
3547 static CamelMessageContentInfo *
3548 content_info_new_from_parser (CamelFolderSummary *summary,
3549 CamelMimeParser *mp)
3551 CamelMessageContentInfo *ci = NULL;
3553 switch (camel_mime_parser_state (mp)) {
3554 case CAMEL_MIME_PARSER_STATE_HEADER:
3555 case CAMEL_MIME_PARSER_STATE_MESSAGE:
3556 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3557 ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3560 camel_content_type_unref (ci->type);
3561 ci->type = camel_mime_parser_content_type (mp);
3562 camel_content_type_ref (ci->type);
3566 g_error ("Invalid parser state");
3572 static CamelMessageInfo *
3573 message_info_new_from_message (CamelFolderSummary *summary,
3574 CamelMimeMessage *msg,
3575 const gchar *bodystructure)
3577 CamelMessageInfo *mi;
3579 mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->message_info_new_from_header (summary, ((CamelMimePart *) msg)->headers);
3580 ((CamelMessageInfoBase *) mi)->bodystructure = g_strdup (bodystructure);
3585 static CamelMessageContentInfo *
3586 content_info_new_from_message (CamelFolderSummary *summary,
3589 CamelMessageContentInfo *ci;
3591 ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->content_info_new_from_header (summary, mp->headers);
3597 summary_format_address (struct _camel_header_raw *h,
3599 const gchar *charset)
3601 struct _camel_header_address *addr;
3604 if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3607 while (isspace ((unsigned) *text))
3610 text = camel_header_unfold (text);
3612 if ((addr = camel_header_address_decode (text, charset))) {
3613 str = camel_header_address_list_format (addr);
3614 camel_header_address_list_clear (&addr);
3624 summary_format_string (struct _camel_header_raw *h,
3626 const gchar *charset)
3630 if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3633 while (isspace ((unsigned) *text))
3636 text = camel_header_unfold (text);
3637 str = camel_header_decode_string (text, charset);
3644 * camel_folder_summary_content_info_new:
3645 * @summary: a #CamelFolderSummary object
3647 * Allocate a new #CamelMessageContentInfo, suitable for adding
3650 * Returns: a newly allocated #CamelMessageContentInfo
3652 CamelMessageContentInfo *
3653 camel_folder_summary_content_info_new (CamelFolderSummary *summary)
3655 CamelFolderSummaryClass *class;
3656 CamelMessageContentInfo *ci;
3658 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3660 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_ALLOC_LOCK);
3661 ci = g_slice_alloc0 (class->content_info_size);
3662 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_ALLOC_LOCK);
3667 static CamelMessageInfo *
3668 message_info_new_from_header (CamelFolderSummary *summary,
3669 struct _camel_header_raw *h)
3671 const gchar *received, *date, *content, *charset = NULL;
3672 struct _camel_header_references *refs, *irt, *scan;
3673 gchar *subject, *from, *to, *cc, *mlist;
3674 CamelContentType *ct = NULL;
3675 CamelMessageInfoBase *mi;
3681 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
3682 digest = g_alloca (length);
3684 mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
3686 if ((content = camel_header_raw_find (&h, "Content-Type", NULL))
3687 && (ct = camel_content_type_decode (content))
3688 && (charset = camel_content_type_param (ct, "charset"))
3689 && (g_ascii_strcasecmp (charset, "us-ascii") == 0))
3692 charset = charset ? camel_iconv_charset_name (charset) : NULL;
3694 subject = summary_format_string (h, "subject", charset);
3695 from = summary_format_address (h, "from", charset);
3696 to = summary_format_address (h, "to", charset);
3697 cc = summary_format_address (h, "cc", charset);
3698 mlist = camel_header_raw_check_mailing_list (&h);
3701 camel_content_type_unref (ct);
3703 mi->subject = camel_pstring_add (subject, TRUE);
3704 mi->from = camel_pstring_add (from, TRUE);
3705 mi->to = camel_pstring_add (to, TRUE);
3706 mi->cc = camel_pstring_add (cc, TRUE);
3707 mi->mlist = camel_pstring_add (mlist, TRUE);
3709 mi->user_flags = NULL;
3710 mi->user_tags = NULL;
3712 if ((date = camel_header_raw_find (&h, "date", NULL)))
3713 mi->date_sent = camel_header_decode_date (date, NULL);
3717 received = camel_header_raw_find (&h, "received", NULL);
3719 received = strrchr (received, ';');
3721 mi->date_received = camel_header_decode_date (received + 1, NULL);
3723 mi->date_received = 0;
3725 msgid = camel_header_msgid_decode (camel_header_raw_find (&h, "message-id", NULL));
3727 GChecksum *checksum;
3729 checksum = g_checksum_new (G_CHECKSUM_MD5);
3730 g_checksum_update (checksum, (guchar *) msgid, -1);
3731 g_checksum_get_digest (checksum, digest, &length);
3732 g_checksum_free (checksum);
3734 memcpy (mi->message_id.id.hash, digest, sizeof (mi->message_id.id.hash));
3738 /* decode our references and in-reply-to headers */
3739 refs = camel_header_references_decode (camel_header_raw_find (&h, "references", NULL));
3740 irt = camel_header_references_inreplyto_decode (camel_header_raw_find (&h, "in-reply-to", NULL));
3743 /* The References field is populated from the "References" and/or "In-Reply-To"
3744 * headers. If both headers exist, take the first thing in the In-Reply-To header
3745 * that looks like a Message-ID, and append it to the References header. */
3753 count = camel_header_references_list_size (&refs);
3754 mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
3758 GChecksum *checksum;
3760 checksum = g_checksum_new (G_CHECKSUM_MD5);
3761 g_checksum_update (checksum, (guchar *) scan->id, -1);
3762 g_checksum_get_digest (checksum, digest, &length);
3763 g_checksum_free (checksum);
3765 memcpy (mi->references->references[count].id.hash, digest, sizeof (mi->message_id.id.hash));
3769 mi->references->size = count;
3770 camel_header_references_list_clear (&refs);
3773 return (CamelMessageInfo *) mi;
3777 message_info_free (CamelFolderSummary *summary,
3778 CamelMessageInfo *info)
3780 CamelFolderSummaryClass *class;
3781 CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
3785 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3786 if (g_hash_table_lookup (summary->priv->loaded_infos, mi->uid) == mi) {
3787 g_hash_table_remove (summary->priv->loaded_infos, mi->uid);
3789 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
3791 camel_pstring_free (mi->uid);
3793 camel_pstring_free (mi->subject);
3794 camel_pstring_free (mi->from);
3795 camel_pstring_free (mi->to);
3796 camel_pstring_free (mi->cc);
3797 camel_pstring_free (mi->mlist);
3798 g_free (mi->bodystructure);
3799 g_free (mi->references);
3800 g_free (mi->preview);
3801 camel_flag_list_free (&mi->user_flags);
3802 camel_tag_list_free (&mi->user_tags);
3804 camel_header_param_list_free (mi->headers);
3807 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3808 g_slice_free1 (class->message_info_size, mi);
3810 g_slice_free (CamelMessageInfoBase, mi);
3813 static CamelMessageContentInfo *
3814 content_info_new_from_header (CamelFolderSummary *summary,
3815 struct _camel_header_raw *h)
3817 CamelMessageContentInfo *ci;
3818 const gchar *charset;
3820 ci = camel_folder_summary_content_info_new (summary);
3822 charset = camel_iconv_locale_charset ();
3823 ci->id = camel_header_msgid_decode (camel_header_raw_find (&h, "content-id", NULL));
3824 ci->description = camel_header_decode_string (camel_header_raw_find (&h, "content-description", NULL), charset);
3825 ci->encoding = camel_content_transfer_encoding_decode (camel_header_raw_find (&h, "content-transfer-encoding", NULL));
3826 ci->type = camel_content_type_decode (camel_header_raw_find (&h, "content-type", NULL));
3832 content_info_free (CamelFolderSummary *summary,
3833 CamelMessageContentInfo *ci)
3835 CamelFolderSummaryClass *class;
3837 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3839 camel_content_type_unref (ci->type);
3841 g_free (ci->description);
3842 g_free (ci->encoding);
3843 g_slice_free1 (class->content_info_size, ci);
3847 next_uid_string (CamelFolderSummary *summary)
3849 return g_strdup_printf ("%u", camel_folder_summary_next_uid (summary));
3854 Now this is where all the "smarts" happen, where the content info is built,
3855 and any indexing and what not is performed
3858 /* must have filter_lock before calling this function */
3859 static CamelMessageContentInfo *
3860 summary_build_content_info (CamelFolderSummary *summary,
3861 CamelMessageInfo *msginfo,
3862 CamelMimeParser *mp)
3867 CamelMessageContentInfo *info = NULL;
3868 CamelContentType *ct;
3869 gint enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
3870 CamelFolderSummaryPrivate *p = summary->priv;
3871 CamelMimeFilter *mfc;
3872 CamelMessageContentInfo *part;
3873 const gchar *calendar_header;
3875 d (printf ("building content info\n"));
3877 /* start of this part */
3878 state = camel_mime_parser_step (mp, &buffer, &len);
3880 if (summary->priv->build_content)
3881 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_parser (summary, mp);
3884 case CAMEL_MIME_PARSER_STATE_HEADER:
3885 /* check content type for indexing, then read body */
3886 ct = camel_mime_parser_content_type (mp);
3887 /* update attachments flag as we go */
3888 if (camel_content_type_is (ct, "application", "pgp-signature")
3890 || camel_content_type_is (ct, "application", "x-pkcs7-signature")
3891 || camel_content_type_is (ct, "application", "pkcs7-signature")
3894 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3896 calendar_header = camel_mime_parser_header (mp, "Content-class", NULL);
3897 if (calendar_header && g_ascii_strcasecmp (calendar_header, "urn:content-classes:calendarmessage") != 0)
3898 calendar_header = NULL;
3900 if (!calendar_header)
3901 calendar_header = camel_mime_parser_header (mp, "X-Calendar-Attachment", NULL);
3903 if (calendar_header || camel_content_type_is (ct, "text", "calendar"))
3904 camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
3906 if (p->index && camel_content_type_is (ct, "text", "*")) {
3908 const gchar *charset;
3910 d (printf ("generating index:\n"));
3912 encoding = camel_content_transfer_encoding_decode (camel_mime_parser_header (mp, "content-transfer-encoding", NULL));
3914 if (!g_ascii_strcasecmp (encoding, "base64")) {
3915 d (printf (" decoding base64\n"));
3916 if (p->filter_64 == NULL)
3917 p->filter_64 = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
3919 camel_mime_filter_reset (p->filter_64);
3920 enc_id = camel_mime_parser_filter_add (mp, p->filter_64);
3921 } else if (!g_ascii_strcasecmp (encoding, "quoted-printable")) {
3922 d (printf (" decoding quoted-printable\n"));
3923 if (p->filter_qp == NULL)
3924 p->filter_qp = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_DEC);
3926 camel_mime_filter_reset (p->filter_qp);
3927 enc_id = camel_mime_parser_filter_add (mp, p->filter_qp);
3928 } else if (!g_ascii_strcasecmp (encoding, "x-uuencode")) {
3929 d (printf (" decoding x-uuencode\n"));
3930 if (p->filter_uu == NULL)
3931 p->filter_uu = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_UU_DEC);
3933 camel_mime_filter_reset (p->filter_uu);
3934 enc_id = camel_mime_parser_filter_add (mp, p->filter_uu);
3936 d (printf (" ignoring encoding %s\n", encoding));
3941 charset = camel_content_type_param (ct, "charset");
3943 && !(g_ascii_strcasecmp (charset, "us-ascii") == 0
3944 || g_ascii_strcasecmp (charset, "utf-8") == 0)) {
3945 d (printf (" Adding conversion filter from %s to UTF-8\n", charset));
3946 mfc = g_hash_table_lookup (p->filter_charset, charset);
3948 mfc = camel_mime_filter_charset_new (charset, "UTF-8");
3950 g_hash_table_insert (p->filter_charset, g_strdup (charset), mfc);
3952 camel_mime_filter_reset ((CamelMimeFilter *) mfc);
3955 chr_id = camel_mime_parser_filter_add (mp, mfc);
3957 w (g_warning ("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset));
3961 /* we do charset conversions before this filter, which isn't strictly correct,
3962 * but works in most cases */
3963 if (camel_content_type_is (ct, "text", "html")) {
3964 if (p->filter_html == NULL)
3965 p->filter_html = camel_mime_filter_html_new ();
3967 camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
3968 html_id = camel_mime_parser_filter_add (mp, (CamelMimeFilter *) p->filter_html);
3971 /* and this filter actually does the indexing */
3972 idx_id = camel_mime_parser_filter_add (mp, p->filter_index);
3974 /* and scan/index everything */
3975 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
3977 /* and remove the filters */
3978 camel_mime_parser_filter_remove (mp, enc_id);
3979 camel_mime_parser_filter_remove (mp, chr_id);
3980 camel_mime_parser_filter_remove (mp, html_id);
3981 camel_mime_parser_filter_remove (mp, idx_id);
3983 case CAMEL_MIME_PARSER_STATE_MULTIPART:
3984 d (printf ("Summarising multipart\n"));
3985 /* update attachments flag as we go */
3986 ct = camel_mime_parser_content_type (mp);
3987 if (camel_content_type_is (ct, "multipart", "mixed"))
3988 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3989 if (camel_content_type_is (ct, "multipart", "signed")
3990 || camel_content_type_is (ct, "multipart", "encrypted"))
3991 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3993 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
3994 camel_mime_parser_unstep (mp);
3995 part = summary_build_content_info (summary, msginfo, mp);
3997 part->parent = info;
3998 my_list_append ((struct _node **) &info->childs, (struct _node *) part);
4002 case CAMEL_MIME_PARSER_STATE_MESSAGE:
4003 d (printf ("Summarising message\n"));
4004 /* update attachments flag as we go */
4005 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
4007 part = summary_build_content_info (summary, msginfo, mp);
4009 part->parent = info;
4010 my_list_append ((struct _node **) &info->childs, (struct _node *) part);
4012 state = camel_mime_parser_step (mp, &buffer, &len);
4013 if (state != CAMEL_MIME_PARSER_STATE_MESSAGE_END) {
4014 g_error ("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
4015 camel_mime_parser_unstep (mp);
4020 d (printf ("finished building content info\n"));
4025 /* build the content-info, from a message */
4026 /* this needs the filter lock since it uses filters to perform indexing */
4027 static CamelMessageContentInfo *
4028 summary_build_content_info_message (CamelFolderSummary *summary,
4029 CamelMessageInfo *msginfo,
4030 CamelMimePart *object)
4032 CamelDataWrapper *containee;
4034 CamelFolderSummaryPrivate *p = summary->priv;
4035 CamelMessageContentInfo *info = NULL, *child;
4036 CamelContentType *ct;
4037 const struct _camel_header_raw *header;
4039 if (summary->priv->build_content)
4040 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_message (summary, object);
4042 containee = camel_medium_get_content (CAMEL_MEDIUM (object));
4044 if (containee == NULL)
4047 /* TODO: I find it odd that get_part and get_content do not
4048 * add a reference, probably need fixing for multithreading */
4050 /* check for attachments */
4051 ct = ((CamelDataWrapper *) containee)->mime_type;
4052 if (camel_content_type_is (ct, "multipart", "*")) {
4053 if (camel_content_type_is (ct, "multipart", "mixed"))
4054 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
4055 if (camel_content_type_is (ct, "multipart", "signed")
4056 || camel_content_type_is (ct, "multipart", "encrypted"))
4057 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
4058 } else if (camel_content_type_is (ct, "application", "pgp-signature")
4060 || camel_content_type_is (ct, "application", "x-pkcs7-signature")
4061 || camel_content_type_is (ct, "application", "pkcs7-signature")
4064 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
4067 for (header = object->headers; header; header = header->next) {
4068 const gchar *value = header->value;
4070 /* skip preceding spaces in the value */
4071 while (value && *value && isspace (*value))
4074 if (header->name && value && (
4075 (g_ascii_strcasecmp (header->name, "Content-class") == 0 && g_ascii_strcasecmp (value, "urn:content-classes:calendarmessage") == 0) ||
4076 (g_ascii_strcasecmp (header->name, "X-Calendar-Attachment") == 0)))
4080 if (header || camel_content_type_is (ct, "text", "calendar"))
4081 camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
4083 /* using the object types is more accurate than using the mime/types */
4084 if (CAMEL_IS_MULTIPART (containee)) {
4085 parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
4087 for (i = 0; i < parts; i++) {
4088 CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
4090 child = summary_build_content_info_message (summary, msginfo, part);
4092 child->parent = info;
4093 my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4096 } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
4097 /* for messages we only look at its contents */
4098 child = summary_build_content_info_message (summary, msginfo, (CamelMimePart *) containee);
4100 child->parent = info;
4101 my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4103 } else if (p->filter_stream
4104 && camel_content_type_is (ct, "text", "*")) {
4105 gint html_id = -1, idx_id = -1;
4107 /* pre-attach html filter if required, otherwise just index filter */
4108 if (camel_content_type_is (ct, "text", "html")) {
4109 if (p->filter_html == NULL)
4110 p->filter_html = camel_mime_filter_html_new ();
4112 camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
4113 html_id = camel_stream_filter_add (
4114 CAMEL_STREAM_FILTER (p->filter_stream),
4115 (CamelMimeFilter *) p->filter_html);
4117 idx_id = camel_stream_filter_add (
4118 CAMEL_STREAM_FILTER (p->filter_stream),
4121 /* FIXME Pass a GCancellable and GError here. */
4122 camel_data_wrapper_decode_to_stream_sync (
4123 containee, p->filter_stream, NULL, NULL);
4124 camel_stream_flush (p->filter_stream, NULL, NULL);
4126 camel_stream_filter_remove (
4127 CAMEL_STREAM_FILTER (p->filter_stream), idx_id);
4128 camel_stream_filter_remove (
4129 CAMEL_STREAM_FILTER (p->filter_stream), html_id);
4137 * @list: the address of a #CamelFlag list
4138 * @name: name of the flag to get
4140 * Find the state of the flag @name in @list.
4142 * Returns: the state of the flag (%TRUE or %FALSE)
4145 camel_flag_get (CamelFlag **list,
4151 if (!strcmp (flag->name, name))
4160 * @list: the address of a #CamelFlag list
4161 * @name: name of the flag to set or change
4162 * @value: the value to set on the flag
4164 * Set the state of a flag @name in the list @list to @value.
4166 * Returns: %TRUE if the value of the flag has been changed or %FALSE
4170 camel_flag_set (CamelFlag **list,
4174 CamelFlag *flag, *tmp;
4179 /* this 'trick' works because flag->next is the first element */
4180 flag = (CamelFlag *) list;
4181 while (flag->next) {
4183 if (!strcmp (flag->next->name, name)) {
4185 flag->next = tmp->next;
4194 tmp = g_malloc (sizeof (*tmp) + strlen (name));
4195 strcpy (tmp->name, name);
4203 * camel_flag_list_size:
4204 * @list: the address of a #CamelFlag list
4206 * Get the length of the flag list.
4208 * Returns: the number of flags in the list
4211 camel_flag_list_size (CamelFlag **list)
4225 * camel_flag_list_free:
4226 * @list: the address of a #CamelFlag list
4228 * Free the memory associated with the flag list @list.
4231 camel_flag_list_free (CamelFlag **list)
4233 CamelFlag *flag, *tmp;
4244 * camel_flag_list_copy:
4245 * @to: the address of the #CamelFlag list to copy to
4246 * @from: the address of the #CamelFlag list to copy from
4250 * Returns: %TRUE if @to is changed or %FALSE otherwise
4253 camel_flag_list_copy (CamelFlag **to,
4256 CamelFlag *flag, *tmp;
4257 gint changed = FALSE;
4259 if (*to == NULL && from == NULL)
4262 /* Remove any now-missing flags */
4263 flag = (CamelFlag *) to;
4264 while (flag->next) {
4266 if (!camel_flag_get (from, tmp->name)) {
4267 flag->next = tmp->next;
4275 /* Add any new flags */
4278 changed |= camel_flag_set (to, flag->name, TRUE);
4287 * @list: the address of a #CamelTag list
4288 * @name: name of the tag to get
4290 * Find the flag @name in @list and get the value.
4292 * Returns: the value of the flag or %NULL if unset
4295 camel_tag_get (CamelTag **list,
4302 if (!strcmp (tag->name, name))
4303 return (const gchar *) tag->value;
4311 * @list: the address of a #CamelTag list
4312 * @name: name of the tag to set
4313 * @value: value to set on the tag
4315 * Set the tag @name in the tag list @list to @value.
4317 * Returns: %TRUE if the value on the tag changed or %FALSE otherwise
4320 camel_tag_set (CamelTag **list,
4324 CamelTag *tag, *tmp;
4326 /* this 'trick' works because tag->next is the first element */
4327 tag = (CamelTag *) list;
4330 if (!strcmp (tmp->name, name)) {
4331 if (value == NULL) { /* clear it? */
4332 tag->next = tmp->next;
4333 g_free (tmp->value);
4336 } else if (strcmp (tmp->value, value)) { /* has it changed? */
4337 g_free (tmp->value);
4338 tmp->value = g_strdup (value);
4347 tmp = g_malloc (sizeof (*tmp) + strlen (name));
4348 strcpy (tmp->name, name);
4349 tmp->value = g_strdup (value);
4358 * camel_tag_list_size:
4359 * @list: the address of a #CamelTag list
4361 * Get the number of tags present in the tag list @list.
4363 * Returns: the number of tags
4366 camel_tag_list_size (CamelTag **list)
4380 rem_tag (gchar *key,
4384 camel_tag_set (to, key, NULL);
4388 * camel_tag_list_copy:
4389 * @to: the address of the #CamelTag list to copy to
4390 * @from: the address of the #CamelTag list to copy from
4394 * Returns: %TRUE if @to is changed or %FALSE otherwise
4397 camel_tag_list_copy (CamelTag **to,
4400 gint changed = FALSE;
4404 if (*to == NULL && from == NULL)
4407 left = g_hash_table_new (g_str_hash, g_str_equal);
4410 g_hash_table_insert (left, tag->name, tag);
4416 changed |= camel_tag_set (to, tag->name, tag->value);
4417 g_hash_table_remove (left, tag->name);
4421 if (g_hash_table_size (left) > 0) {
4422 g_hash_table_foreach (left, (GHFunc) rem_tag, to);
4425 g_hash_table_destroy (left);
4431 * camel_tag_list_free:
4432 * @list: the address of a #CamelTag list
4434 * Free the tag list @list.
4437 camel_tag_list_free (CamelTag **list)
4439 CamelTag *tag, *tmp;
4443 g_free (tag->value);
4450 static struct flag_names_t {
4454 { "answered", CAMEL_MESSAGE_ANSWERED },
4455 { "deleted", CAMEL_MESSAGE_DELETED },
4456 { "draft", CAMEL_MESSAGE_DRAFT },
4457 { "flagged", CAMEL_MESSAGE_FLAGGED },
4458 { "seen", CAMEL_MESSAGE_SEEN },
4459 { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
4460 { "junk", CAMEL_MESSAGE_JUNK },
4461 { "notjunk", CAMEL_MESSAGE_NOTJUNK },
4462 { "secure", CAMEL_MESSAGE_SECURE },
4467 * camel_system_flag:
4468 * @name: name of a system flag
4470 * Returns: the integer value of the system flag string
4473 camel_system_flag (const gchar *name)
4475 struct flag_names_t *flag;
4477 g_return_val_if_fail (name != NULL, 0);
4479 for (flag = flag_names; flag->name; flag++)
4480 if (!g_ascii_strcasecmp (name, flag->name))
4487 * camel_system_flag_get:
4488 * @flags: bitwise system flags
4489 * @name: name of the flag to check for
4491 * Find the state of the flag @name in @flags.
4493 * Returns: %TRUE if the named flag is set or %FALSE otherwise
4496 camel_system_flag_get (CamelMessageFlags flags,
4499 g_return_val_if_fail (name != NULL, FALSE);
4501 return flags & camel_system_flag (name);
4505 * camel_message_info_new:
4506 * @summary: a #CamelFolderSummary object or %NULL
4508 * Create a new #CamelMessageInfo.
4510 * Returns: a new #CamelMessageInfo
4513 camel_message_info_new (CamelFolderSummary *summary)
4515 CamelFolderSummaryClass *class;
4516 CamelMessageInfo *info;
4519 camel_folder_summary_lock (summary, CAMEL_FOLDER_SUMMARY_ALLOC_LOCK);
4520 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
4521 info = g_slice_alloc0 (class->message_info_size);
4522 camel_folder_summary_unlock (summary, CAMEL_FOLDER_SUMMARY_ALLOC_LOCK);
4524 info = g_slice_alloc0 (sizeof (CamelMessageInfoBase));
4528 info->summary = summary;
4530 /* We assume that mi is always dirty unless freshly read or just saved*/
4531 ((CamelMessageInfoBase *) info)->dirty = TRUE;
4537 * camel_message_info_ref:
4538 * @info: a #CamelMessageInfo
4540 * Reference an info.
4543 camel_message_info_ref (gpointer o)
4545 CamelMessageInfo *mi = o;
4548 camel_folder_summary_lock (mi->summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
4549 g_assert (mi->refcount >= 1);
4551 camel_folder_summary_unlock (mi->summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
4553 GLOBAL_INFO_LOCK (info);
4554 g_assert (mi->refcount >= 1);
4556 GLOBAL_INFO_UNLOCK (info);
4563 * camel_message_info_new_from_header:
4564 * @summary: a #CamelFolderSummary object or %NULL
4565 * @header: raw header
4567 * Create a new #CamelMessageInfo pre-populated with info from
4570 * Returns: a new #CamelMessageInfo
4573 camel_message_info_new_from_header (CamelFolderSummary *summary,
4574 struct _camel_header_raw *header)
4576 if (summary != NULL)
4577 return CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->
4578 message_info_new_from_header (summary, header);
4580 return message_info_new_from_header (NULL, header);
4584 * camel_message_info_free:
4585 * @info: a #CamelMessageInfo
4587 * Unref's and potentially frees a #CamelMessageInfo and its contents.
4590 camel_message_info_free (gpointer o)
4592 CamelMessageInfo *mi = o;
4594 g_return_if_fail (mi != NULL);
4597 camel_folder_summary_lock (mi->summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
4599 if (mi->refcount >= 1)
4601 if (mi->refcount > 0) {
4602 camel_folder_summary_unlock (mi->summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
4606 camel_folder_summary_unlock (mi->summary, CAMEL_FOLDER_SUMMARY_REF_LOCK);
4608 /* FIXME: this is kinda busted, should really be handled by message info free */
4609 if (mi->summary->priv->build_content
4610 && ((CamelMessageInfoBase *) mi)->content) {
4611 camel_folder_summary_content_info_free (mi->summary, ((CamelMessageInfoBase *) mi)->content);
4612 ((CamelMessageInfoBase *) mi)->content = NULL;
4615 CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->message_info_free (mi->summary, mi);
4617 GLOBAL_INFO_LOCK (info);
4619 if (mi->refcount > 0) {
4620 GLOBAL_INFO_UNLOCK (info);
4623 GLOBAL_INFO_UNLOCK (info);
4625 message_info_free (NULL, mi);
4630 * camel_message_info_clone:
4631 * @info: a #CamelMessageInfo
4633 * Duplicate a #CamelMessageInfo.
4635 * Returns: the duplicated #CamelMessageInfo
4638 camel_message_info_clone (gconstpointer o)
4640 const CamelMessageInfo *mi = o;
4643 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->message_info_clone (mi->summary, mi);
4645 return message_info_clone (NULL, mi);
4649 * camel_message_info_ptr:
4650 * @mi: a #CamelMessageInfo
4653 * Generic accessor method for getting pointer data.
4655 * Returns: the pointer data
4658 camel_message_info_ptr (const CamelMessageInfo *mi,
4662 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_ptr (mi, id);
4664 return info_ptr (mi, id);
4668 * camel_message_info_uint32:
4669 * @mi: a #CamelMessageInfo
4672 * Generic accessor method for getting 32bit gint data.
4674 * Returns: the gint data
4677 camel_message_info_uint32 (const CamelMessageInfo *mi,
4681 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_uint32 (mi, id);
4683 return info_uint32 (mi, id);
4687 * camel_message_info_time:
4688 * @mi: a #CamelMessageInfo
4691 * Generic accessor method for getting time_t data.
4693 * Returns: the time_t data
4696 camel_message_info_time (const CamelMessageInfo *mi,
4700 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_time (mi, id);
4702 return info_time (mi, id);
4706 * camel_message_info_user_flag:
4707 * @mi: a #CamelMessageInfo
4708 * @id: user flag to get
4710 * Get the state of a user flag named @id.
4712 * Returns: the state of the user flag
4715 camel_message_info_user_flag (const CamelMessageInfo *mi,
4719 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_user_flag (mi, id);
4721 return info_user_flag (mi, id);
4725 * camel_message_info_user_tag:
4726 * @mi: a #CamelMessageInfo
4727 * @id: user tag to get
4729 * Get the value of a user tag named @id.
4731 * Returns: the value of the user tag
4734 camel_message_info_user_tag (const CamelMessageInfo *mi,
4738 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_user_tag (mi, id);
4740 return info_user_tag (mi, id);
4744 * camel_message_info_set_flags:
4745 * @mi: a #CamelMessageInfo
4746 * @flags: mask of flags to change
4747 * @set: state the flags should be changed to
4749 * Change the state of the system flags on the #CamelMessageInfo
4751 * Returns: %TRUE if any of the flags changed or %FALSE otherwise
4754 camel_message_info_set_flags (CamelMessageInfo *mi,
4755 CamelMessageFlags flags,
4759 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_set_flags (mi, flags, set);
4761 return info_set_flags (mi, flags, set);
4765 * camel_message_info_set_user_flag:
4766 * @mi: a #CamelMessageInfo
4767 * @id: name of the user flag to set
4768 * @state: state to set the flag to
4770 * Set the state of a user flag on a #CamelMessageInfo.
4772 * Returns: %TRUE if the state changed or %FALSE otherwise
4775 camel_message_info_set_user_flag (CamelMessageInfo *mi,
4780 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_set_user_flag (mi, id, state);
4782 return info_set_user_flag (mi, id, state);
4786 * camel_message_info_set_user_tag:
4787 * @mi: a #CamelMessageInfo
4788 * @id: name of the user tag to set
4789 * @val: value to set
4791 * Set the value of a user tag on a #CamelMessageInfo.
4793 * Returns: %TRUE if the value changed or %FALSE otherwise
4796 camel_message_info_set_user_tag (CamelMessageInfo *mi,
4801 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->info_set_user_tag (mi, id, val);
4803 return info_set_user_tag (mi, id, val);
4807 camel_content_info_dump (CamelMessageContentInfo *ci,
4812 p = alloca (depth * 4 + 1);
4813 memset (p, ' ', depth * 4);
4817 printf ("%s<empty>\n", p);
4822 printf ("%scontent-type: %s/%s\n", p, ci->type->type ? ci->type->type : "(null)",
4823 ci->type->subtype ? ci->type->subtype : "(null)");
4825 printf ("%scontent-type: <unset>\n", p);
4826 printf ("%scontent-transfer-encoding: %s\n", p, ci->encoding ? ci->encoding : "(null)");
4827 printf ("%scontent-description: %s\n", p, ci->description ? ci->description : "(null)");
4828 printf ("%ssize: %lu\n", p, (gulong) ci->size);
4831 camel_content_info_dump (ci, depth + 1);
4837 camel_message_info_dump (CamelMessageInfo *mi)
4840 printf ("No message?\n");
4844 printf ("Subject: %s\n", camel_message_info_subject (mi));
4845 printf ("To: %s\n", camel_message_info_to (mi));
4846 printf ("Cc: %s\n", camel_message_info_cc (mi));
4847 printf ("mailing list: %s\n", camel_message_info_mlist (mi));
4848 printf ("From: %s\n", camel_message_info_from (mi));
4849 printf ("UID: %s\n", camel_message_info_uid (mi));
4850 printf ("Flags: %04x\n", camel_message_info_flags (mi));
4851 camel_content_info_dump (((CamelMessageInfoBase *) mi)->content, 0);
4855 compare_strings (const gchar *str1,
4858 if (str1 && str2 && !g_ascii_strcasecmp (str1, str2))
4860 else if (!str1 && !str2)
4867 match_content_type (CamelContentType *info_ctype,
4868 CamelContentType *ctype)
4870 const gchar *name1, *name2;
4872 if (!compare_strings (info_ctype->type, ctype->type))
4874 if (!compare_strings (info_ctype->subtype, ctype->subtype))
4877 name1 = camel_content_type_param (info_ctype, "name");
4878 name2 = camel_content_type_param (ctype, "name");
4879 if (!compare_strings (name1, name2))
4886 * camel_folder_summary_guess_content_info:
4887 * @mi: a #CamelMessageInfo
4888 * @ctype: a #CamelContentType
4890 * FIXME Document me!
4894 const CamelMessageContentInfo *
4895 camel_folder_summary_guess_content_info (CamelMessageInfo *mi,
4896 CamelContentType *ctype)
4898 const CamelMessageContentInfo *ci = camel_message_info_content (mi);
4901 const CamelMessageContentInfo *child = ci;
4904 if (child->type && match_content_type (child->type, ctype))
4907 child = child->next;
4908 } while (child != NULL);
4917 * camel_folder_summary_lock:
4918 * @summary: a #CamelFolderSummary
4919 * @lock: lock type to lock
4921 * Locks @summary's @lock. Unlock it with camel_folder_summary_unlock().
4926 camel_folder_summary_lock (CamelFolderSummary *summary,
4927 CamelFolderSummaryLock lock)
4929 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4932 case CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK:
4933 g_static_rec_mutex_lock (&summary->priv->summary_lock);
4935 case CAMEL_FOLDER_SUMMARY_IO_LOCK:
4936 g_static_rec_mutex_lock (&summary->priv->io_lock);
4938 case CAMEL_FOLDER_SUMMARY_FILTER_LOCK:
4939 g_static_rec_mutex_lock (&summary->priv->filter_lock);
4941 case CAMEL_FOLDER_SUMMARY_ALLOC_LOCK:
4942 g_static_rec_mutex_lock (&summary->priv->alloc_lock);
4944 case CAMEL_FOLDER_SUMMARY_REF_LOCK:
4945 g_static_rec_mutex_lock (&summary->priv->ref_lock);
4948 g_return_if_reached ();
4953 * camel_folder_summary_unlock:
4954 * @summary: a #CamelFolderSummary
4955 * @lock: lock type to unlock
4957 * Unlocks @summary's @lock, previously locked with camel_folder_summary_lock().
4962 camel_folder_summary_unlock (CamelFolderSummary *summary,
4963 CamelFolderSummaryLock lock)
4965 g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4968 case CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK:
4969 g_static_rec_mutex_unlock (&summary->priv->summary_lock);
4971 case CAMEL_FOLDER_SUMMARY_IO_LOCK:
4972 g_static_rec_mutex_unlock (&summary->priv->io_lock);
4974 case CAMEL_FOLDER_SUMMARY_FILTER_LOCK:
4975 g_static_rec_mutex_unlock (&summary->priv->filter_lock);
4977 case CAMEL_FOLDER_SUMMARY_ALLOC_LOCK:
4978 g_static_rec_mutex_unlock (&summary->priv->alloc_lock);
4980 case CAMEL_FOLDER_SUMMARY_REF_LOCK:
4981 g_static_rec_mutex_unlock (&summary->priv->ref_lock);
4984 g_return_if_reached ();
4989 bdata_extract_digit (/* const */ gchar **part)
4991 if (!part || !*part || !**part)
5000 return strtoul (*part, part, 10);
5003 /* expecting "digit-value", where digit is length of the value */
5005 bdata_extract_string (/* const */ gchar **part)
5010 len = bdata_extract_digit (part);
5012 /* might be a '-' sign */
5013 if (part && *part && **part)
5016 if (len <= 0 || !part || !*part || !**part)
5017 return g_strdup ("");
5020 return g_strdup ("");
5022 has_len = strlen (*part);
5026 val = g_strndup (*part, len);