updated changelog
[platform/upstream/evolution-data-server.git] / camel / camel-folder-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * Authors: Michael Zucchi <notzed@ximian.com>
6  *
7  * This library is free software you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
35
36 #include "camel-db.h"
37 #include "camel-debug.h"
38 #include "camel-file-utils.h"
39 #include "camel-folder-summary.h"
40 #include "camel-folder.h"
41 #include "camel-iconv.h"
42 #include "camel-mime-filter-basic.h"
43 #include "camel-mime-filter-charset.h"
44 #include "camel-mime-filter-html.h"
45 #include "camel-mime-filter-index.h"
46 #include "camel-mime-filter.h"
47 #include "camel-mime-message.h"
48 #include "camel-multipart.h"
49 #include "camel-session.h"
50 #include "camel-stream-filter.h"
51 #include "camel-stream-mem.h"
52 #include "camel-stream-null.h"
53 #include "camel-string-utils.h"
54 #include "camel-store.h"
55 #include "camel-vee-folder.h"
56 #include "camel-vtrash-folder.h"
57 #include "camel-mime-part-utils.h"
58
59 #define CAMEL_FOLDER_SUMMARY_GET_PRIVATE(obj) \
60         (G_TYPE_INSTANCE_GET_PRIVATE \
61         ((obj), CAMEL_TYPE_FOLDER_SUMMARY, CamelFolderSummaryPrivate))
62
63 /* Make 5 minutes as default cache drop */
64 #define SUMMARY_CACHE_DROP 300
65 #define dd(x) if (camel_debug("sync")) x
66
67 struct _CamelFolderSummaryPrivate {
68         GHashTable *filter_charset;     /* CamelMimeFilterCharset's indexed by source charset */
69
70         struct _CamelMimeFilter *filter_index;
71         struct _CamelMimeFilter *filter_64;
72         struct _CamelMimeFilter *filter_qp;
73         struct _CamelMimeFilter *filter_uu;
74         struct _CamelMimeFilter *filter_save;
75         struct _CamelMimeFilter *filter_html;
76
77         struct _CamelStream *filter_stream;
78
79         struct _CamelIndex *index;
80
81         GRecMutex summary_lock; /* for the summary hashtable/array */
82         GRecMutex filter_lock;  /* for accessing any of the filtering/indexing stuff, since we share them */
83
84         gboolean need_preview;
85         GHashTable *preview_updates;
86
87         guint32 nextuid;        /* next uid? */
88         guint32 saved_count;    /* how many were saved/loaded */
89         guint32 unread_count;   /* handy totals */
90         guint32 deleted_count;
91         guint32 junk_count;
92         guint32 junk_not_deleted_count;
93         guint32 visible_count;
94
95         gboolean build_content; /* do we try and parse/index the content, or not? */
96
97         GHashTable *uids; /* uids of all known message infos; the 'value' are used flags for the message info */
98         GHashTable *loaded_infos; /* uid->CamelMessageInfo *, those currently in memory */
99
100         struct _CamelFolder *folder; /* parent folder, for events */
101         time_t cache_load_time;
102         guint timeout_handle;
103 };
104
105 /* this should probably be conditional on it existing */
106 #define USE_BSEARCH
107
108 #define d(x)
109 #define io(x)                   /* io debug */
110 #define w(x)
111
112 #define CAMEL_FOLDER_SUMMARY_VERSION (14)
113
114 /* trivial lists, just because ... */
115 struct _node {
116         struct _node *next;
117 };
118
119 static void cfs_schedule_info_release_timer (CamelFolderSummary *summary);
120
121 static struct _node *my_list_append (struct _node **list, struct _node *n);
122 static gint my_list_size (struct _node **list);
123
124 static CamelMessageInfo * message_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
125 static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
126 static CamelMessageInfo * message_info_new_from_message (CamelFolderSummary *summary, CamelMimeMessage *msg, const gchar *bodystructure);
127 static void               message_info_free (CamelFolderSummary *, CamelMessageInfo *);
128
129 static CamelMessageContentInfo * content_info_new_from_header (CamelFolderSummary *, struct _camel_header_raw *);
130 static CamelMessageContentInfo * content_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
131 static CamelMessageContentInfo * content_info_new_from_message (CamelFolderSummary *summary, CamelMimePart *mp);
132 static void                      content_info_free (CamelFolderSummary *, CamelMessageContentInfo *);
133
134 static gint save_message_infos_to_db (CamelFolderSummary *summary, GError **error);
135 static gint camel_read_mir_callback (gpointer  ref, gint ncol, gchar ** cols, gchar ** name);
136
137 static gchar *next_uid_string (CamelFolderSummary *summary);
138
139 static CamelMessageContentInfo * summary_build_content_info (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimeParser *mp);
140 static CamelMessageContentInfo * summary_build_content_info_message (CamelFolderSummary *summary, CamelMessageInfo *msginfo, CamelMimePart *object);
141
142 static CamelMessageInfo * message_info_from_uid (CamelFolderSummary *summary, const gchar *uid);
143
144 enum {
145         PROP_0,
146         PROP_FOLDER,
147         PROP_SAVED_COUNT,
148         PROP_UNREAD_COUNT,
149         PROP_DELETED_COUNT,
150         PROP_JUNK_COUNT,
151         PROP_JUNK_NOT_DELETED_COUNT,
152         PROP_VISIBLE_COUNT,
153         PROP_BUILD_CONTENT,
154         PROP_NEED_PREVIEW
155 };
156
157 G_DEFINE_TYPE (CamelFolderSummary, camel_folder_summary, G_TYPE_OBJECT)
158
159 static gboolean
160 remove_each_item (gpointer uid,
161                   gpointer mi,
162                   gpointer user_data)
163 {
164         GSList **to_remove_infos = user_data;
165
166         *to_remove_infos = g_slist_prepend (*to_remove_infos, mi);
167
168         return TRUE;
169 }
170
171 static void
172 remove_all_loaded (CamelFolderSummary *summary)
173 {
174         GSList *to_remove_infos = NULL;
175
176         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
177
178         camel_folder_summary_lock (summary);
179
180         g_hash_table_foreach_remove (summary->priv->loaded_infos, remove_each_item, &to_remove_infos);
181
182         g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_unref, NULL);
183         g_slist_free (to_remove_infos);
184
185         camel_folder_summary_unlock (summary);
186 }
187
188 static void
189 free_o_name (gpointer key,
190              gpointer value,
191              gpointer data)
192 {
193         g_object_unref (value);
194         g_free (key);
195 }
196
197 static void
198 folder_summary_dispose (GObject *object)
199 {
200         CamelFolderSummaryPrivate *priv;
201
202         priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (object);
203
204         if (priv->timeout_handle) {
205                 /* this should not happen, because the release timer
206                  * holds a reference on object */
207                 g_source_remove (priv->timeout_handle);
208                 priv->timeout_handle = 0;
209         }
210
211         g_clear_object (&priv->filter_index);
212         g_clear_object (&priv->filter_64);
213         g_clear_object (&priv->filter_qp);
214         g_clear_object (&priv->filter_uu);
215         g_clear_object (&priv->filter_save);
216         g_clear_object (&priv->filter_html);
217         g_clear_object (&priv->filter_stream);
218         g_clear_object (&priv->filter_index);
219
220         if (priv->folder) {
221                 g_object_weak_unref (G_OBJECT (priv->folder), (GWeakNotify) g_nullify_pointer, &priv->folder);
222                 priv->folder = NULL;
223         }
224
225         /* Chain up to parent's dispose() method. */
226         G_OBJECT_CLASS (camel_folder_summary_parent_class)->dispose (object);
227 }
228
229 static void
230 folder_summary_finalize (GObject *object)
231 {
232         CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (object);
233         CamelFolderSummaryPrivate *priv = summary->priv;
234
235         g_hash_table_destroy (priv->uids);
236         remove_all_loaded (summary);
237         g_hash_table_destroy (priv->loaded_infos);
238
239         g_hash_table_foreach (priv->filter_charset, free_o_name, NULL);
240         g_hash_table_destroy (priv->filter_charset);
241
242         g_hash_table_destroy (priv->preview_updates);
243
244         g_rec_mutex_clear (&priv->summary_lock);
245         g_rec_mutex_clear (&priv->filter_lock);
246
247         /* Chain up to parent's finalize() method. */
248         G_OBJECT_CLASS (camel_folder_summary_parent_class)->finalize (object);
249 }
250
251 static void
252 folder_summary_set_folder (CamelFolderSummary *summary,
253                            CamelFolder *folder)
254 {
255         g_return_if_fail (summary->priv->folder == NULL);
256         /* folder can be NULL in certain cases, see maildir-store */
257
258         summary->priv->folder = folder;
259         if (folder)
260                 g_object_weak_ref (G_OBJECT (folder), (GWeakNotify) g_nullify_pointer, &summary->priv->folder);
261 }
262
263 static void
264 folder_summary_set_property (GObject *object,
265                              guint property_id,
266                              const GValue *value,
267                              GParamSpec *pspec)
268 {
269         switch (property_id) {
270                 case PROP_FOLDER:
271                         folder_summary_set_folder (
272                                 CAMEL_FOLDER_SUMMARY (object),
273                                 CAMEL_FOLDER (g_value_get_object (value)));
274                         return;
275
276                 case PROP_BUILD_CONTENT:
277                         camel_folder_summary_set_build_content (
278                                 CAMEL_FOLDER_SUMMARY (object),
279                                 g_value_get_boolean (value));
280                         return;
281
282                 case PROP_NEED_PREVIEW:
283                         camel_folder_summary_set_need_preview (
284                                 CAMEL_FOLDER_SUMMARY (object),
285                                 g_value_get_boolean (value));
286                         return;
287         }
288
289         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
290 }
291
292 static void
293 folder_summary_get_property (GObject *object,
294                              guint property_id,
295                              GValue *value,
296                              GParamSpec *pspec)
297 {
298         switch (property_id) {
299                 case PROP_FOLDER:
300                         g_value_set_object (
301                                 value,
302                                 camel_folder_summary_get_folder (
303                                 CAMEL_FOLDER_SUMMARY (object)));
304                         return;
305
306                 case PROP_SAVED_COUNT:
307                         g_value_set_uint (
308                                 value,
309                                 camel_folder_summary_get_saved_count (
310                                 CAMEL_FOLDER_SUMMARY (object)));
311                         return;
312
313                 case PROP_UNREAD_COUNT:
314                         g_value_set_uint (
315                                 value,
316                                 camel_folder_summary_get_unread_count (
317                                 CAMEL_FOLDER_SUMMARY (object)));
318                         return;
319
320                 case PROP_DELETED_COUNT:
321                         g_value_set_uint (
322                                 value,
323                                 camel_folder_summary_get_deleted_count (
324                                 CAMEL_FOLDER_SUMMARY (object)));
325                         return;
326
327                 case PROP_JUNK_COUNT:
328                         g_value_set_uint (
329                                 value,
330                                 camel_folder_summary_get_junk_count (
331                                 CAMEL_FOLDER_SUMMARY (object)));
332                         return;
333
334                 case PROP_JUNK_NOT_DELETED_COUNT:
335                         g_value_set_uint (
336                                 value,
337                                 camel_folder_summary_get_junk_not_deleted_count (
338                                 CAMEL_FOLDER_SUMMARY (object)));
339                         return;
340
341                 case PROP_VISIBLE_COUNT:
342                         g_value_set_uint (
343                                 value,
344                                 camel_folder_summary_get_visible_count (
345                                 CAMEL_FOLDER_SUMMARY (object)));
346                         return;
347
348                 case PROP_BUILD_CONTENT:
349                         g_value_set_boolean (
350                                 value,
351                                 camel_folder_summary_get_build_content (
352                                 CAMEL_FOLDER_SUMMARY (object)));
353                         return;
354
355                 case PROP_NEED_PREVIEW:
356                         g_value_set_boolean (
357                                 value,
358                                 camel_folder_summary_get_need_preview (
359                                 CAMEL_FOLDER_SUMMARY (object)));
360                         return;
361         }
362
363         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
364 }
365
366 static gboolean
367 is_in_memory_summary (CamelFolderSummary *summary)
368 {
369         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
370
371         return (summary->flags & CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY) != 0;
372 }
373
374 #define UPDATE_COUNTS_ADD               (1)
375 #define UPDATE_COUNTS_SUB               (2)
376 #define UPDATE_COUNTS_ADD_WITHOUT_TOTAL (3)
377 #define UPDATE_COUNTS_SUB_WITHOUT_TOTAL (4)
378
379 static gboolean
380 folder_summary_update_counts_by_flags (CamelFolderSummary *summary,
381                                        guint32 flags,
382                                        gint op_type)
383 {
384         gint unread = 0, deleted = 0, junk = 0;
385         gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
386         gboolean subtract = op_type == UPDATE_COUNTS_SUB || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
387         gboolean without_total = op_type == UPDATE_COUNTS_ADD_WITHOUT_TOTAL || op_type == UPDATE_COUNTS_SUB_WITHOUT_TOTAL;
388         gboolean changed = FALSE;
389         GObject *summary_object;
390
391         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
392
393         summary_object = G_OBJECT (summary);
394
395         if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
396                 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
397
398                 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
399                 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
400         }
401
402         if (!(flags & CAMEL_MESSAGE_SEEN))
403                 unread = subtract ? -1 : 1;
404
405         if (flags & CAMEL_MESSAGE_DELETED)
406                 deleted = subtract ? -1 : 1;
407
408         if (flags & CAMEL_MESSAGE_JUNK)
409                 junk = subtract ? -1 : 1;
410
411         dd (printf ("%p: %d %d %d | %d %d %d \n", (gpointer) summary, unread, deleted, junk, summary->priv->unread_count, summary->priv->visible_count, summary->priv->saved_count));
412
413         g_object_freeze_notify (summary_object);
414
415         if (deleted) {
416                 summary->priv->deleted_count += deleted;
417                 g_object_notify (summary_object, "deleted-count");
418                 changed = TRUE;
419         }
420
421         if (junk) {
422                 summary->priv->junk_count += junk;
423                 g_object_notify (summary_object, "junk-count");
424                 changed = TRUE;
425         }
426
427         if (junk && !deleted) {
428                 summary->priv->junk_not_deleted_count += junk;
429                 g_object_notify (summary_object, "junk-not-deleted-count");
430                 changed = TRUE;
431         }
432
433         if (!junk && !deleted) {
434                 summary->priv->visible_count += subtract ? -1 : 1;
435                 g_object_notify (summary_object, "visible-count");
436                 changed = TRUE;
437         }
438
439         if (junk && !is_junk_folder)
440                 unread = 0;
441         if (deleted && !is_trash_folder)
442                 unread = 0;
443
444         if (unread) {
445                 summary->priv->unread_count += unread;
446                 g_object_notify (summary_object, "unread-count");
447                 changed = TRUE;
448         }
449
450         if (!without_total) {
451                 summary->priv->saved_count += subtract ? -1 : 1;
452                 g_object_notify (summary_object, "saved-count");
453                 changed = TRUE;
454         }
455
456         if (changed)
457                 camel_folder_summary_touch (summary);
458
459         g_object_thaw_notify (summary_object);
460
461         dd (printf ("%p: %d %d %d | %d %d %d\n", (gpointer) summary, unread, deleted, junk, summary->priv->unread_count, summary->priv->visible_count, summary->priv->saved_count));
462
463         return changed;
464 }
465
466 static gboolean
467 summary_header_from_db (CamelFolderSummary *summary,
468                         CamelFIRecord *record)
469 {
470         io (printf ("Loading header from db \n"));
471
472         summary->version = record->version;
473
474         /* We may not worry, as we are setting a new standard here */
475 #if 0
476         /* Legacy version check, before version 12 we have no upgrade knowledge */
477         if ((summary->version > 0xff) && (summary->version & 0xff) < 12) {
478                 io (printf ("Summary header version mismatch"));
479                 errno = EINVAL;
480                 return FALSE;
481         }
482
483         if (!(summary->version < 0x100 && summary->version >= 13))
484                 io (printf ("Loading legacy summary\n"));
485         else
486                 io (printf ("loading new-format summary\n"));
487 #endif
488
489         summary->flags = record->flags;
490         summary->priv->nextuid = record->nextuid;
491         summary->time = record->time;
492         summary->priv->saved_count = record->saved_count;
493
494         summary->priv->unread_count = record->unread_count;
495         summary->priv->deleted_count = record->deleted_count;
496         summary->priv->junk_count = record->junk_count;
497         summary->priv->visible_count = record->visible_count;
498         summary->priv->junk_not_deleted_count = record->jnd_count;
499
500         return TRUE;
501 }
502
503 static  CamelFIRecord *
504 summary_header_to_db (CamelFolderSummary *summary,
505                       GError **error)
506 {
507         CamelFIRecord * record = g_new0 (CamelFIRecord, 1);
508         CamelStore *parent_store;
509         CamelDB *db;
510         const gchar *table_name;
511
512         /* Though we are going to read, we do this during write,
513          * so lets use it that way. */
514         table_name = camel_folder_get_full_name (summary->priv->folder);
515         parent_store = camel_folder_get_parent_store (summary->priv->folder);
516         db = parent_store->cdb_w;
517
518         io (printf ("Savining header to db\n"));
519
520         record->folder_name = g_strdup (table_name);
521
522         /* we always write out the current version */
523         record->version = CAMEL_FOLDER_SUMMARY_VERSION;
524         record->flags = summary->flags;
525         record->nextuid = summary->priv->nextuid;
526         record->time = summary->time;
527
528         if (!is_in_memory_summary (summary)) {
529                 /* FIXME: Ever heard of Constructors and initializing ? */
530                 if (camel_db_count_total_message_info (db, table_name, &(record->saved_count), NULL))
531                         record->saved_count = 0;
532                 if (camel_db_count_junk_message_info (db, table_name, &(record->junk_count), NULL))
533                         record->junk_count = 0;
534                 if (camel_db_count_deleted_message_info (db, table_name, &(record->deleted_count), NULL))
535                         record->deleted_count = 0;
536                 if (camel_db_count_unread_message_info (db, table_name, &(record->unread_count), NULL))
537                         record->unread_count = 0;
538                 if (camel_db_count_visible_message_info (db, table_name, &(record->visible_count), NULL))
539                         record->visible_count = 0;
540                 if (camel_db_count_junk_not_deleted_message_info (db, table_name, &(record->jnd_count), NULL))
541                         record->jnd_count = 0;
542         }
543
544         summary->priv->unread_count = record->unread_count;
545         summary->priv->deleted_count = record->deleted_count;
546         summary->priv->junk_count = record->junk_count;
547         summary->priv->visible_count = record->visible_count;
548         summary->priv->junk_not_deleted_count = record->jnd_count;
549
550         return record;
551 }
552
553 static CamelMessageInfo *
554 message_info_from_db (CamelFolderSummary *summary,
555                       CamelMIRecord *record)
556 {
557         CamelMessageInfoBase *mi;
558         gint i;
559         gint count;
560         gchar *part, *label;
561
562         mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
563
564         io (printf ("Loading message info from db\n"));
565
566         mi->flags = record->flags;
567         mi->size = record->size;
568         mi->date_sent = record->dsent;
569         mi->date_received = record->dreceived;
570
571         mi->uid = (gchar *) camel_pstring_strdup (record->uid);
572         mi->subject = (gchar *) camel_pstring_add (record->subject, FALSE);
573         mi->from = (gchar *) camel_pstring_add (record->from, FALSE);
574         mi->to = (gchar *) camel_pstring_add (record->to, FALSE);
575         mi->cc = (gchar *) camel_pstring_add (record->cc, FALSE);
576         mi->mlist = (gchar *) camel_pstring_add (record->mlist, FALSE);
577
578         /* Evolution itself doesn't yet use this, so we ignore it (saving some memory) */
579         mi->bodystructure = NULL;
580
581         /* Extract Message id & References */
582         mi->content = NULL;
583         part = record->part;
584         if (part) {
585                 mi->message_id.id.part.hi = bdata_extract_digit (&part);
586                 mi->message_id.id.part.lo = bdata_extract_digit (&part);
587                 count = bdata_extract_digit (&part);
588
589                 if (count > 0) {
590                         mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
591                         mi->references->size = count;
592                         for (i = 0; i < count; i++) {
593                                 mi->references->references[i].id.part.hi = bdata_extract_digit (&part);
594                                 mi->references->references[i].id.part.lo = bdata_extract_digit (&part);
595                         }
596                 } else
597                         mi->references = NULL;
598
599         }
600
601         /* Extract User flags/labels */
602         part = record->labels;
603         if (part) {
604                 label = part;
605                 for (i = 0; part[i]; i++) {
606
607                         if (part[i] == ' ') {
608                                 part[i] = 0;
609                                 camel_flag_set (&mi->user_flags, label, TRUE);
610                                 label = &(part[i + 1]);
611                         }
612                 }
613                 camel_flag_set (&mi->user_flags, label, TRUE);
614         }
615
616         /* Extract User tags */
617         part = record->usertags;
618         count = bdata_extract_digit (&part);
619         for (i = 0; i < count; i++) {
620                 gchar *name, *value;
621
622                 name = bdata_extract_string (&part);
623                 value = bdata_extract_string (&part);
624                 camel_tag_set (&mi->user_tags, name, value);
625
626                 g_free (name);
627                 g_free (value);
628         }
629
630         return (CamelMessageInfo *) mi;
631 }
632
633 static CamelMIRecord *
634 message_info_to_db (CamelFolderSummary *summary,
635                     CamelMessageInfo *info)
636 {
637         CamelMIRecord *record = g_new0 (CamelMIRecord, 1);
638         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
639         GString *tmp;
640         CamelFlag *flag;
641         CamelTag *tag;
642         gint count, i;
643
644         /* Assume that we dont have to take care of DB Safeness. It will be done while doing the DB transaction */
645         record->uid = (gchar *) camel_pstring_strdup (camel_message_info_uid (mi));
646         record->flags = mi->flags;
647
648         record->read = ((mi->flags & (CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_JUNK))) ? 1 : 0;
649         record->deleted = mi->flags & CAMEL_MESSAGE_DELETED ? 1 : 0;
650         record->replied = mi->flags & CAMEL_MESSAGE_ANSWERED ? 1 : 0;
651         record->important = mi->flags & CAMEL_MESSAGE_FLAGGED ? 1 : 0;
652         record->junk = mi->flags & CAMEL_MESSAGE_JUNK ? 1 : 0;
653         record->dirty = mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED ? 1 : 0;
654         record->attachment = mi->flags & CAMEL_MESSAGE_ATTACHMENTS ? 1 : 0;
655
656         record->size = mi->size;
657         record->dsent = mi->date_sent;
658         record->dreceived = mi->date_received;
659
660         record->subject = (gchar *) camel_pstring_strdup (camel_message_info_subject (mi));
661         record->from = (gchar *) camel_pstring_strdup (camel_message_info_from (mi));
662         record->to = (gchar *) camel_pstring_strdup (camel_message_info_to (mi));
663         record->cc = (gchar *) camel_pstring_strdup (camel_message_info_cc (mi));
664         record->mlist = (gchar *) camel_pstring_strdup (camel_message_info_mlist (mi));
665
666         record->followup_flag = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "follow-up"));
667         record->followup_completed_on = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "completed-on"));
668         record->followup_due_by = (gchar *) camel_pstring_strdup (camel_message_info_user_tag (info, "due-by"));
669
670         record->bodystructure = mi->bodystructure ? g_strdup (mi->bodystructure) : NULL;
671
672         tmp = g_string_new (NULL);
673         if (mi->references) {
674                 g_string_append_printf (tmp, "%lu %lu %lu", (gulong) mi->message_id.id.part.hi, (gulong) mi->message_id.id.part.lo, (gulong) mi->references->size);
675                 for (i = 0; i < mi->references->size; i++)
676                         g_string_append_printf (tmp, " %lu %lu", (gulong) mi->references->references[i].id.part.hi, (gulong) mi->references->references[i].id.part.lo);
677         } else {
678                 g_string_append_printf (tmp, "%lu %lu %lu", (gulong) mi->message_id.id.part.hi, (gulong) mi->message_id.id.part.lo, (gulong) 0);
679         }
680         record->part = tmp->str;
681         g_string_free (tmp, FALSE);
682
683         tmp = g_string_new (NULL);
684         flag = mi->user_flags;
685         while (flag) {
686                 g_string_append_printf (tmp, "%s ", flag->name);
687                 flag = flag->next;
688         }
689
690         /* Strip off the last space */
691         if (tmp->len)
692                 tmp->len--;
693
694         record->labels = tmp->str;
695         g_string_free (tmp, FALSE);
696
697         tmp = g_string_new (NULL);
698         count = camel_tag_list_size (&mi->user_tags);
699         g_string_append_printf (tmp, "%lu", (gulong) count);
700         tag = mi->user_tags;
701         while (tag) {
702                 /* FIXME: Should we handle empty tags? Can it be empty? If it potential crasher ahead*/
703                 g_string_append_printf (tmp, " %lu-%s %lu-%s", (gulong) strlen (tag->name), tag->name, (gulong) strlen (tag->value), tag->value);
704                 tag = tag->next;
705         }
706         record->usertags = tmp->str;
707         g_string_free (tmp, FALSE);
708
709         return record;
710 }
711
712 static CamelMessageContentInfo *
713 content_info_from_db (CamelFolderSummary *summary,
714                       CamelMIRecord *record)
715 {
716         CamelMessageContentInfo *ci;
717         gchar *type, *subtype;
718         guint32 count, i;
719         CamelContentType *ct;
720         gchar *part = record->cinfo;
721
722         io (printf ("Loading content info from db\n"));
723
724         if (!part)
725                 return NULL;
726
727         ci = camel_folder_summary_content_info_new (summary);
728         if (*part == ' ') part++; /* Move off the space in the record */
729
730         type = bdata_extract_string (&part);
731         subtype = bdata_extract_string (&part);
732         ct = camel_content_type_new (type, subtype);
733         g_free (type);          /* can this be removed? */
734         g_free (subtype);
735         count = bdata_extract_digit (&part);
736
737         for (i = 0; i < count; i++) {
738                 gchar *name, *value;
739                 name = bdata_extract_string (&part);
740                 value = bdata_extract_string (&part);
741
742                 camel_content_type_set_param (ct, name, value);
743                 /* TODO: do this so we dont have to double alloc/free */
744                 g_free (name);
745                 g_free (value);
746         }
747         ci->type = ct;
748
749         /* FIXME[disk-summary] move all these to camel pstring */
750         ci->id = bdata_extract_string (&part);
751         ci->description = bdata_extract_string (&part);
752         ci->encoding = bdata_extract_string (&part);
753         ci->size = bdata_extract_digit (&part);
754
755         record->cinfo = part; /* Keep moving the cursor in the record */
756
757         ci->childs = NULL;
758
759         return ci;
760 }
761
762 static gboolean
763 content_info_to_db (CamelFolderSummary *summary,
764                     CamelMessageContentInfo *ci,
765                     CamelMIRecord *record)
766 {
767         CamelContentType *ct;
768         struct _camel_header_param *hp;
769         GString *str = g_string_new (NULL);
770         gchar *oldr;
771
772         io (printf ("Saving content info to db\n"));
773
774         ct = ci->type;
775         if (ct) {
776                 if (ct->type)
777                         g_string_append_printf (str, " %d-%s", (gint) strlen (ct->type), ct->type);
778                 else
779                         g_string_append_printf (str, " 0-");
780                 if (ct->subtype)
781                         g_string_append_printf (str, " %d-%s", (gint) strlen (ct->subtype), ct->subtype);
782                 else
783                         g_string_append_printf (str, " 0-");
784                 g_string_append_printf (str, " %d", my_list_size ((struct _node **) &ct->params));
785                 hp = ct->params;
786                 while (hp) {
787                         if (hp->name)
788                                 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->name), hp->name);
789                         else
790                                 g_string_append_printf (str, " 0-");
791                         if (hp->value)
792                                 g_string_append_printf (str, " %d-%s", (gint) strlen (hp->value), hp->value);
793                         else
794                                 g_string_append_printf (str, " 0-");
795                         hp = hp->next;
796                 }
797         } else {
798                 g_string_append_printf (str, " %d-", 0);
799                 g_string_append_printf (str, " %d-", 0);
800                 g_string_append_printf (str, " %d", 0);
801         }
802
803         if (ci->id)
804                 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->id), ci->id);
805         else
806                 g_string_append_printf (str, " 0-");
807         if (ci->description)
808                 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->description), ci->description);
809         else
810                 g_string_append_printf (str, " 0-");
811         if (ci->encoding)
812                 g_string_append_printf (str, " %d-%s", (gint) strlen (ci->encoding), ci->encoding);
813         else
814                 g_string_append_printf (str, " 0-");
815         g_string_append_printf (str, " %u", ci->size);
816
817         if (record->cinfo) {
818                 oldr = record->cinfo;
819                 record->cinfo = g_strconcat (oldr, str->str, NULL);
820                 g_free (oldr); g_string_free (str, TRUE);
821         } else {
822                 record->cinfo = str->str;
823                 g_string_free (str, FALSE);
824         }
825
826         return TRUE;
827 }
828
829 /**
830  * camel_folder_summary_replace_flags:
831  * @summary: a #CamelFolderSummary
832  * @info: a #CamelMessageInfo
833  *
834  * Updates internal counts based on the flags in @info.
835  *
836  * Returns: Whether any count changed
837  *
838  * Since: 3.6
839  **/
840 gboolean
841 camel_folder_summary_replace_flags (CamelFolderSummary *summary,
842                                     CamelMessageInfo *info)
843 {
844         guint32 old_flags, new_flags, added_flags, removed_flags;
845         gboolean is_junk_folder = FALSE, is_trash_folder = FALSE;
846         GObject *summary_object;
847         gboolean changed = FALSE;
848
849         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
850         g_return_val_if_fail (info != NULL, FALSE);
851
852         if (!camel_message_info_uid (info) ||
853             !camel_folder_summary_check_uid (summary, camel_message_info_uid (info)))
854                 return FALSE;
855
856         summary_object = G_OBJECT (summary);
857
858         camel_folder_summary_lock (summary);
859         g_object_freeze_notify (summary_object);
860
861         old_flags = GPOINTER_TO_UINT (g_hash_table_lookup (summary->priv->uids, camel_message_info_uid (info)));
862         new_flags = camel_message_info_flags (info);
863
864         if ((old_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED) == (new_flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED)) {
865                 g_object_thaw_notify (summary_object);
866                 camel_folder_summary_unlock (summary);
867                 return FALSE;
868         }
869
870         if (summary->priv->folder && CAMEL_IS_VTRASH_FOLDER (summary->priv->folder)) {
871                 CamelVTrashFolder *vtrash = CAMEL_VTRASH_FOLDER (summary->priv->folder);
872
873                 is_junk_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_JUNK;
874                 is_trash_folder = vtrash && vtrash->type == CAMEL_VTRASH_FOLDER_TRASH;
875         }
876
877         added_flags = new_flags & (~(old_flags & new_flags));
878         removed_flags = old_flags & (~(old_flags & new_flags));
879
880         if ((old_flags & CAMEL_MESSAGE_SEEN) == (new_flags & CAMEL_MESSAGE_SEEN)) {
881                 /* unread count is different from others, it asks for nonexistence
882                  * of the flag, thus if it wasn't changed, then simply set it
883                  * in added/removed, thus there are no false notifications
884                  * on unread counts */
885                 added_flags |= CAMEL_MESSAGE_SEEN;
886                 removed_flags |= CAMEL_MESSAGE_SEEN;
887         } else if ((!is_junk_folder && (new_flags & CAMEL_MESSAGE_JUNK) != 0 &&
888                    (old_flags & CAMEL_MESSAGE_JUNK) == (new_flags & CAMEL_MESSAGE_JUNK)) ||
889                    (!is_trash_folder && (new_flags & CAMEL_MESSAGE_DELETED) != 0 &&
890                    (old_flags & CAMEL_MESSAGE_DELETED) == (new_flags & CAMEL_MESSAGE_DELETED))) {
891                 /* The message was set read or unread, but it is a junk or deleted message,
892                  * in a non-Junk/non-Trash folder, thus it doesn't influence an unread count
893                  * there, thus pretend unread didn't change */
894                 added_flags |= CAMEL_MESSAGE_SEEN;
895                 removed_flags |= CAMEL_MESSAGE_SEEN;
896         }
897
898         /* decrement counts with removed flags */
899         changed = folder_summary_update_counts_by_flags (summary, removed_flags, UPDATE_COUNTS_SUB_WITHOUT_TOTAL) || changed;
900         /* increment counts with added flags */
901         changed = folder_summary_update_counts_by_flags (summary, added_flags, UPDATE_COUNTS_ADD_WITHOUT_TOTAL) || changed;
902
903         /* update current flags on the summary */
904         g_hash_table_insert (
905                 summary->priv->uids,
906                 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
907                 GUINT_TO_POINTER (new_flags));
908
909         g_object_thaw_notify (summary_object);
910         camel_folder_summary_unlock (summary);
911
912         return changed;
913 }
914
915 static CamelMessageInfo *
916 message_info_clone (CamelFolderSummary *summary,
917                     const CamelMessageInfo *mi)
918 {
919         CamelMessageInfoBase *to, *from = (CamelMessageInfoBase *) mi;
920         CamelFlag *flag;
921         CamelTag *tag;
922
923         to = (CamelMessageInfoBase *) camel_message_info_new (summary);
924
925         to->flags = from->flags;
926         to->size = from->size;
927         to->date_sent = from->date_sent;
928         to->date_received = from->date_received;
929         to->refcount = 1;
930
931         /* NB: We don't clone the uid */
932
933         to->subject = camel_pstring_strdup (from->subject);
934         to->from = camel_pstring_strdup (from->from);
935         to->to = camel_pstring_strdup (from->to);
936         to->cc = camel_pstring_strdup (from->cc);
937         to->mlist = camel_pstring_strdup (from->mlist);
938         memcpy (&to->message_id, &from->message_id, sizeof (to->message_id));
939         to->preview = g_strdup (from->preview);
940         if (from->references) {
941                 gint len = sizeof (*from->references) + ((from->references->size - 1) * sizeof (from->references->references[0]));
942
943                 to->references = g_malloc (len);
944                 memcpy (to->references, from->references, len);
945         }
946
947         flag = from->user_flags;
948         while (flag) {
949                 camel_flag_set (&to->user_flags, flag->name, TRUE);
950                 flag = flag->next;
951         }
952
953         tag = from->user_tags;
954         while (tag) {
955                 camel_tag_set (&to->user_tags, tag->name, tag->value);
956                 tag = tag->next;
957         }
958
959         if (from->content) {
960                 /* FIXME: copy content-infos */
961         }
962
963         return (CamelMessageInfo *) to;
964 }
965
966 static gconstpointer
967 info_ptr (const CamelMessageInfo *mi,
968           gint id)
969 {
970         switch (id) {
971                 case CAMEL_MESSAGE_INFO_SUBJECT:
972                         return ((const CamelMessageInfoBase *) mi)->subject;
973                 case CAMEL_MESSAGE_INFO_FROM:
974                         return ((const CamelMessageInfoBase *) mi)->from;
975                 case CAMEL_MESSAGE_INFO_TO:
976                         return ((const CamelMessageInfoBase *) mi)->to;
977                 case CAMEL_MESSAGE_INFO_CC:
978                         return ((const CamelMessageInfoBase *) mi)->cc;
979                 case CAMEL_MESSAGE_INFO_MLIST:
980                         return ((const CamelMessageInfoBase *) mi)->mlist;
981                 case CAMEL_MESSAGE_INFO_MESSAGE_ID:
982                         return &((const CamelMessageInfoBase *) mi)->message_id;
983                 case CAMEL_MESSAGE_INFO_REFERENCES:
984                         return ((const CamelMessageInfoBase *) mi)->references;
985                 case CAMEL_MESSAGE_INFO_USER_FLAGS:
986                         return ((const CamelMessageInfoBase *) mi)->user_flags;
987                 case CAMEL_MESSAGE_INFO_USER_TAGS:
988                         return ((const CamelMessageInfoBase *) mi)->user_tags;
989                 case CAMEL_MESSAGE_INFO_HEADERS:
990                         return ((const CamelMessageInfoBase *) mi)->headers;
991                 case CAMEL_MESSAGE_INFO_CONTENT:
992                         return ((const CamelMessageInfoBase *) mi)->content;
993                 case CAMEL_MESSAGE_INFO_PREVIEW:
994                         return ((const CamelMessageInfoBase *) mi)->preview;
995                 default:
996                         g_return_val_if_reached (NULL);
997         }
998 }
999
1000 static guint32
1001 info_uint32 (const CamelMessageInfo *mi,
1002              gint id)
1003 {
1004         switch (id) {
1005                 case CAMEL_MESSAGE_INFO_FLAGS:
1006                         return ((const CamelMessageInfoBase *) mi)->flags;
1007                 case CAMEL_MESSAGE_INFO_SIZE:
1008                         return ((const CamelMessageInfoBase *) mi)->size;
1009                 default:
1010                         g_return_val_if_reached (0);
1011         }
1012 }
1013
1014 static time_t
1015 info_time (const CamelMessageInfo *mi,
1016            gint id)
1017 {
1018         switch (id) {
1019                 case CAMEL_MESSAGE_INFO_DATE_SENT:
1020                         return ((const CamelMessageInfoBase *) mi)->date_sent;
1021                 case CAMEL_MESSAGE_INFO_DATE_RECEIVED:
1022                         return ((const CamelMessageInfoBase *) mi)->date_received;
1023                 default:
1024                         g_return_val_if_reached (0);
1025         }
1026 }
1027
1028 static gboolean
1029 info_user_flag (const CamelMessageInfo *mi,
1030                 const gchar *id)
1031 {
1032         return camel_flag_get (&((CamelMessageInfoBase *) mi)->user_flags, id);
1033 }
1034
1035 static const gchar *
1036 info_user_tag (const CamelMessageInfo *mi,
1037                const gchar *id)
1038 {
1039         return camel_tag_get (&((CamelMessageInfoBase *) mi)->user_tags, id);
1040 }
1041
1042 static gboolean
1043 info_set_user_flag (CamelMessageInfo *info,
1044                     const gchar *name,
1045                     gboolean value)
1046 {
1047         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1048         gint res;
1049
1050         res = camel_flag_set (&mi->user_flags, name, value);
1051
1052         if (mi->summary && res && mi->summary->priv->folder && mi->uid
1053             && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1054                 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1055
1056                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1057                 mi->dirty = TRUE;
1058                 camel_folder_summary_touch (mi->summary);
1059                 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1060                 camel_folder_changed (mi->summary->priv->folder, changes);
1061                 camel_folder_change_info_free (changes);
1062         }
1063
1064         return res;
1065 }
1066
1067 static gboolean
1068 info_set_user_tag (CamelMessageInfo *info,
1069                    const gchar *name,
1070                    const gchar *value)
1071 {
1072         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1073         gint res;
1074
1075         res = camel_tag_set (&mi->user_tags, name, value);
1076
1077         if (mi->summary && res && mi->summary->priv->folder && mi->uid
1078             && camel_folder_summary_check_uid (mi->summary, mi->uid)) {
1079                 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1080
1081                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1082                 mi->dirty = TRUE;
1083                 camel_folder_summary_touch (mi->summary);
1084                 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1085                 camel_folder_changed (mi->summary->priv->folder, changes);
1086                 camel_folder_change_info_free (changes);
1087         }
1088
1089         return res;
1090 }
1091
1092 static gboolean
1093 info_set_flags (CamelMessageInfo *info,
1094                 guint32 flags,
1095                 guint32 set)
1096 {
1097         guint32 old;
1098         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
1099         gboolean counts_changed = FALSE;
1100
1101         old = camel_message_info_flags (info);
1102         mi->flags = (old & ~flags) | (set & flags);
1103         if (old != mi->flags) {
1104                 mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
1105                 mi->dirty = TRUE;
1106                 if (mi->summary)
1107                         camel_folder_summary_touch (mi->summary);
1108         }
1109
1110         if (mi->summary) {
1111                 camel_folder_summary_lock (mi->summary);
1112                 g_object_freeze_notify (G_OBJECT (mi->summary));
1113                 counts_changed = camel_folder_summary_replace_flags (mi->summary, info);
1114         }
1115
1116         if (!counts_changed && ((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK)) && !((set & CAMEL_MESSAGE_JUNK_LEARN) && !(set & CAMEL_MESSAGE_JUNK))) {
1117                 if (mi->summary) {
1118                         g_object_thaw_notify (G_OBJECT (mi->summary));
1119                         camel_folder_summary_unlock (mi->summary);
1120                 }
1121                 return FALSE;
1122         }
1123
1124         if (mi->summary) {
1125                 g_object_thaw_notify (G_OBJECT (mi->summary));
1126                 camel_folder_summary_unlock (mi->summary);
1127         }
1128
1129         if (mi->summary && mi->summary->priv->folder && mi->uid) {
1130                 CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
1131
1132                 camel_folder_change_info_change_uid (changes, camel_message_info_uid (info));
1133                 camel_folder_changed (mi->summary->priv->folder, changes);
1134                 camel_folder_change_info_free (changes);
1135         }
1136
1137         return TRUE;
1138 }
1139
1140 static void
1141 camel_folder_summary_class_init (CamelFolderSummaryClass *class)
1142 {
1143         GObjectClass *object_class;
1144
1145         g_type_class_add_private (class, sizeof (CamelFolderSummaryPrivate));
1146
1147         object_class = G_OBJECT_CLASS (class);
1148         object_class->set_property = folder_summary_set_property;
1149         object_class->get_property = folder_summary_get_property;
1150         object_class->dispose = folder_summary_dispose;
1151         object_class->finalize = folder_summary_finalize;
1152
1153         class->message_info_size = sizeof (CamelMessageInfoBase);
1154         class->content_info_size = sizeof (CamelMessageContentInfo);
1155
1156         class->summary_header_from_db = summary_header_from_db;
1157         class->summary_header_to_db = summary_header_to_db;
1158         class->message_info_from_db = message_info_from_db;
1159         class->message_info_to_db = message_info_to_db;
1160         class->content_info_from_db = content_info_from_db;
1161         class->content_info_to_db = content_info_to_db;
1162
1163         class->message_info_new_from_header = message_info_new_from_header;
1164         class->message_info_new_from_parser = message_info_new_from_parser;
1165         class->message_info_new_from_message = message_info_new_from_message;
1166         class->message_info_free = message_info_free;
1167         class->message_info_clone = message_info_clone;
1168         class->message_info_from_uid = message_info_from_uid;
1169
1170         class->content_info_new_from_header = content_info_new_from_header;
1171         class->content_info_new_from_parser = content_info_new_from_parser;
1172         class->content_info_new_from_message = content_info_new_from_message;
1173         class->content_info_free = content_info_free;
1174
1175         class->next_uid_string = next_uid_string;
1176
1177         class->info_ptr = info_ptr;
1178         class->info_uint32 = info_uint32;
1179         class->info_time = info_time;
1180         class->info_user_flag = info_user_flag;
1181         class->info_user_tag = info_user_tag;
1182
1183         class->info_set_user_flag = info_set_user_flag;
1184         class->info_set_user_tag = info_set_user_tag;
1185
1186         class->info_set_flags = info_set_flags;
1187
1188         /**
1189          * CamelFolderSummary:folder
1190          *
1191          * The #CamelFolder to which the folder summary belongs.
1192          **/
1193         g_object_class_install_property (
1194                 object_class,
1195                 PROP_FOLDER,
1196                 g_param_spec_object (
1197                         "folder",
1198                         "Folder",
1199                         "The folder to which the folder summary belongs",
1200                         CAMEL_TYPE_FOLDER,
1201                         G_PARAM_READWRITE |
1202                         G_PARAM_CONSTRUCT_ONLY));
1203
1204         /**
1205          * CamelFolderSummary:saved-count
1206          *
1207          * How many infos is saved in a summary.
1208          **/
1209         g_object_class_install_property (
1210                 object_class,
1211                 PROP_SAVED_COUNT,
1212                 g_param_spec_uint (
1213                         "saved-count",
1214                         "Saved count",
1215                         "How many infos is savef in a summary",
1216                         0,  G_MAXUINT32,
1217                         0, G_PARAM_READABLE));
1218
1219         /**
1220          * CamelFolderSummary:unread-count
1221          *
1222          * How many unread infos is saved in a summary.
1223          **/
1224         g_object_class_install_property (
1225                 object_class,
1226                 PROP_UNREAD_COUNT,
1227                 g_param_spec_uint (
1228                         "unread-count",
1229                         "Unread count",
1230                         "How many unread infos is saved in a summary",
1231                         0,  G_MAXUINT32,
1232                         0, G_PARAM_READABLE));
1233
1234         /**
1235          * CamelFolderSummary:deleted-count
1236          *
1237          * How many deleted infos is saved in a summary.
1238          **/
1239         g_object_class_install_property (
1240                 object_class,
1241                 PROP_DELETED_COUNT,
1242                 g_param_spec_uint (
1243                         "deleted-count",
1244                         "Deleted count",
1245                         "How many deleted infos is saved in a summary",
1246                         0,  G_MAXUINT32,
1247                         0, G_PARAM_READABLE));
1248
1249         /**
1250          * CamelFolderSummary:junk-count
1251          *
1252          * How many junk infos is saved in a summary.
1253          **/
1254         g_object_class_install_property (
1255                 object_class,
1256                 PROP_JUNK_COUNT,
1257                 g_param_spec_uint (
1258                         "junk-count",
1259                         "Junk count",
1260                         "How many junk infos is saved in a summary",
1261                         0,  G_MAXUINT32,
1262                         0, G_PARAM_READABLE));
1263
1264         /**
1265          * CamelFolderSummary:junk-not-deleted-count
1266          *
1267          * How many junk and not deleted infos is saved in a summary.
1268          **/
1269         g_object_class_install_property (
1270                 object_class,
1271                 PROP_JUNK_NOT_DELETED_COUNT,
1272                 g_param_spec_uint (
1273                         "junk-not-deleted-count",
1274                         "Junk not deleted count",
1275                         "How many junk and not deleted infos is saved in a summary",
1276                         0,  G_MAXUINT32,
1277                         0, G_PARAM_READABLE));
1278
1279         /**
1280          * CamelFolderSummary:visible-count
1281          *
1282          * How many visible (not deleted and not junk) infos is saved in a summary.
1283          **/
1284         g_object_class_install_property (
1285                 object_class,
1286                 PROP_VISIBLE_COUNT,
1287                 g_param_spec_uint (
1288                         "visible-count",
1289                         "Visible count",
1290                         "How many visible (not deleted and not junk) infos is saved in a summary",
1291                         0,  G_MAXUINT32,
1292                         0, G_PARAM_READABLE));
1293
1294         /**
1295          * CamelFolderSummary:build-content
1296          *
1297          * Whether to build CamelMessageInfo.content.
1298          **/
1299         g_object_class_install_property (
1300                 object_class,
1301                 PROP_BUILD_CONTENT,
1302                 g_param_spec_boolean (
1303                         "build-content",
1304                         "Build content",
1305                         "Whether to build CamelMessageInfo.content",
1306                         FALSE,
1307                         G_PARAM_READWRITE));
1308
1309         /**
1310          * CamelFolderSummary:need-preview
1311          *
1312          **/
1313         g_object_class_install_property (
1314                 object_class,
1315                 PROP_NEED_PREVIEW,
1316                 g_param_spec_boolean (
1317                         "need-preview",
1318                         "Need preview",
1319                         "",
1320                         FALSE,
1321                         G_PARAM_READWRITE));
1322 }
1323
1324 static void
1325 camel_folder_summary_init (CamelFolderSummary *summary)
1326 {
1327         summary->priv = CAMEL_FOLDER_SUMMARY_GET_PRIVATE (summary);
1328
1329         summary->version = CAMEL_FOLDER_SUMMARY_VERSION;
1330         summary->flags = 0;
1331         summary->time = 0;
1332
1333         summary->priv->filter_charset = g_hash_table_new (
1334                 camel_strcase_hash, camel_strcase_equal);
1335
1336         summary->priv->need_preview = FALSE;
1337         summary->priv->preview_updates = g_hash_table_new (g_str_hash, g_str_equal);
1338
1339         summary->priv->nextuid = 1;
1340         summary->priv->uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
1341         summary->priv->loaded_infos = g_hash_table_new (g_str_hash, g_str_equal);
1342
1343         g_rec_mutex_init (&summary->priv->summary_lock);
1344         g_rec_mutex_init (&summary->priv->filter_lock);
1345
1346         summary->priv->cache_load_time = 0;
1347         summary->priv->timeout_handle = 0;
1348 }
1349
1350 /**
1351  * camel_folder_summary_new:
1352  * @folder: parent #CamelFolder object
1353  *
1354  * Create a new #CamelFolderSummary object.
1355  *
1356  * Returns: a new #CamelFolderSummary object
1357  **/
1358 CamelFolderSummary *
1359 camel_folder_summary_new (CamelFolder *folder)
1360 {
1361         return g_object_new (CAMEL_TYPE_FOLDER_SUMMARY, "folder", folder, NULL);
1362 }
1363
1364 /**
1365  * camel_folder_summary_get_index:
1366  * @summary: a #CamelFolderSummary object
1367  *
1368  * Returns: a #CamelFolder to which the summary if associated.
1369  *
1370  * Since: 3.4
1371  **/
1372 CamelFolder *
1373 camel_folder_summary_get_folder (CamelFolderSummary *summary)
1374 {
1375         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1376
1377         return summary->priv->folder;
1378 }
1379
1380 /**
1381  * camel_folder_summary_get_saved_count:
1382  * @summary: a #CamelFolderSummary object
1383  *
1384  * Returns: Count of saved infos.
1385  *
1386  * Since: 3.4
1387  **/
1388 guint32
1389 camel_folder_summary_get_saved_count (CamelFolderSummary *summary)
1390 {
1391         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1392
1393         return summary->priv->saved_count;
1394 }
1395
1396 /**
1397  * camel_folder_summary_get_unread_count:
1398  * @summary: a #CamelFolderSummary object
1399  *
1400  * Returns: Count of unread infos.
1401  *
1402  * Since: 3.4
1403  **/
1404 guint32
1405 camel_folder_summary_get_unread_count (CamelFolderSummary *summary)
1406 {
1407         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1408
1409         return summary->priv->unread_count;
1410 }
1411
1412 /**
1413  * camel_folder_summary_get_deleted_count:
1414  * @summary: a #CamelFolderSummary object
1415  *
1416  * Returns: Count of deleted infos.
1417  *
1418  * Since: 3.4
1419  **/
1420 guint32
1421 camel_folder_summary_get_deleted_count (CamelFolderSummary *summary)
1422 {
1423         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1424
1425         return summary->priv->deleted_count;
1426 }
1427
1428 /**
1429  * camel_folder_summary_get_junk_count:
1430  * @summary: a #CamelFolderSummary object
1431  *
1432  * Returns: Count of junk infos.
1433  *
1434  * Since: 3.4
1435  **/
1436 guint32
1437 camel_folder_summary_get_junk_count (CamelFolderSummary *summary)
1438 {
1439         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1440
1441         return summary->priv->junk_count;
1442 }
1443
1444 /**
1445  * camel_folder_summary_get_junk_not_deleted_count:
1446  * @summary: a #CamelFolderSummary object
1447  *
1448  * Returns: Count of junk and not deleted infos.
1449  *
1450  * Since: 3.4
1451  **/
1452 guint32
1453 camel_folder_summary_get_junk_not_deleted_count (CamelFolderSummary *summary)
1454 {
1455         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1456
1457         return summary->priv->junk_not_deleted_count;
1458 }
1459
1460 /**
1461  * camel_folder_summary_get_visible_count:
1462  * @summary: a #CamelFolderSummary object
1463  *
1464  * Returns: Count of visible (not junk and not deleted) infos.
1465  *
1466  * Since: 3.4
1467  **/
1468 guint32
1469 camel_folder_summary_get_visible_count (CamelFolderSummary *summary)
1470 {
1471         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1472
1473         return summary->priv->visible_count;
1474 }
1475
1476 /**
1477  * camel_folder_summary_set_index:
1478  * @summary: a #CamelFolderSummary object
1479  * @index: a #CamelIndex
1480  *
1481  * Set the index used to index body content.  If the index is %NULL, or
1482  * not set (the default), no indexing of body content will take place.
1483  *
1484  * Unlike earlier behaviour, build_content need not be set to perform indexing.
1485  **/
1486 void
1487 camel_folder_summary_set_index (CamelFolderSummary *summary,
1488                                 CamelIndex *index)
1489 {
1490         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1491
1492         if (index != NULL)
1493                 g_object_ref (index);
1494
1495         if (summary->priv->index != NULL)
1496                 g_object_unref (summary->priv->index);
1497
1498         summary->priv->index = index;
1499 }
1500
1501 /**
1502  * camel_folder_summary_get_index:
1503  * @summary: a #CamelFolderSummary object
1504  *
1505  * Returns: a #CamelIndex used to index body content.
1506  *
1507  * Since: 3.4
1508  **/
1509 CamelIndex *
1510 camel_folder_summary_get_index (CamelFolderSummary *summary)
1511 {
1512         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1513
1514         return summary->priv->index;
1515 }
1516
1517 /**
1518  * camel_folder_summary_set_build_content:
1519  * @summary: a #CamelFolderSummary object
1520  * @state: to build or not to build the content
1521  *
1522  * Set a flag to tell the summary to build the content info summary
1523  * (#CamelMessageInfo.content).  The default is not to build content
1524  * info summaries.
1525  **/
1526 void
1527 camel_folder_summary_set_build_content (CamelFolderSummary *summary,
1528                                         gboolean state)
1529 {
1530         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1531
1532         if (summary->priv->build_content == state)
1533                 return;
1534
1535         summary->priv->build_content = state;
1536
1537         g_object_notify (G_OBJECT (summary), "build-content");
1538 }
1539
1540 /**
1541  * camel_folder_summary_get_build_content:
1542  * @summary: a #CamelFolderSummary object
1543  *
1544  * Returns: Whether to build #CamelMessageInfo.content.
1545  *
1546  * Since: 3.4
1547  **/
1548 gboolean
1549 camel_folder_summary_get_build_content (CamelFolderSummary *summary)
1550 {
1551         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1552
1553         return summary->priv->build_content;
1554 }
1555
1556 /**
1557  * camel_folder_summary_set_need_preview:
1558  *
1559  * Since: 2.28
1560  **/
1561 void
1562 camel_folder_summary_set_need_preview (CamelFolderSummary *summary,
1563                                        gboolean preview)
1564 {
1565         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
1566
1567         summary->priv->need_preview = preview;
1568 }
1569
1570 /**
1571  * camel_folder_summary_get_need_preview:
1572  *
1573  * Since: 2.28
1574  **/
1575 gboolean
1576 camel_folder_summary_get_need_preview (CamelFolderSummary *summary)
1577 {
1578         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1579
1580         return summary->priv->need_preview;
1581 }
1582
1583 /**
1584  * camel_folder_summary_next_uid:
1585  * @summary: a #CamelFolderSummary object
1586  *
1587  * Generate a new unique uid value as an integer.  This
1588  * may be used to create a unique sequence of numbers.
1589  *
1590  * Returns: the next unique uid value
1591  **/
1592 guint32
1593 camel_folder_summary_next_uid (CamelFolderSummary *summary)
1594 {
1595         guint32 uid;
1596
1597         camel_folder_summary_lock (summary);
1598
1599         uid = summary->priv->nextuid++;
1600         camel_folder_summary_touch (summary);
1601
1602         camel_folder_summary_unlock (summary);
1603
1604         return uid;
1605 }
1606
1607 /**
1608  * camel_folder_summary_set_next_uid:
1609  * @summary: a #CamelFolderSummary object
1610  * @uid: The next minimum uid to assign.  To avoid clashing
1611  * uid's, set this to the uid of a given messages + 1.
1612  *
1613  * Set the next minimum uid available.  This can be used to
1614  * ensure new uid's do not clash with existing uid's.
1615  **/
1616 void
1617 camel_folder_summary_set_next_uid (CamelFolderSummary *summary,
1618                                    guint32 uid)
1619 {
1620         camel_folder_summary_lock (summary);
1621
1622         summary->priv->nextuid = MAX (summary->priv->nextuid, uid);
1623         camel_folder_summary_touch (summary);
1624
1625         camel_folder_summary_unlock (summary);
1626 }
1627
1628 /**
1629  * camel_folder_summary_get_next_uid:
1630  * @summary: a #CamelFolderSummary object
1631  *
1632  * Returns: Next uid currently awaiting for assignment. The difference from
1633  *    camel_folder_summary_next_uid() is that this function returns actual
1634  *    value and doesn't increment it before returning.
1635  *
1636  * Since: 3.4
1637  **/
1638 guint32
1639 camel_folder_summary_get_next_uid (CamelFolderSummary *summary)
1640 {
1641         guint32 res;
1642
1643         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1644
1645         camel_folder_summary_lock (summary);
1646
1647         res = summary->priv->nextuid;
1648
1649         camel_folder_summary_unlock (summary);
1650
1651         return res;
1652 }
1653
1654 /**
1655  * camel_folder_summary_next_uid_string:
1656  * @summary: a #CamelFolderSummary object
1657  *
1658  * Retrieve the next uid, but as a formatted string.
1659  *
1660  * Returns: the next uid as an unsigned integer string.
1661  * This string must be freed by the caller.
1662  **/
1663 gchar *
1664 camel_folder_summary_next_uid_string (CamelFolderSummary *summary)
1665 {
1666         CamelFolderSummaryClass *class;
1667
1668         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1669
1670         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1671         g_return_val_if_fail (class->next_uid_string != NULL, NULL);
1672
1673         return class->next_uid_string (summary);
1674 }
1675
1676 /**
1677  * camel_folder_summary_count:
1678  * @summary: a #CamelFolderSummary object
1679  *
1680  * Get the number of summary items stored in this summary.
1681  *
1682  * Returns: the number of items in the summary
1683  **/
1684 guint
1685 camel_folder_summary_count (CamelFolderSummary *summary)
1686 {
1687         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), 0);
1688
1689         return g_hash_table_size (summary->priv->uids);
1690 }
1691
1692 /**
1693  * camel_folder_summary_check_uid
1694  * @summary: a #CamelFolderSummary object
1695  * @uid: a uid
1696  *
1697  * Check if the uid is valid. This isn't very efficient, so it shouldn't be called iteratively.
1698  *
1699  *
1700  * Returns: if the uid is present in the summary or not  (%TRUE or %FALSE)
1701  *
1702  * Since: 2.24
1703  **/
1704 gboolean
1705 camel_folder_summary_check_uid (CamelFolderSummary *summary,
1706                                 const gchar *uid)
1707 {
1708         gboolean ret;
1709
1710         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
1711         g_return_val_if_fail (uid != NULL, FALSE);
1712
1713         camel_folder_summary_lock (summary);
1714
1715         ret = g_hash_table_lookup_extended (summary->priv->uids, uid, NULL, NULL);
1716
1717         camel_folder_summary_unlock (summary);
1718
1719         return ret;
1720 }
1721
1722 static void
1723 folder_summary_dupe_uids_to_array (gpointer key_uid,
1724                                    gpointer value_flags,
1725                                    gpointer user_data)
1726 {
1727         g_ptr_array_add (user_data, (gpointer) camel_pstring_strdup (key_uid));
1728 }
1729
1730 /**
1731  * camel_folder_summary_get_array:
1732  * @summary: a #CamelFolderSummary object
1733  *
1734  * Obtain a copy of the summary array.  This is done atomically,
1735  * so cannot contain empty entries.
1736  *
1737  * Free with camel_folder_summary_free_array()
1738  *
1739  * Returns: a #GPtrArray of uids
1740  *
1741  * Since: 3.4
1742  **/
1743 GPtrArray *
1744 camel_folder_summary_get_array (CamelFolderSummary *summary)
1745 {
1746         GPtrArray *res;
1747
1748         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1749
1750         camel_folder_summary_lock (summary);
1751
1752         res = g_ptr_array_sized_new (g_hash_table_size (summary->priv->uids));
1753         g_hash_table_foreach (summary->priv->uids, folder_summary_dupe_uids_to_array, res);
1754
1755         camel_folder_summary_unlock (summary);
1756
1757         return res;
1758 }
1759
1760 /**
1761  * camel_folder_summary_free_array:
1762  * @array: a #GPtrArray returned from camel_folder_summary_get_array()
1763  *
1764  * Free's array and its elements returned from camel_folder_summary_get_array().
1765  *
1766  * Since: 3.4
1767  **/
1768 void
1769 camel_folder_summary_free_array (GPtrArray *array)
1770 {
1771         if (!array)
1772                 return;
1773
1774         g_ptr_array_foreach (array, (GFunc) camel_pstring_free, NULL);
1775         g_ptr_array_free (array, TRUE);
1776 }
1777
1778 static void
1779 cfs_copy_uids_cb (gpointer key,
1780                   gpointer value,
1781                   gpointer user_data)
1782 {
1783         const gchar *uid = key;
1784         GHashTable *copy_hash = user_data;
1785
1786         g_hash_table_insert (copy_hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
1787 }
1788
1789 /**
1790  * camel_folder_summary_get_hash:
1791  * @summary: a #CamelFolderSummary object
1792  *
1793  * Returns hash of current stored 'uids' in summary, where key is 'uid'
1794  * from the string pool, and value is 1. The returned pointer should
1795  * be freed with g_hash_table_destroy().
1796  *
1797  * Note: When searching for values always use uids from the string pool.
1798  *
1799  * Since: 3.6
1800  **/
1801 GHashTable *
1802 camel_folder_summary_get_hash (CamelFolderSummary *summary)
1803 {
1804         GHashTable *uids;
1805
1806         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1807
1808         camel_folder_summary_lock (summary);
1809
1810         /* using direct hash because of strings being from the string pool */
1811         uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
1812         g_hash_table_foreach (summary->priv->uids, cfs_copy_uids_cb, uids);
1813
1814         camel_folder_summary_unlock (summary);
1815
1816         return uids;
1817 }
1818
1819 /**
1820  * camel_folder_summary_peek_loaded:
1821  *
1822  * Since: 2.26
1823  **/
1824 CamelMessageInfo *
1825 camel_folder_summary_peek_loaded (CamelFolderSummary *summary,
1826                                   const gchar *uid)
1827 {
1828         CamelMessageInfo *info;
1829
1830         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1831
1832         info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1833
1834         if (info)
1835                 camel_message_info_ref (info);
1836
1837         return info;
1838 }
1839
1840 struct _db_pass_data {
1841         GHashTable *columns_hash;
1842         CamelFolderSummary *summary;
1843         gboolean add; /* or just insert to hashtable */
1844 };
1845
1846 static CamelMessageInfo *
1847 message_info_from_uid (CamelFolderSummary *summary,
1848                        const gchar *uid)
1849 {
1850         CamelMessageInfo *info;
1851         gint ret;
1852
1853         camel_folder_summary_lock (summary);
1854
1855         info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1856
1857         if (!info) {
1858                 CamelDB *cdb;
1859                 CamelStore *parent_store;
1860                 const gchar *folder_name;
1861                 struct _db_pass_data data;
1862
1863                 folder_name = camel_folder_get_full_name (summary->priv->folder);
1864
1865                 if (is_in_memory_summary (summary)) {
1866                         camel_folder_summary_unlock (summary);
1867                         g_warning (
1868                                 "%s: Tried to load uid '%s' "
1869                                 "from DB on in-memory summary of '%s'",
1870                                 G_STRFUNC, uid, folder_name);
1871                         return NULL;
1872                 }
1873
1874                 parent_store =
1875                         camel_folder_get_parent_store (summary->priv->folder);
1876                 cdb = parent_store->cdb_r;
1877
1878                 data.columns_hash = NULL;
1879                 data.summary = summary;
1880                 data.add = FALSE;
1881
1882                 ret = camel_db_read_message_info_record_with_uid (
1883                         cdb, folder_name, uid, &data,
1884                         camel_read_mir_callback, NULL);
1885                 if (data.columns_hash)
1886                         g_hash_table_destroy (data.columns_hash);
1887
1888                 if (ret != 0) {
1889                         camel_folder_summary_unlock (summary);
1890                         return NULL;
1891                 }
1892
1893                 /* We would have double reffed at camel_read_mir_callback */
1894                 info = g_hash_table_lookup (summary->priv->loaded_infos, uid);
1895
1896                 cfs_schedule_info_release_timer (summary);
1897         }
1898
1899         if (info)
1900                 camel_message_info_ref (info);
1901
1902         camel_folder_summary_unlock (summary);
1903
1904         return info;
1905 }
1906
1907 /**
1908  * camel_folder_summary_get:
1909  * @summary: a #CamelFolderSummary object
1910  * @uid: a uid
1911  *
1912  * Retrieve a summary item by uid.
1913  *
1914  * A referenced to the summary item is returned, which may be
1915  * ref'd or free'd as appropriate.
1916  *
1917  * Returns: the summary item, or %NULL if the uid @uid is not available
1918  *
1919  * See camel_folder_summary_get_info_flags().
1920  *
1921  * Since: 3.4
1922  **/
1923 CamelMessageInfo *
1924 camel_folder_summary_get (CamelFolderSummary *summary,
1925                           const gchar *uid)
1926 {
1927         CamelFolderSummaryClass *class;
1928
1929         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
1930         g_return_val_if_fail (uid != NULL, NULL);
1931
1932         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
1933         g_return_val_if_fail (class->message_info_from_uid != NULL, NULL);
1934
1935         return class->message_info_from_uid (summary, uid);
1936 }
1937
1938 /**
1939  * camel_folder_summary_get_info_flags:
1940  * @summary: a #CamelFolderSummary object
1941  * @uid: a uid
1942  *
1943  * Retrieve CamelMessageInfo::flags for a message info with UID @uid.
1944  * This is much quicker than camel_folder_summary_get(), because it
1945  * doesn't require reading the message info from a disk.
1946  *
1947  * Returns: the flags currently stored for message info with UID @uid,
1948  *          or (~0) on error
1949  *
1950  * Since: 3.12
1951  **/
1952 guint32
1953 camel_folder_summary_get_info_flags (CamelFolderSummary *summary,
1954                                      const gchar *uid)
1955 {
1956         gpointer ptr_uid = NULL, ptr_flags = NULL;
1957
1958         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), (~0));
1959         g_return_val_if_fail (uid != NULL, (~0));
1960
1961         camel_folder_summary_lock (summary);
1962         if (!g_hash_table_lookup_extended (summary->priv->uids, uid, &ptr_uid, &ptr_flags)) {
1963                 camel_folder_summary_unlock (summary);
1964                 return (~0);
1965         }
1966
1967         camel_folder_summary_unlock (summary);
1968
1969         return GPOINTER_TO_UINT (ptr_flags);
1970 }
1971
1972 static CamelMessageContentInfo *
1973 perform_content_info_load_from_db (CamelFolderSummary *summary,
1974                                    CamelMIRecord *mir)
1975 {
1976         gint i;
1977         guint32 count;
1978         CamelMessageContentInfo *ci, *pci;
1979         gchar *part;
1980
1981         ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_from_db (summary, mir);
1982         if (ci == NULL)
1983                 return NULL;
1984         part = mir->cinfo;
1985         if (!part)
1986                 return ci;
1987         if (*part == ' ') part++;
1988         count = bdata_extract_digit (&part);
1989
1990         mir->cinfo = part;
1991         for (i = 0; i < count; i++) {
1992                 pci = perform_content_info_load_from_db (summary, mir);
1993                 if (pci ) {
1994                         my_list_append ((struct _node **) &ci->childs, (struct _node *) pci);
1995                         pci->parent = ci;
1996                 } else {
1997                         d (fprintf (stderr, "Summary file format messed up?"));
1998                         camel_folder_summary_content_info_free (summary, ci);
1999                         return NULL;
2000                 }
2001         }
2002         return ci;
2003 }
2004
2005 static void
2006 gather_dirty_uids (gpointer key,
2007                    gpointer value,
2008                    gpointer user_data)
2009 {
2010         const gchar *uid = key;
2011         CamelMessageInfoBase *info = value;
2012         GHashTable *hash = user_data;
2013
2014         if (info->dirty)
2015                 g_hash_table_insert (hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2016 }
2017
2018 static void
2019 gather_changed_uids (gpointer key,
2020                      gpointer value,
2021                      gpointer user_data)
2022 {
2023         const gchar *uid = key;
2024         guint32 flags = GPOINTER_TO_UINT (value);
2025         GHashTable *hash = user_data;
2026
2027         if ((flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0)
2028                 g_hash_table_insert (hash, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2029 }
2030
2031 /**
2032  * camel_folder_summary_get_changed:
2033  *
2034  * Since: 2.24
2035  **/
2036 GPtrArray *
2037 camel_folder_summary_get_changed (CamelFolderSummary *summary)
2038 {
2039         GPtrArray *res;
2040         GHashTable *hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
2041
2042         camel_folder_summary_lock (summary);
2043
2044         g_hash_table_foreach (summary->priv->loaded_infos, gather_dirty_uids, hash);
2045         g_hash_table_foreach (summary->priv->uids, gather_changed_uids, hash);
2046
2047         res = g_ptr_array_sized_new (g_hash_table_size (hash));
2048         g_hash_table_foreach (hash, folder_summary_dupe_uids_to_array, res);
2049
2050         camel_folder_summary_unlock (summary);
2051
2052         g_hash_table_destroy (hash);
2053
2054         return res;
2055 }
2056
2057 static void
2058 count_changed_uids (gchar *key,
2059                     CamelMessageInfoBase *info,
2060                     gint *count)
2061 {
2062         if (info->dirty)
2063                 (*count)++;
2064 }
2065
2066 static gint
2067 cfs_count_dirty (CamelFolderSummary *summary)
2068 {
2069         gint count = 0;
2070
2071         camel_folder_summary_lock (summary);
2072         g_hash_table_foreach (summary->priv->loaded_infos, (GHFunc) count_changed_uids, &count);
2073         camel_folder_summary_unlock (summary);
2074
2075         return count;
2076 }
2077
2078 static gboolean
2079 remove_item (gchar *uid,
2080              CamelMessageInfoBase *info,
2081              GSList **to_remove_infos)
2082 {
2083         if (info->refcount == 1 && !info->dirty && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) {
2084                 *to_remove_infos = g_slist_prepend (*to_remove_infos, info);
2085                 return TRUE;
2086         }
2087
2088         return FALSE;
2089 }
2090
2091 static void
2092 remove_cache (CamelSession *session,
2093               GCancellable *cancellable,
2094               CamelFolderSummary *summary,
2095               GError **error)
2096 {
2097         GSList *to_remove_infos = NULL;
2098
2099         CAMEL_DB_RELEASE_SQLITE_MEMORY;
2100
2101         if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2102                 return;
2103
2104         camel_folder_summary_lock (summary);
2105
2106         g_hash_table_foreach_remove (summary->priv->loaded_infos, (GHRFunc) remove_item, &to_remove_infos);
2107
2108         g_slist_foreach (to_remove_infos, (GFunc) camel_message_info_unref, NULL);
2109         g_slist_free (to_remove_infos);
2110
2111         camel_folder_summary_unlock (summary);
2112
2113         summary->priv->cache_load_time = time (NULL);
2114 }
2115
2116 static gboolean
2117 cfs_try_release_memory (CamelFolderSummary *summary)
2118 {
2119         CamelStore *parent_store;
2120         CamelSession *session;
2121
2122         /* If folder is freed or if the cache is nil then clean up */
2123         if (!summary->priv->folder ||
2124             !g_hash_table_size (summary->priv->loaded_infos) ||
2125             is_in_memory_summary (summary)) {
2126                 summary->priv->cache_load_time = 0;
2127                 summary->priv->timeout_handle = 0;
2128                 g_object_unref (summary);
2129
2130                 return FALSE;
2131         }
2132
2133         if (time (NULL) - summary->priv->cache_load_time < SUMMARY_CACHE_DROP)
2134                 return TRUE;
2135
2136         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2137         session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
2138
2139         camel_session_submit_job (
2140                 session,
2141                 (CamelSessionCallback) remove_cache,
2142                 g_object_ref (summary),
2143                 (GDestroyNotify) g_object_unref);
2144
2145         g_object_unref (session);
2146
2147         return TRUE;
2148 }
2149
2150 static void
2151 cfs_schedule_info_release_timer (CamelFolderSummary *summary)
2152 {
2153         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2154
2155         if (is_in_memory_summary (summary))
2156                 return;
2157
2158         if (!summary->priv->timeout_handle) {
2159                 static gboolean know_can_do = FALSE, can_do = TRUE;
2160
2161                 if (!know_can_do) {
2162                         can_do = !g_getenv ("CAMEL_FREE_INFOS");
2163                         know_can_do = TRUE;
2164                 }
2165
2166                 /* FIXME[disk-summary] LRU please and not timeouts */
2167                 if (can_do) {
2168                         summary->priv->timeout_handle = g_timeout_add_seconds (
2169                                 SUMMARY_CACHE_DROP,
2170                                 (GSourceFunc) cfs_try_release_memory,
2171                                 g_object_ref (summary));
2172                         g_source_set_name_by_id (
2173                                 summary->priv->timeout_handle,
2174                                 "[camel] cfs_try_release_memory");
2175                 }
2176         }
2177
2178         /* update also cache load time to the actual, to not release something just loaded */
2179         summary->priv->cache_load_time = time (NULL);
2180 }
2181
2182 static gint
2183 cfs_cache_size (CamelFolderSummary *summary)
2184 {
2185         /* FIXME[disk-summary] this is a timely hack. fix it well */
2186         if (!CAMEL_IS_VEE_FOLDER (summary->priv->folder))
2187                 return g_hash_table_size (summary->priv->loaded_infos);
2188         else
2189                 return g_hash_table_size (summary->priv->uids);
2190 }
2191
2192 /* Update preview of cached messages */
2193
2194 static void
2195 msg_update_preview (const gchar *uid,
2196                     gpointer value,
2197                     CamelFolder *folder)
2198 {
2199         CamelMessageInfoBase *info = (CamelMessageInfoBase *) camel_folder_summary_get (folder->summary, uid);
2200         CamelMimeMessage *msg;
2201         CamelStore *parent_store;
2202         const gchar *full_name;
2203
2204         full_name = camel_folder_get_full_name (folder);
2205         parent_store = camel_folder_get_parent_store (folder);
2206
2207         /* FIXME Pass a GCancellable */
2208         msg = camel_folder_get_message_sync (folder, uid, NULL, NULL);
2209         if (msg != NULL) {
2210                 if (camel_mime_message_build_preview ((CamelMimePart *) msg, (CamelMessageInfo *) info) && info->preview) {
2211                         if (!is_in_memory_summary (folder->summary))
2212                                 camel_db_write_preview_record (parent_store->cdb_w, full_name, info->uid, info->preview, NULL);
2213                 }
2214         }
2215         camel_message_info_unref (info);
2216 }
2217
2218 static void
2219 pick_uids (const gchar *uid,
2220            CamelMessageInfoBase *mi,
2221            GPtrArray *array)
2222 {
2223         if (mi->preview)
2224                 g_ptr_array_add (array, (gchar *) camel_pstring_strdup (uid));
2225 }
2226
2227 static void
2228 copy_all_uids_to_hash (gpointer uid,
2229                        gpointer hash)
2230 {
2231         g_return_if_fail (uid != NULL);
2232
2233         g_hash_table_insert (hash, (gchar *) camel_pstring_strdup (uid), GINT_TO_POINTER (1));
2234 }
2235
2236 static gboolean
2237 fill_mi (const gchar *uid,
2238          const gchar *msg,
2239          CamelFolder *folder)
2240 {
2241         CamelMessageInfoBase *info;
2242
2243         info = g_hash_table_lookup (folder->summary->priv->loaded_infos, uid);
2244         if (info) /* We re assign the memory of msg */
2245                 info->preview = (gchar *) msg;
2246         camel_pstring_free (uid); /* unref the uid */
2247
2248         return TRUE;
2249 }
2250
2251 static void
2252 preview_update (CamelSession *session,
2253                 GCancellable *cancellable,
2254                 CamelFolder *folder,
2255                 GError **error)
2256 {
2257         /* FIXME: Either lock & use or copy & use.*/
2258         GPtrArray *uids_uncached, *uids_array;
2259         GHashTable *preview_data, *uids_hash;
2260         CamelStore *parent_store;
2261         const gchar *full_name;
2262         gboolean is_in_memory = is_in_memory_summary (folder->summary);
2263         gint i;
2264
2265         uids_array = camel_folder_summary_get_array (folder->summary);
2266         uids_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
2267         g_ptr_array_foreach (uids_array, copy_all_uids_to_hash, uids_hash);
2268         uids_uncached = camel_folder_get_uncached_uids (folder, uids_array, NULL);
2269         camel_folder_summary_free_array (uids_array);
2270         uids_array = NULL;
2271
2272         full_name = camel_folder_get_full_name (folder);
2273         parent_store = camel_folder_get_parent_store (folder);
2274         preview_data = is_in_memory ? NULL : camel_db_get_folder_preview (parent_store->cdb_r, full_name, NULL);
2275         if (preview_data) {
2276                 g_hash_table_foreach_remove (preview_data, (GHRFunc) fill_mi, folder);
2277                 g_hash_table_destroy (preview_data);
2278         }
2279
2280         camel_folder_summary_lock (folder->summary);
2281         g_hash_table_foreach (folder->summary->priv->loaded_infos, (GHFunc) pick_uids, uids_uncached);
2282         camel_folder_summary_unlock (folder->summary);
2283
2284         for (i = 0; i < uids_uncached->len; i++) {
2285                 g_hash_table_remove (uids_hash, uids_uncached->pdata[i]);
2286         }
2287
2288         camel_folder_lock (folder);
2289         if (!is_in_memory)
2290                 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2291         g_hash_table_foreach (uids_hash, (GHFunc) msg_update_preview, folder);
2292         if (!is_in_memory)
2293                 camel_db_end_transaction (parent_store->cdb_w, NULL);
2294         camel_folder_unlock (folder);
2295         camel_folder_free_uids (folder, uids_uncached);
2296         g_hash_table_destroy (uids_hash);
2297 }
2298
2299 /* end */
2300
2301 static gint
2302 cfs_reload_from_db (CamelFolderSummary *summary,
2303                     GError **error)
2304 {
2305         CamelDB *cdb;
2306         CamelStore *parent_store;
2307         const gchar *folder_name;
2308         gint ret = 0;
2309         struct _db_pass_data data;
2310
2311         /* FIXME[disk-summary] baseclass this, and vfolders we may have to
2312          * load better. */
2313         d (printf ("\ncamel_folder_summary_reload_from_db called \n"));
2314
2315         if (is_in_memory_summary (summary))
2316                 return 0;
2317
2318         folder_name = camel_folder_get_full_name (summary->priv->folder);
2319         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2320         cdb = parent_store->cdb_r;
2321
2322         data.columns_hash = NULL;
2323         data.summary = summary;
2324         data.add = FALSE;
2325
2326         ret = camel_db_read_message_info_records (
2327                 cdb, folder_name, (gpointer) &data,
2328                 camel_read_mir_callback, NULL);
2329
2330         if (data.columns_hash)
2331                 g_hash_table_destroy (data.columns_hash);
2332
2333         cfs_schedule_info_release_timer (summary);
2334
2335         /* FIXME Convert this to a GTask, submitted through
2336          *       camel_service_queue_task().  Then it won't
2337          *       have to call camel_folder_lock/unlock(). */
2338         if (summary->priv->need_preview) {
2339                 CamelSession *session;
2340
2341                 /* This may not be available in a case of this being called as part
2342                    of CamelSession's dispose, because the CamelService uses GWeakRef
2343                    object which is invalidates its content when it reaches the dispose. */
2344                 session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
2345                 if (session) {
2346                         camel_session_submit_job (
2347                                 session,
2348                                 (CamelSessionCallback) preview_update,
2349                                 g_object_ref (summary->priv->folder),
2350                                 (GDestroyNotify) g_object_unref);
2351                         g_object_unref (session);
2352                 }
2353         }
2354
2355         return ret == 0 ? 0 : -1;
2356 }
2357
2358 /**
2359  * camel_folder_summary_add_preview:
2360  *
2361  * Since: 2.28
2362  **/
2363 void
2364 camel_folder_summary_add_preview (CamelFolderSummary *summary,
2365                                   CamelMessageInfo *info)
2366 {
2367         camel_folder_summary_lock (summary);
2368         g_hash_table_insert (summary->priv->preview_updates, (gchar *) info->uid, ((CamelMessageInfoBase *) info)->preview);
2369         camel_folder_summary_touch (summary);
2370         camel_folder_summary_unlock (summary);
2371 }
2372
2373 /**
2374  * camel_folder_summary_prepare_fetch_all:
2375  * @summary: #CamelFolderSummary object
2376  * @error: return location for a #GError, or %NULL
2377  *
2378  * Loads all infos into memory, if they are not yet and ensures
2379  * they will not be freed in next couple minutes. Call this function
2380  * before any mass operation or when all message infos will be needed,
2381  * for better performance.
2382  *
2383  * Since: 2.32
2384  **/
2385 void
2386 camel_folder_summary_prepare_fetch_all (CamelFolderSummary *summary,
2387                                         GError **error)
2388 {
2389         guint loaded, known;
2390
2391         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2392
2393         loaded = cfs_cache_size (summary);
2394         known = camel_folder_summary_count (summary);
2395
2396         if (known - loaded > 50) {
2397                 camel_folder_summary_lock (summary);
2398                 cfs_reload_from_db (summary, error);
2399                 camel_folder_summary_unlock (summary);
2400         }
2401
2402         /* update also cache load time, even when not loaded anything */
2403         summary->priv->cache_load_time = time (NULL);
2404 }
2405
2406 /**
2407  * camel_folder_summary_load_from_db:
2408  *
2409  * Since: 2.24
2410  **/
2411 gboolean
2412 camel_folder_summary_load_from_db (CamelFolderSummary *summary,
2413                                    GError **error)
2414 {
2415         CamelDB *cdb;
2416         CamelStore *parent_store;
2417         const gchar *full_name;
2418         gint ret = 0;
2419         GError *local_error = NULL;
2420
2421         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
2422
2423         if (is_in_memory_summary (summary))
2424                 return TRUE;
2425
2426         camel_folder_summary_lock (summary);
2427         camel_folder_summary_save_to_db (summary, NULL);
2428
2429         /* struct _db_pass_data data; */
2430         d (printf ("\ncamel_folder_summary_load_from_db called \n"));
2431
2432         full_name = camel_folder_get_full_name (summary->priv->folder);
2433         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2434         if (!camel_folder_summary_header_load_from_db (summary, parent_store, full_name, error)) {
2435                 camel_folder_summary_unlock (summary);
2436                 return FALSE;
2437         }
2438
2439         cdb = parent_store->cdb_r;
2440
2441         ret = camel_db_get_folder_uids (
2442                 cdb, full_name, summary->sort_by, summary->collate,
2443                 summary->priv->uids, &local_error);
2444
2445         if (local_error != NULL && local_error->message != NULL &&
2446             strstr (local_error->message, "no such table") != NULL) {
2447                 g_clear_error (&local_error);
2448
2449                 /* create table the first time it is accessed and missing */
2450                 ret = camel_db_prepare_message_info_table (cdb, full_name, error);
2451         } else if (local_error != NULL)
2452                 g_propagate_error (error, local_error);
2453
2454         camel_folder_summary_unlock (summary);
2455
2456         return ret == 0;
2457 }
2458
2459 static void
2460 mir_from_cols (CamelMIRecord *mir,
2461                CamelFolderSummary *summary,
2462                GHashTable **columns_hash,
2463                gint ncol,
2464                gchar **cols,
2465                gchar **name)
2466 {
2467         gint i;
2468
2469         for (i = 0; i < ncol; ++i) {
2470                 if (!name[i] || !cols[i])
2471                         continue;
2472
2473                 switch (camel_db_get_column_ident (columns_hash, i, ncol, name)) {
2474                         case CAMEL_DB_COLUMN_UID:
2475                                 mir->uid = (gchar *) camel_pstring_strdup (cols[i]);
2476                                 break;
2477                         case CAMEL_DB_COLUMN_FLAGS:
2478                                 mir->flags = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2479                                 break;
2480                         case CAMEL_DB_COLUMN_READ:
2481                                 mir->read = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2482                                 break;
2483                         case CAMEL_DB_COLUMN_DELETED:
2484                                 mir->deleted = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2485                                 break;
2486                         case CAMEL_DB_COLUMN_REPLIED:
2487                                 mir->replied = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2488                                 break;
2489                         case CAMEL_DB_COLUMN_IMPORTANT:
2490                                 mir->important = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2491                                 break;
2492                         case CAMEL_DB_COLUMN_JUNK:
2493                                 mir->junk = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2494                                 break;
2495                         case CAMEL_DB_COLUMN_ATTACHMENT:
2496                                 mir->attachment = (cols[i]) ? ( ((strtoul (cols[i], NULL, 10)) ? TRUE : FALSE)) : FALSE;
2497                                 break;
2498                         case CAMEL_DB_COLUMN_SIZE:
2499                                 mir->size = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
2500                                 break;
2501                         case CAMEL_DB_COLUMN_DSENT:
2502                                 mir->dsent = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2503                                 break;
2504                         case CAMEL_DB_COLUMN_DRECEIVED:
2505                                 mir->dreceived = cols[i] ? strtol (cols[i], NULL, 10) : 0;
2506                                 break;
2507                         case CAMEL_DB_COLUMN_SUBJECT:
2508                                 mir->subject = (gchar *) camel_pstring_strdup (cols[i]);
2509                                 break;
2510                         case CAMEL_DB_COLUMN_MAIL_FROM:
2511                                 mir->from = (gchar *) camel_pstring_strdup (cols[i]);
2512                                 break;
2513                         case CAMEL_DB_COLUMN_MAIL_TO:
2514                                 mir->to = (gchar *) camel_pstring_strdup (cols[i]);
2515                                 break;
2516                         case CAMEL_DB_COLUMN_MAIL_CC:
2517                                 mir->cc = (gchar *) camel_pstring_strdup (cols[i]);
2518                                 break;
2519                         case CAMEL_DB_COLUMN_MLIST:
2520                                 mir->mlist = (gchar *) camel_pstring_strdup (cols[i]);
2521                                 break;
2522                         case CAMEL_DB_COLUMN_FOLLOWUP_FLAG:
2523                                 mir->followup_flag = (gchar *) camel_pstring_strdup (cols[i]);
2524                                 break;
2525                         case CAMEL_DB_COLUMN_FOLLOWUP_COMPLETED_ON:
2526                                 mir->followup_completed_on = (gchar *) camel_pstring_strdup (cols[i]);
2527                                 break;
2528                         case CAMEL_DB_COLUMN_FOLLOWUP_DUE_BY:
2529                                 mir->followup_due_by = (gchar *) camel_pstring_strdup (cols[i]);
2530                                 break;
2531                         case CAMEL_DB_COLUMN_PART:
2532                                 mir->part = g_strdup (cols[i]);
2533                                 break;
2534                         case CAMEL_DB_COLUMN_LABELS:
2535                                 mir->labels = g_strdup (cols[i]);
2536                                 break;
2537                         case CAMEL_DB_COLUMN_USERTAGS:
2538                                 mir->usertags = g_strdup (cols[i]);
2539                                 break;
2540                         case CAMEL_DB_COLUMN_CINFO:
2541                                 mir->cinfo = g_strdup (cols[i]);
2542                                 break;
2543                         case CAMEL_DB_COLUMN_BDATA:
2544                                 mir->bdata = g_strdup (cols[i]);
2545                                 break;
2546                         case CAMEL_DB_COLUMN_BODYSTRUCTURE:
2547                                 /* Evolution itself doesn't yet use this, ignoring */
2548                                 /* mir->bodystructure = g_strdup (cols[i]); */
2549                                 break;
2550                         default:
2551                                 g_warn_if_reached ();
2552                                 break;
2553                 }
2554         }
2555 }
2556
2557 static gint
2558 camel_read_mir_callback (gpointer ref,
2559                          gint ncol,
2560                          gchar **cols,
2561                          gchar **name)
2562 {
2563         struct _db_pass_data *data = (struct _db_pass_data *) ref;
2564         CamelFolderSummary *summary = data->summary;
2565         CamelMIRecord *mir;
2566         CamelMessageInfo *info;
2567         gint ret = 0;
2568
2569         mir = g_new0 (CamelMIRecord , 1);
2570         mir_from_cols (mir, summary, &data->columns_hash, ncol, cols, name);
2571
2572         camel_folder_summary_lock (summary);
2573         if (!mir->uid || g_hash_table_lookup (summary->priv->loaded_infos, mir->uid)) {
2574                 /* Unlock and better return */
2575                 camel_folder_summary_unlock (summary);
2576                 camel_db_camel_mir_free (mir);
2577                 return ret;
2578         }
2579         camel_folder_summary_unlock (summary);
2580
2581         info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_from_db (summary, mir);
2582
2583         if (info) {
2584
2585                 if (summary->priv->build_content) {
2586                         gchar *tmp;
2587                         tmp = mir->cinfo;
2588                         /* FIXME: this should be done differently, how i don't know */
2589                         ((CamelMessageInfoBase *) info)->content = perform_content_info_load_from_db (summary, mir);
2590                         if (((CamelMessageInfoBase *) info)->content == NULL) {
2591                                 camel_message_info_unref (info);
2592                                 info = NULL;
2593                         }
2594                         mir->cinfo = tmp;
2595
2596                         if (!info) {
2597                                 camel_db_camel_mir_free (mir);
2598                                 return -1;
2599                         }
2600                 }
2601
2602                 /* Just now we are reading from the DB, it can't be dirty. */
2603                 ((CamelMessageInfoBase *) info)->dirty = FALSE;
2604                 if (data->add)
2605                         camel_folder_summary_add (summary, info);
2606                 else
2607                         camel_folder_summary_insert (summary, info, TRUE);
2608
2609         } else {
2610                 g_warning ("Loading messageinfo from db failed");
2611                 ret = -1;
2612         }
2613
2614         camel_db_camel_mir_free (mir);
2615
2616         return ret;
2617 }
2618
2619 /* saves the content descriptions, recursively */
2620 static gboolean
2621 perform_content_info_save_to_db (CamelFolderSummary *summary,
2622                                  CamelMessageContentInfo *ci,
2623                                  CamelMIRecord *record)
2624 {
2625         CamelMessageContentInfo *part;
2626         gchar *oldr;
2627
2628         if (!CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_to_db (summary, ci, record))
2629                 return FALSE;
2630
2631         oldr = record->cinfo;
2632         record->cinfo = g_strdup_printf ("%s %d", oldr, my_list_size ((struct _node **) &ci->childs));
2633         g_free (oldr);
2634
2635         part = ci->childs;
2636         while (part) {
2637                 if (perform_content_info_save_to_db (summary, part, record) == -1)
2638                         return FALSE;
2639                 part = part->next;
2640         }
2641
2642         return TRUE;
2643 }
2644
2645 static void
2646 save_to_db_cb (gpointer key,
2647                gpointer value,
2648                gpointer data)
2649 {
2650         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) value;
2651         CamelFolderSummary *summary = (CamelFolderSummary *) mi->summary;
2652         CamelStore *parent_store;
2653         const gchar *full_name;
2654         CamelDB *cdb;
2655         CamelMIRecord *mir;
2656         GError **error = data;
2657
2658         full_name = camel_folder_get_full_name (summary->priv->folder);
2659         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2660         cdb = parent_store->cdb_w;
2661
2662         if (!mi->dirty)
2663                 return;
2664
2665         mir = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_to_db (summary, (CamelMessageInfo *) mi);
2666
2667         if (mir && summary->priv->build_content) {
2668                 if (!perform_content_info_save_to_db (summary, ((CamelMessageInfoBase *) mi)->content, mir)) {
2669                         g_warning ("unable to save mir+cinfo for uid: %s\n", mir->uid);
2670                         camel_db_camel_mir_free (mir);
2671                         /* FIXME: Add exception here */
2672                         return;
2673                 }
2674         }
2675
2676         g_return_if_fail (mir != NULL);
2677
2678         if (camel_db_write_message_info_record (cdb, full_name, mir, error) != 0) {
2679                 camel_db_camel_mir_free (mir);
2680                 return;
2681         }
2682
2683         /* Reset the dirty flag which decides if the changes are synced to the DB or not.
2684         The FOLDER_FLAGGED should be used to check if the changes are synced to the server.
2685         So, dont unset the FOLDER_FLAGGED flag */
2686         mi->dirty = FALSE;
2687
2688         camel_db_camel_mir_free (mir);
2689 }
2690
2691 static gint
2692 save_message_infos_to_db (CamelFolderSummary *summary,
2693                           GError **error)
2694 {
2695         CamelStore *parent_store;
2696         CamelDB *cdb;
2697         const gchar *full_name;
2698
2699         if (is_in_memory_summary (summary))
2700                 return 0;
2701
2702         full_name = camel_folder_get_full_name (summary->priv->folder);
2703         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2704         cdb = parent_store->cdb_w;
2705
2706         if (camel_db_prepare_message_info_table (cdb, full_name, error) != 0)
2707                 return -1;
2708
2709         camel_folder_summary_lock (summary);
2710
2711         /* Push MessageInfo-es */
2712         camel_db_begin_transaction (cdb, NULL);
2713         g_hash_table_foreach (summary->priv->loaded_infos, save_to_db_cb, error);
2714         camel_db_end_transaction (cdb, NULL);
2715
2716         camel_folder_summary_unlock (summary);
2717         cfs_schedule_info_release_timer (summary);
2718
2719         return 0;
2720 }
2721
2722 static void
2723 msg_save_preview (const gchar *uid,
2724                   gpointer value,
2725                   CamelFolder *folder)
2726 {
2727         CamelStore *parent_store;
2728         const gchar *full_name;
2729
2730         full_name = camel_folder_get_full_name (folder);
2731         parent_store = camel_folder_get_parent_store (folder);
2732
2733         camel_db_write_preview_record (
2734                 parent_store->cdb_w, full_name, uid, (gchar *) value, NULL);
2735 }
2736
2737 /**
2738  * camel_folder_summary_save_to_db:
2739  *
2740  * Since: 2.24
2741  **/
2742 gboolean
2743 camel_folder_summary_save_to_db (CamelFolderSummary *summary,
2744                                  GError **error)
2745 {
2746         CamelStore *parent_store;
2747         CamelDB *cdb;
2748         CamelFIRecord *record;
2749         gint ret, count;
2750
2751         g_return_val_if_fail (summary != NULL, FALSE);
2752
2753         if (!(summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) ||
2754             is_in_memory_summary (summary))
2755                 return TRUE;
2756
2757         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2758         cdb = parent_store->cdb_w;
2759
2760         camel_folder_summary_lock (summary);
2761
2762         d (printf ("\ncamel_folder_summary_save_to_db called \n"));
2763         if (summary->priv->need_preview && g_hash_table_size (summary->priv->preview_updates)) {
2764                 camel_db_begin_transaction (parent_store->cdb_w, NULL);
2765                 g_hash_table_foreach (summary->priv->preview_updates, (GHFunc) msg_save_preview, summary->priv->folder);
2766                 g_hash_table_remove_all (summary->priv->preview_updates);
2767                 camel_db_end_transaction (parent_store->cdb_w, NULL);
2768         }
2769
2770         summary->flags &= ~CAMEL_FOLDER_SUMMARY_DIRTY;
2771
2772         count = cfs_count_dirty (summary);
2773         if (!count) {
2774                 gboolean res = camel_folder_summary_header_save_to_db (summary, error);
2775                 camel_folder_summary_unlock (summary);
2776                 return res;
2777         }
2778
2779         ret = save_message_infos_to_db (summary, error);
2780         if (ret != 0) {
2781                 /* Failed, so lets reset the flag */
2782                 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2783                 camel_folder_summary_unlock (summary);
2784                 return FALSE;
2785         }
2786
2787         /* XXX So... if an error is set, how do we even reach this point
2788          *     given the above error check?  Oye vey this logic is nasty. */
2789         if (error != NULL && *error != NULL &&
2790                 strstr ((*error)->message, "26 columns but 28 values") != NULL) {
2791                 const gchar *full_name;
2792
2793                 full_name = camel_folder_get_full_name (summary->priv->folder);
2794                 g_warning ("Fixing up a broken summary migration on %s\n", full_name);
2795
2796                 /* Begin everything again. */
2797                 camel_db_begin_transaction (cdb, NULL);
2798                 camel_db_reset_folder_version (cdb, full_name, 0, NULL);
2799                 camel_db_end_transaction (cdb, NULL);
2800
2801                 ret = save_message_infos_to_db (summary, error);
2802                 if (ret != 0) {
2803                         summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2804                         camel_folder_summary_unlock (summary);
2805                         return FALSE;
2806                 }
2807         }
2808
2809         record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2810         if (!record) {
2811                 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2812                 camel_folder_summary_unlock (summary);
2813                 return FALSE;
2814         }
2815
2816         camel_db_begin_transaction (cdb, NULL);
2817         ret = camel_db_write_folder_info_record (cdb, record, error);
2818         g_free (record->folder_name);
2819         g_free (record->bdata);
2820         g_free (record);
2821
2822         if (ret != 0) {
2823                 camel_db_abort_transaction (cdb, NULL);
2824                 summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
2825                 camel_folder_summary_unlock (summary);
2826                 return FALSE;
2827         }
2828
2829         camel_db_end_transaction (cdb, NULL);
2830         camel_folder_summary_unlock (summary);
2831
2832         return ret == 0;
2833 }
2834
2835 /**
2836  * camel_folder_summary_header_save_to_db:
2837  *
2838  * Since: 2.24
2839  **/
2840 gboolean
2841 camel_folder_summary_header_save_to_db (CamelFolderSummary *summary,
2842                                         GError **error)
2843 {
2844         CamelStore *parent_store;
2845         CamelFIRecord *record;
2846         CamelDB *cdb;
2847         gint ret;
2848
2849         if (is_in_memory_summary (summary))
2850                 return TRUE;
2851
2852         parent_store = camel_folder_get_parent_store (summary->priv->folder);
2853         cdb = parent_store->cdb_w;
2854         camel_folder_summary_lock (summary);
2855
2856         d (printf ("\ncamel_folder_summary_header_save_to_db called \n"));
2857
2858         record = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_to_db (summary, error);
2859         if (!record) {
2860                 camel_folder_summary_unlock (summary);
2861                 return FALSE;
2862         }
2863
2864         camel_db_begin_transaction (cdb, NULL);
2865         ret = camel_db_write_folder_info_record (cdb, record, error);
2866         g_free (record->folder_name);
2867         g_free (record->bdata);
2868         g_free (record);
2869
2870         if (ret != 0) {
2871                 camel_db_abort_transaction (cdb, NULL);
2872                 camel_folder_summary_unlock (summary);
2873                 return FALSE;
2874         }
2875
2876         camel_db_end_transaction (cdb, NULL);
2877         camel_folder_summary_unlock (summary);
2878
2879         return ret == 0;
2880 }
2881
2882 /**
2883  * camel_folder_summary_header_load_from_db:
2884  *
2885  * Since: 2.24
2886  **/
2887 gboolean
2888 camel_folder_summary_header_load_from_db (CamelFolderSummary *summary,
2889                                           CamelStore *store,
2890                                           const gchar *folder_name,
2891                                           GError **error)
2892 {
2893         CamelDB *cdb;
2894         CamelFIRecord *record;
2895         gboolean ret = FALSE;
2896
2897         d (printf ("\ncamel_folder_summary_header_load_from_db called \n"));
2898
2899         if (is_in_memory_summary (summary))
2900                 return TRUE;
2901
2902         camel_folder_summary_lock (summary);
2903         camel_folder_summary_save_to_db (summary, NULL);
2904
2905         cdb = store->cdb_r;
2906
2907         record = g_new0 (CamelFIRecord, 1);
2908         camel_db_read_folder_info_record (cdb, folder_name, record, error);
2909
2910         ret = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->summary_header_from_db (summary, record);
2911
2912         camel_folder_summary_unlock (summary);
2913
2914         g_free (record->folder_name);
2915         g_free (record->bdata);
2916         g_free (record);
2917
2918         return ret;
2919 }
2920
2921 static gboolean
2922 summary_assign_uid (CamelFolderSummary *summary,
2923                     CamelMessageInfo *info)
2924 {
2925         const gchar *uid;
2926         CamelMessageInfo *mi;
2927
2928         uid = camel_message_info_uid (info);
2929
2930         if (uid == NULL || uid[0] == 0) {
2931                 camel_pstring_free (info->uid);
2932                 uid = info->uid = (gchar *) camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2933         }
2934
2935         camel_folder_summary_lock (summary);
2936
2937         while ((mi = g_hash_table_lookup (summary->priv->loaded_infos, uid))) {
2938                 camel_folder_summary_unlock (summary);
2939
2940                 if (mi == info)
2941                         return FALSE;
2942
2943                 d (printf ("Trying to insert message with clashing uid (%s).  new uid re-assigned", camel_message_info_uid (info)));
2944
2945                 camel_pstring_free (info->uid);
2946                 uid = info->uid = camel_pstring_add (camel_folder_summary_next_uid_string (summary), TRUE);
2947                 camel_message_info_set_flags (info, CAMEL_MESSAGE_FOLDER_FLAGGED, CAMEL_MESSAGE_FOLDER_FLAGGED);
2948
2949                 camel_folder_summary_lock (summary);
2950         }
2951
2952         camel_folder_summary_unlock (summary);
2953
2954         return TRUE;
2955 }
2956
2957 /**
2958  * camel_folder_summary_add:
2959  * @summary: a #CamelFolderSummary object
2960  * @info: a #CamelMessageInfo
2961  *
2962  * Adds a new @info record to the summary.  If @info->uid is %NULL,
2963  * then a new uid is automatically re-assigned by calling
2964  * camel_folder_summary_next_uid_string().
2965  *
2966  * The @info record should have been generated by calling one of the
2967  * info_new_*() functions, as it will be free'd based on the summary
2968  * class.  And MUST NOT be allocated directly using malloc.
2969  **/
2970 void
2971 camel_folder_summary_add (CamelFolderSummary *summary,
2972                           CamelMessageInfo *info)
2973 {
2974         CamelMessageInfoBase *base_info;
2975
2976         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
2977
2978         if (info == NULL)
2979                 return;
2980
2981         camel_folder_summary_lock (summary);
2982         if (!summary_assign_uid (summary, info)) {
2983                 camel_folder_summary_unlock (summary);
2984                 return;
2985         }
2986
2987         base_info = (CamelMessageInfoBase *) info;
2988         folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
2989         base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
2990         base_info->dirty = TRUE;
2991
2992         g_hash_table_insert (
2993                 summary->priv->uids,
2994                 (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
2995                 GUINT_TO_POINTER (camel_message_info_flags (info)));
2996
2997         /* Summary always holds a ref for the loaded infos */
2998         g_hash_table_insert (summary->priv->loaded_infos, (gpointer) camel_message_info_uid (info), info);
2999
3000         camel_folder_summary_touch (summary);
3001
3002         camel_folder_summary_unlock (summary);
3003 }
3004
3005 /**
3006  * camel_folder_summary_insert:
3007  *
3008  * Since: 2.24
3009  **/
3010 void
3011 camel_folder_summary_insert (CamelFolderSummary *summary,
3012                              CamelMessageInfo *info,
3013                              gboolean load)
3014 {
3015         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
3016
3017         if (info == NULL)
3018                 return;
3019
3020         camel_folder_summary_lock (summary);
3021
3022         if (!load) {
3023                 CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
3024
3025                 folder_summary_update_counts_by_flags (summary, camel_message_info_flags (info), UPDATE_COUNTS_ADD);
3026                 base_info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
3027                 base_info->dirty = TRUE;
3028
3029                 g_hash_table_insert (
3030                         summary->priv->uids,
3031                         (gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
3032                         GUINT_TO_POINTER (camel_message_info_flags (info)));
3033
3034                 camel_folder_summary_touch (summary);
3035         }
3036
3037         /* Summary always holds a ref for the loaded infos */
3038         g_hash_table_insert (summary->priv->loaded_infos, (gchar *) camel_message_info_uid (info), info);
3039
3040         camel_folder_summary_unlock (summary);
3041 }
3042
3043 /**
3044  * camel_folder_summary_info_new_from_header:
3045  * @summary: a #CamelFolderSummary object
3046  * @headers: rfc822 headers
3047  *
3048  * Create a new info record from a header.
3049  *
3050  * Returns: the newly allocated record which must be unreferenced with
3051  *          camel_message_info_unref()
3052  **/
3053 CamelMessageInfo *
3054 camel_folder_summary_info_new_from_header (CamelFolderSummary *summary,
3055                                            struct _camel_header_raw *h)
3056 {
3057         CamelFolderSummaryClass *class;
3058
3059         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
3060
3061         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3062         g_return_val_if_fail (class->message_info_new_from_header != NULL, NULL);
3063
3064         return class->message_info_new_from_header (summary, h);
3065 }
3066
3067 /**
3068  * camel_folder_summary_info_new_from_parser:
3069  * @summary: a #CamelFolderSummary object
3070  * @parser: a #CamelMimeParser object
3071  *
3072  * Create a new info record from a parser.  If the parser cannot
3073  * determine a uid, then none will be assigned.
3074  *
3075  * If indexing is enabled, and the parser cannot determine a new uid, then
3076  * one is automatically assigned.
3077  *
3078  * If indexing is enabled, then the content will be indexed based
3079  * on this new uid.  In this case, the message info MUST be
3080  * added using :add().
3081  *
3082  * Once complete, the parser will be positioned at the end of
3083  * the message.
3084  *
3085  * Returns: the newly allocated record which must be unreferenced with
3086  *          camel_message_info_unref()
3087  **/
3088 CamelMessageInfo *
3089 camel_folder_summary_info_new_from_parser (CamelFolderSummary *summary,
3090                                            CamelMimeParser *mp)
3091 {
3092         CamelMessageInfo *info = NULL;
3093         gchar *buffer;
3094         gsize len;
3095         CamelFolderSummaryPrivate *p = summary->priv;
3096         goffset start;
3097         CamelIndexName *name = NULL;
3098
3099         /* should this check the parser is in the right state, or assume it is?? */
3100
3101         start = camel_mime_parser_tell (mp);
3102         if (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_EOF) {
3103                 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_parser (summary, mp);
3104
3105                 camel_mime_parser_unstep (mp);
3106
3107                 /* assign a unique uid, this is slightly 'wrong' as we do not really
3108                  * know if we are going to store this in the summary, but no matter */
3109                 if (p->index)
3110                         summary_assign_uid (summary, info);
3111
3112                 g_rec_mutex_lock (&summary->priv->filter_lock);
3113
3114                 if (p->index) {
3115                         if (p->filter_index == NULL)
3116                                 p->filter_index = camel_mime_filter_index_new (p->index);
3117                         camel_index_delete_name (p->index, camel_message_info_uid (info));
3118                         name = camel_index_add_name (p->index, camel_message_info_uid (info));
3119                         camel_mime_filter_index_set_name (CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3120                 }
3121
3122                 /* always scan the content info, even if we dont save it */
3123                 ((CamelMessageInfoBase *) info)->content = summary_build_content_info (summary, info, mp);
3124
3125                 if (name && p->index) {
3126                         camel_index_write_name (p->index, name);
3127                         g_object_unref (name);
3128                         camel_mime_filter_index_set_name (
3129                                 CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3130                 }
3131
3132                 g_rec_mutex_unlock (&summary->priv->filter_lock);
3133
3134                 ((CamelMessageInfoBase *) info)->size = camel_mime_parser_tell (mp) - start;
3135         }
3136         return info;
3137 }
3138
3139 /**
3140  * camel_folder_summary_info_new_from_message:
3141  * @summary: a #CamelFolderSummary object
3142  * @message: a #CamelMimeMessage object
3143  * @bodystructure: a bodystructure or NULL
3144  *
3145  * Create a summary item from a message.
3146  *
3147  * Returns: the newly allocated record which must be unreferenced with
3148  *          camel_message_info_unref()
3149  **/
3150 CamelMessageInfo *
3151 camel_folder_summary_info_new_from_message (CamelFolderSummary *summary,
3152                                             CamelMimeMessage *msg,
3153                                             const gchar *bodystructure)
3154 {
3155         CamelMessageInfo *info;
3156         CamelFolderSummaryPrivate *p = summary->priv;
3157         CamelIndexName *name = NULL;
3158
3159         info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_message (summary, msg, bodystructure);
3160
3161         /* assign a unique uid, this is slightly 'wrong' as we do not really
3162          * know if we are going to store this in the summary, but we need it set for indexing */
3163         if (p->index)
3164                 summary_assign_uid (summary, info);
3165
3166         g_rec_mutex_lock (&summary->priv->filter_lock);
3167
3168         if (p->index) {
3169                 if (p->filter_index == NULL)
3170                         p->filter_index = camel_mime_filter_index_new (p->index);
3171                 camel_index_delete_name (p->index, camel_message_info_uid (info));
3172                 name = camel_index_add_name (p->index, camel_message_info_uid (info));
3173                 camel_mime_filter_index_set_name (
3174                         CAMEL_MIME_FILTER_INDEX (p->filter_index), name);
3175
3176                 if (p->filter_stream == NULL) {
3177                         CamelStream *null = camel_stream_null_new ();
3178
3179                         p->filter_stream = camel_stream_filter_new (null);
3180                         g_object_unref (null);
3181                 }
3182         }
3183
3184         ((CamelMessageInfoBase *) info)->content = summary_build_content_info_message (summary, info, (CamelMimePart *) msg);
3185
3186         if (name) {
3187                 camel_index_write_name (p->index, name);
3188                 g_object_unref (name);
3189                 camel_mime_filter_index_set_name (
3190                         CAMEL_MIME_FILTER_INDEX (p->filter_index), NULL);
3191         }
3192
3193         g_rec_mutex_unlock (&summary->priv->filter_lock);
3194
3195         return info;
3196 }
3197
3198 /**
3199  * camel_folder_summary_content_info_free:
3200  * @summary: a #CamelFolderSummary object
3201  * @ci: a #CamelMessageContentInfo
3202  *
3203  * Free the content info @ci, and all associated memory.
3204  **/
3205 void
3206 camel_folder_summary_content_info_free (CamelFolderSummary *summary,
3207                                         CamelMessageContentInfo *ci)
3208 {
3209         CamelMessageContentInfo *pw, *pn;
3210
3211         pw = ci->childs;
3212         CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_free (summary, ci);
3213         while (pw) {
3214                 pn = pw->next;
3215                 camel_folder_summary_content_info_free (summary, pw);
3216                 pw = pn;
3217         }
3218 }
3219
3220 /**
3221  * camel_folder_summary_touch:
3222  * @summary: a #CamelFolderSummary object
3223  *
3224  * Mark the summary as changed, so that a save will force it to be
3225  * written back to disk.
3226  **/
3227 void
3228 camel_folder_summary_touch (CamelFolderSummary *summary)
3229 {
3230         camel_folder_summary_lock (summary);
3231         summary->flags |= CAMEL_FOLDER_SUMMARY_DIRTY;
3232         camel_folder_summary_unlock (summary);
3233 }
3234
3235 /**
3236  * camel_folder_summary_clear:
3237  * @summary: a #CamelFolderSummary object
3238  *
3239  * Empty the summary contents.
3240  **/
3241 gboolean
3242 camel_folder_summary_clear (CamelFolderSummary *summary,
3243                             GError **error)
3244 {
3245         GObject *summary_object;
3246         CamelStore *parent_store;
3247         CamelDB *cdb;
3248         const gchar *folder_name;
3249         gboolean res;
3250
3251         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3252
3253         camel_folder_summary_lock (summary);
3254         if (camel_folder_summary_count (summary) == 0) {
3255                 camel_folder_summary_unlock (summary);
3256                 return TRUE;
3257         }
3258
3259         g_hash_table_remove_all (summary->priv->uids);
3260         remove_all_loaded (summary);
3261         g_hash_table_remove_all (summary->priv->loaded_infos);
3262
3263         summary->priv->saved_count = 0;
3264         summary->priv->unread_count = 0;
3265         summary->priv->deleted_count = 0;
3266         summary->priv->junk_count = 0;
3267         summary->priv->junk_not_deleted_count = 0;
3268         summary->priv->visible_count = 0;
3269
3270         camel_folder_summary_touch (summary);
3271
3272         folder_name = camel_folder_get_full_name (summary->priv->folder);
3273         parent_store = camel_folder_get_parent_store (summary->priv->folder);
3274         cdb = parent_store->cdb_w;
3275
3276         if (!is_in_memory_summary (summary))
3277                 res = camel_db_clear_folder_summary (cdb, folder_name, error) == 0;
3278         else
3279                 res = TRUE;
3280
3281         summary_object = G_OBJECT (summary);
3282         g_object_freeze_notify (summary_object);
3283         g_object_notify (summary_object, "saved-count");
3284         g_object_notify (summary_object, "unread-count");
3285         g_object_notify (summary_object, "deleted-count");
3286         g_object_notify (summary_object, "junk-count");
3287         g_object_notify (summary_object, "junk-not-deleted-count");
3288         g_object_notify (summary_object, "visible-count");
3289         g_object_thaw_notify (summary_object);
3290
3291         camel_folder_summary_unlock (summary);
3292
3293         return res;
3294 }
3295
3296 /**
3297  * camel_folder_summary_remove:
3298  * @summary: a #CamelFolderSummary object
3299  * @info: a #CamelMessageInfo
3300  *
3301  * Remove a specific @info record from the summary.
3302  *
3303  * Returns: Whether the @info was found and removed from the @summary.
3304  **/
3305 gboolean
3306 camel_folder_summary_remove (CamelFolderSummary *summary,
3307                              CamelMessageInfo *info)
3308 {
3309         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3310         g_return_val_if_fail (info != NULL, FALSE);
3311
3312         if (camel_folder_summary_remove_uid (summary, camel_message_info_uid (info))) {
3313                 camel_message_info_unref (info);
3314                 return TRUE;
3315         }
3316
3317         return FALSE;
3318 }
3319
3320 /**
3321  * camel_folder_summary_remove_uid:
3322  * @summary: a #CamelFolderSummary object
3323  * @uid: a uid
3324  *
3325  * Remove a specific info record from the summary, by @uid.
3326  *
3327  * Returns: Whether the @uid was found and removed from the @summary.
3328  **/
3329 gboolean
3330 camel_folder_summary_remove_uid (CamelFolderSummary *summary,
3331                                  const gchar *uid)
3332 {
3333         gpointer ptr_uid = NULL, ptr_flags = NULL;
3334         CamelStore *parent_store;
3335         const gchar *full_name;
3336         const gchar *uid_copy;
3337         gboolean res = TRUE;
3338
3339         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3340         g_return_val_if_fail (uid != NULL, FALSE);
3341
3342         camel_folder_summary_lock (summary);
3343         if (!g_hash_table_lookup_extended (summary->priv->uids, uid, &ptr_uid, &ptr_flags)) {
3344                 camel_folder_summary_unlock (summary);
3345                 return FALSE;
3346         }
3347
3348         folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3349
3350         uid_copy = camel_pstring_strdup (uid);
3351         g_hash_table_remove (summary->priv->uids, uid_copy);
3352         g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3353
3354         if (!is_in_memory_summary (summary)) {
3355                 full_name = camel_folder_get_full_name (summary->priv->folder);
3356                 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3357                 if (camel_db_delete_uid (parent_store->cdb_w, full_name, uid_copy, NULL) != 0)
3358                         res = FALSE;
3359         }
3360
3361         camel_pstring_free (uid_copy);
3362
3363         camel_folder_summary_touch (summary);
3364         camel_folder_summary_unlock (summary);
3365
3366         return res;
3367 }
3368
3369 /**
3370  * camel_folder_summary_remove_uids:
3371  * @summary: a #CamelFolderSummary object
3372  * @uids: a GList of uids
3373  *
3374  * Remove a specific info record from the summary, by @uid.
3375  *
3376  * Returns: Whether the @uid was found and removed from the @summary.
3377  *
3378  * Since: 3.6
3379  **/
3380 gboolean
3381 camel_folder_summary_remove_uids (CamelFolderSummary *summary,
3382                                   GList *uids)
3383 {
3384         CamelStore *parent_store;
3385         const gchar *full_name;
3386         GList *l;
3387         gboolean res = TRUE;
3388
3389         g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
3390         g_return_val_if_fail (uids != NULL, FALSE);
3391
3392         g_object_freeze_notify (G_OBJECT (summary));
3393         camel_folder_summary_lock (summary);
3394
3395         for (l = g_list_first (uids); l; l = g_list_next (l)) {
3396                 gpointer ptr_uid = NULL, ptr_flags = NULL;
3397                 if (g_hash_table_lookup_extended (summary->priv->uids, l->data, &ptr_uid, &ptr_flags)) {
3398                         const gchar *uid_copy = camel_pstring_strdup (l->data);
3399                         CamelMessageInfo *mi;
3400
3401                         folder_summary_update_counts_by_flags (summary, GPOINTER_TO_UINT (ptr_flags), UPDATE_COUNTS_SUB);
3402                         g_hash_table_remove (summary->priv->uids, uid_copy);
3403
3404                         mi = g_hash_table_lookup (summary->priv->loaded_infos, uid_copy);
3405                         g_hash_table_remove (summary->priv->loaded_infos, uid_copy);
3406
3407                         if (mi)
3408                                 camel_message_info_unref (mi);
3409                         camel_pstring_free (uid_copy);
3410                 }
3411         }
3412
3413         if (!is_in_memory_summary (summary)) {
3414                 full_name = camel_folder_get_full_name (summary->priv->folder);
3415                 parent_store = camel_folder_get_parent_store (summary->priv->folder);
3416                 if (camel_db_delete_uids (parent_store->cdb_w, full_name, uids, NULL) != 0)
3417                         res = FALSE;
3418         }
3419
3420         camel_folder_summary_touch (summary);
3421         camel_folder_summary_unlock (summary);
3422         g_object_thaw_notify (G_OBJECT (summary));
3423
3424         return res;
3425 }
3426
3427 static struct _node *
3428 my_list_append (struct _node **list,
3429                 struct _node *n)
3430 {
3431         struct _node *ln = *list;
3432         n->next = NULL;
3433
3434         if (!ln) {
3435                 *list = n;
3436                 return n;
3437         }
3438
3439         while (ln->next)
3440                 ln = ln->next;
3441         ln->next = n;
3442         return n;
3443 }
3444
3445 static gint
3446 my_list_size (struct _node **list)
3447 {
3448         gint len = 0;
3449         struct _node *ln = (struct _node *) list;
3450         while (ln->next) {
3451                 ln = ln->next;
3452                 len++;
3453         }
3454         return len;
3455 }
3456
3457 /* are these even useful for anything??? */
3458 static CamelMessageInfo *
3459 message_info_new_from_parser (CamelFolderSummary *summary,
3460                               CamelMimeParser *mp)
3461 {
3462         CamelMessageInfo *mi = NULL;
3463         gint state;
3464
3465         state = camel_mime_parser_state (mp);
3466         switch (state) {
3467         case CAMEL_MIME_PARSER_STATE_HEADER:
3468         case CAMEL_MIME_PARSER_STATE_MESSAGE:
3469         case CAMEL_MIME_PARSER_STATE_MULTIPART:
3470                 mi = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->message_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3471                 break;
3472         default:
3473                 g_error ("Invalid parser state");
3474         }
3475
3476         return mi;
3477 }
3478
3479 static CamelMessageContentInfo *
3480 content_info_new_from_parser (CamelFolderSummary *summary,
3481                               CamelMimeParser *mp)
3482 {
3483         CamelMessageContentInfo *ci = NULL;
3484
3485         switch (camel_mime_parser_state (mp)) {
3486         case CAMEL_MIME_PARSER_STATE_HEADER:
3487         case CAMEL_MIME_PARSER_STATE_MESSAGE:
3488         case CAMEL_MIME_PARSER_STATE_MULTIPART:
3489                 ci = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_header (summary, camel_mime_parser_headers_raw (mp));
3490                 if (ci) {
3491                         if (ci->type)
3492                                 camel_content_type_unref (ci->type);
3493                         ci->type = camel_mime_parser_content_type (mp);
3494                         camel_content_type_ref (ci->type);
3495                 }
3496                 break;
3497         default:
3498                 g_error ("Invalid parser state");
3499         }
3500
3501         return ci;
3502 }
3503
3504 static CamelMessageInfo *
3505 message_info_new_from_message (CamelFolderSummary *summary,
3506                                CamelMimeMessage *msg,
3507                                const gchar *bodystructure)
3508 {
3509         CamelMessageInfo *mi;
3510
3511         mi = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->message_info_new_from_header (summary, ((CamelMimePart *) msg)->headers);
3512         ((CamelMessageInfoBase *) mi)->bodystructure = g_strdup (bodystructure);
3513
3514         return mi;
3515 }
3516
3517 static CamelMessageContentInfo *
3518 content_info_new_from_message (CamelFolderSummary *summary,
3519                                CamelMimePart *mp)
3520 {
3521         CamelMessageContentInfo *ci;
3522
3523         ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS (summary)))->content_info_new_from_header (summary, mp->headers);
3524
3525         return ci;
3526 }
3527
3528 static gchar *
3529 summary_format_address (struct _camel_header_raw *h,
3530                         const gchar *name,
3531                         const gchar *charset)
3532 {
3533         struct _camel_header_address *addr;
3534         gchar *text, *str;
3535
3536         if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3537                 return NULL;
3538
3539         while (isspace ((unsigned) *text))
3540                 text++;
3541
3542         text = camel_header_unfold (text);
3543
3544         if ((addr = camel_header_address_decode (text, charset))) {
3545                 str = camel_header_address_list_format (addr);
3546                 camel_header_address_list_clear (&addr);
3547                 g_free (text);
3548         } else {
3549                 str = text;
3550         }
3551
3552         return str;
3553 }
3554
3555 static gchar *
3556 summary_format_string (struct _camel_header_raw *h,
3557                        const gchar *name,
3558                        const gchar *charset)
3559 {
3560         gchar *text, *str;
3561
3562         if (!(text = (gchar *) camel_header_raw_find (&h, name, NULL)))
3563                 return NULL;
3564
3565         while (isspace ((unsigned) *text))
3566                 text++;
3567
3568         text = camel_header_unfold (text);
3569         str = camel_header_decode_string (text, charset);
3570         g_free (text);
3571
3572         return str;
3573 }
3574
3575 /**
3576  * camel_folder_summary_content_info_new:
3577  * @summary: a #CamelFolderSummary object
3578  *
3579  * Allocate a new #CamelMessageContentInfo, suitable for adding
3580  * to this summary.
3581  *
3582  * Returns: a newly allocated #CamelMessageContentInfo
3583  **/
3584 CamelMessageContentInfo *
3585 camel_folder_summary_content_info_new (CamelFolderSummary *summary)
3586 {
3587         CamelFolderSummaryClass *class;
3588
3589         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3590         g_return_val_if_fail (class->content_info_size > 0, NULL);
3591
3592         return g_slice_alloc0 (class->content_info_size);
3593 }
3594
3595 static CamelMessageInfo *
3596 message_info_new_from_header (CamelFolderSummary *summary,
3597                               struct _camel_header_raw *h)
3598 {
3599         const gchar *received, *date, *content, *charset = NULL;
3600         struct _camel_header_references *refs, *irt, *scan;
3601         gchar *subject, *from, *to, *cc, *mlist;
3602         CamelContentType *ct = NULL;
3603         CamelMessageInfoBase *mi;
3604         guint8 *digest;
3605         gsize length;
3606         gchar *msgid;
3607         gint count;
3608
3609         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
3610         digest = g_alloca (length);
3611
3612         mi = (CamelMessageInfoBase *) camel_message_info_new (summary);
3613
3614         if ((content = camel_header_raw_find (&h, "Content-Type", NULL))
3615              && (ct = camel_content_type_decode (content))
3616              && (charset = camel_content_type_param (ct, "charset"))
3617              && (g_ascii_strcasecmp (charset, "us-ascii") == 0))
3618                 charset = NULL;
3619
3620         charset = charset ? camel_iconv_charset_name (charset) : NULL;
3621
3622         subject = summary_format_string (h, "subject", charset);
3623         from = summary_format_address (h, "from", charset);
3624         to = summary_format_address (h, "to", charset);
3625         cc = summary_format_address (h, "cc", charset);
3626         mlist = camel_header_raw_check_mailing_list (&h);
3627
3628         if (ct)
3629                 camel_content_type_unref (ct);
3630
3631         mi->subject = camel_pstring_add (subject, TRUE);
3632         mi->from = camel_pstring_add (from, TRUE);
3633         mi->to = camel_pstring_add (to, TRUE);
3634         mi->cc = camel_pstring_add (cc, TRUE);
3635         mi->mlist = camel_pstring_add (mlist, TRUE);
3636
3637         mi->user_flags = NULL;
3638         mi->user_tags = NULL;
3639
3640         if ((date = camel_header_raw_find (&h, "date", NULL)))
3641                 mi->date_sent = camel_header_decode_date (date, NULL);
3642         else
3643                 mi->date_sent = 0;
3644
3645         received = camel_header_raw_find (&h, "received", NULL);
3646         if (received)
3647                 received = strrchr (received, ';');
3648         if (received)
3649                 mi->date_received = camel_header_decode_date (received + 1, NULL);
3650         else
3651                 mi->date_received = 0;
3652
3653         msgid = camel_header_msgid_decode (camel_header_raw_find (&h, "message-id", NULL));
3654         if (msgid) {
3655                 GChecksum *checksum;
3656
3657                 checksum = g_checksum_new (G_CHECKSUM_MD5);
3658                 g_checksum_update (checksum, (guchar *) msgid, -1);
3659                 g_checksum_get_digest (checksum, digest, &length);
3660                 g_checksum_free (checksum);
3661
3662                 memcpy (mi->message_id.id.hash, digest, sizeof (mi->message_id.id.hash));
3663                 g_free (msgid);
3664         }
3665
3666         /* decode our references and in-reply-to headers */
3667         refs = camel_header_references_decode (camel_header_raw_find (&h, "references", NULL));
3668         irt = camel_header_references_inreplyto_decode (camel_header_raw_find (&h, "in-reply-to", NULL));
3669         if (refs || irt) {
3670                 if (irt) {
3671                         /* The References field is populated from the "References" and/or "In-Reply-To"
3672                          * headers. If both headers exist, take the first thing in the In-Reply-To header
3673                          * that looks like a Message-ID, and append it to the References header. */
3674
3675                         if (refs)
3676                                 irt->next = refs;
3677
3678                         refs = irt;
3679                 }
3680
3681                 count = camel_header_references_list_size (&refs);
3682                 mi->references = g_malloc (sizeof (*mi->references) + ((count - 1) * sizeof (mi->references->references[0])));
3683                 count = 0;
3684                 scan = refs;
3685                 while (scan) {
3686                         GChecksum *checksum;
3687
3688                         checksum = g_checksum_new (G_CHECKSUM_MD5);
3689                         g_checksum_update (checksum, (guchar *) scan->id, -1);
3690                         g_checksum_get_digest (checksum, digest, &length);
3691                         g_checksum_free (checksum);
3692
3693                         memcpy (mi->references->references[count].id.hash, digest, sizeof (mi->message_id.id.hash));
3694                         count++;
3695                         scan = scan->next;
3696                 }
3697                 mi->references->size = count;
3698                 camel_header_references_list_clear (&refs);
3699         }
3700
3701         return (CamelMessageInfo *) mi;
3702 }
3703
3704 static void
3705 message_info_free (CamelFolderSummary *summary,
3706                    CamelMessageInfo *info)
3707 {
3708         CamelFolderSummaryClass *class;
3709         CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;
3710
3711         if (mi->uid) {
3712                 if (summary) {
3713                         camel_folder_summary_lock (summary);
3714                         if (g_hash_table_lookup (summary->priv->loaded_infos, mi->uid) == mi) {
3715                                 g_hash_table_remove (summary->priv->loaded_infos, mi->uid);
3716                         }
3717                         camel_folder_summary_unlock (summary);
3718                 }
3719                 camel_pstring_free (mi->uid);
3720         }
3721         camel_pstring_free (mi->subject);
3722         camel_pstring_free (mi->from);
3723         camel_pstring_free (mi->to);
3724         camel_pstring_free (mi->cc);
3725         camel_pstring_free (mi->mlist);
3726         g_free (mi->bodystructure);
3727         g_free (mi->references);
3728         g_free (mi->preview);
3729         camel_flag_list_free (&mi->user_flags);
3730         camel_tag_list_free (&mi->user_tags);
3731         if (mi->headers)
3732                 camel_header_param_list_free (mi->headers);
3733
3734         if (summary) {
3735                 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3736                 g_slice_free1 (class->message_info_size, mi);
3737         } else
3738                 g_slice_free (CamelMessageInfoBase, mi);
3739 }
3740
3741 static CamelMessageContentInfo *
3742 content_info_new_from_header (CamelFolderSummary *summary,
3743                               struct _camel_header_raw *h)
3744 {
3745         CamelMessageContentInfo *ci;
3746         const gchar *charset;
3747
3748         ci = camel_folder_summary_content_info_new (summary);
3749
3750         charset = camel_iconv_locale_charset ();
3751         ci->id = camel_header_msgid_decode (camel_header_raw_find (&h, "content-id", NULL));
3752         ci->description = camel_header_decode_string (camel_header_raw_find (&h, "content-description", NULL), charset);
3753         ci->encoding = camel_content_transfer_encoding_decode (camel_header_raw_find (&h, "content-transfer-encoding", NULL));
3754         ci->type = camel_content_type_decode (camel_header_raw_find (&h, "content-type", NULL));
3755
3756         return ci;
3757 }
3758
3759 static void
3760 content_info_free (CamelFolderSummary *summary,
3761                    CamelMessageContentInfo *ci)
3762 {
3763         CamelFolderSummaryClass *class;
3764
3765         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
3766
3767         camel_content_type_unref (ci->type);
3768         g_free (ci->id);
3769         g_free (ci->description);
3770         g_free (ci->encoding);
3771         g_slice_free1 (class->content_info_size, ci);
3772 }
3773
3774 static gchar *
3775 next_uid_string (CamelFolderSummary *summary)
3776 {
3777         return g_strdup_printf ("%u", camel_folder_summary_next_uid (summary));
3778 }
3779
3780 /*
3781   OK
3782   Now this is where all the "smarts" happen, where the content info is built,
3783   and any indexing and what not is performed
3784 */
3785
3786 /* must have filter_lock before calling this function */
3787 static CamelMessageContentInfo *
3788 summary_build_content_info (CamelFolderSummary *summary,
3789                             CamelMessageInfo *msginfo,
3790                             CamelMimeParser *mp)
3791 {
3792         gint state;
3793         gsize len;
3794         gchar *buffer;
3795         CamelMessageContentInfo *info = NULL;
3796         CamelContentType *ct;
3797         gint enc_id = -1, chr_id = -1, html_id = -1, idx_id = -1;
3798         CamelFolderSummaryPrivate *p = summary->priv;
3799         CamelMimeFilter *mfc;
3800         CamelMessageContentInfo *part;
3801         const gchar *calendar_header;
3802
3803         d (printf ("building content info\n"));
3804
3805         /* start of this part */
3806         state = camel_mime_parser_step (mp, &buffer, &len);
3807
3808         if (summary->priv->build_content)
3809                 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_parser (summary, mp);
3810
3811         switch (state) {
3812         case CAMEL_MIME_PARSER_STATE_HEADER:
3813                 /* check content type for indexing, then read body */
3814                 ct = camel_mime_parser_content_type (mp);
3815                 /* update attachments flag as we go */
3816                 if (camel_content_type_is (ct, "application", "pgp-signature")
3817 #ifdef ENABLE_SMIME
3818                     || camel_content_type_is (ct, "application", "x-pkcs7-signature")
3819                     || camel_content_type_is (ct, "application", "pkcs7-signature")
3820 #endif
3821                         )
3822                         camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3823
3824                 calendar_header = camel_mime_parser_header (mp, "Content-class", NULL);
3825                 if (calendar_header && g_ascii_strcasecmp (calendar_header, "urn:content-classes:calendarmessage") != 0)
3826                         calendar_header = NULL;
3827
3828                 if (!calendar_header)
3829                         calendar_header = camel_mime_parser_header (mp, "X-Calendar-Attachment", NULL);
3830
3831                 if (calendar_header || camel_content_type_is (ct, "text", "calendar"))
3832                         camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
3833
3834                 if (p->index && camel_content_type_is (ct, "text", "*")) {
3835                         gchar *encoding;
3836                         const gchar *charset;
3837
3838                         d (printf ("generating index:\n"));
3839
3840                         encoding = camel_content_transfer_encoding_decode (camel_mime_parser_header (mp, "content-transfer-encoding", NULL));
3841                         if (encoding) {
3842                                 if (!g_ascii_strcasecmp (encoding, "base64")) {
3843                                         d (printf (" decoding base64\n"));
3844                                         if (p->filter_64 == NULL)
3845                                                 p->filter_64 = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
3846                                         else
3847                                                 camel_mime_filter_reset (p->filter_64);
3848                                         enc_id = camel_mime_parser_filter_add (mp, p->filter_64);
3849                                 } else if (!g_ascii_strcasecmp (encoding, "quoted-printable")) {
3850                                         d (printf (" decoding quoted-printable\n"));
3851                                         if (p->filter_qp == NULL)
3852                                                 p->filter_qp = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_DEC);
3853                                         else
3854                                                 camel_mime_filter_reset (p->filter_qp);
3855                                         enc_id = camel_mime_parser_filter_add (mp, p->filter_qp);
3856                                 } else if (!g_ascii_strcasecmp (encoding, "x-uuencode")) {
3857                                         d (printf (" decoding x-uuencode\n"));
3858                                         if (p->filter_uu == NULL)
3859                                                 p->filter_uu = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_UU_DEC);
3860                                         else
3861                                                 camel_mime_filter_reset (p->filter_uu);
3862                                         enc_id = camel_mime_parser_filter_add (mp, p->filter_uu);
3863                                 } else {
3864                                         d (printf (" ignoring encoding %s\n", encoding));
3865                                 }
3866                                 g_free (encoding);
3867                         }
3868
3869                         charset = camel_content_type_param (ct, "charset");
3870                         if (charset != NULL
3871                             && !(g_ascii_strcasecmp (charset, "us-ascii") == 0
3872                                  || g_ascii_strcasecmp (charset, "utf-8") == 0)) {
3873                                 d (printf (" Adding conversion filter from %s to UTF-8\n", charset));
3874                                 mfc = g_hash_table_lookup (p->filter_charset, charset);
3875                                 if (mfc == NULL) {
3876                                         mfc = camel_mime_filter_charset_new (charset, "UTF-8");
3877                                         if (mfc)
3878                                                 g_hash_table_insert (p->filter_charset, g_strdup (charset), mfc);
3879                                 } else {
3880                                         camel_mime_filter_reset ((CamelMimeFilter *) mfc);
3881                                 }
3882                                 if (mfc) {
3883                                         chr_id = camel_mime_parser_filter_add (mp, mfc);
3884                                 } else {
3885                                         w (g_warning ("Cannot convert '%s' to 'UTF-8', message index may be corrupt", charset));
3886                                 }
3887                         }
3888
3889                         /* we do charset conversions before this filter, which isn't strictly correct,
3890                          * but works in most cases */
3891                         if (camel_content_type_is (ct, "text", "html")) {
3892                                 if (p->filter_html == NULL)
3893                                         p->filter_html = camel_mime_filter_html_new ();
3894                                 else
3895                                         camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
3896                                 html_id = camel_mime_parser_filter_add (mp, (CamelMimeFilter *) p->filter_html);
3897                         }
3898
3899                         /* and this filter actually does the indexing */
3900                         idx_id = camel_mime_parser_filter_add (mp, p->filter_index);
3901                 }
3902                 /* and scan/index everything */
3903                 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
3904                         ;
3905                 /* and remove the filters */
3906                 camel_mime_parser_filter_remove (mp, enc_id);
3907                 camel_mime_parser_filter_remove (mp, chr_id);
3908                 camel_mime_parser_filter_remove (mp, html_id);
3909                 camel_mime_parser_filter_remove (mp, idx_id);
3910                 break;
3911         case CAMEL_MIME_PARSER_STATE_MULTIPART:
3912                 d (printf ("Summarising multipart\n"));
3913                 /* update attachments flag as we go */
3914                 ct = camel_mime_parser_content_type (mp);
3915                 if (camel_content_type_is (ct, "multipart", "mixed"))
3916                         camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3917                 if (camel_content_type_is (ct, "multipart", "signed")
3918                     || camel_content_type_is (ct, "multipart", "encrypted"))
3919                         camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3920
3921                 while (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
3922                         camel_mime_parser_unstep (mp);
3923                         part = summary_build_content_info (summary, msginfo, mp);
3924                         if (part) {
3925                                 part->parent = info;
3926                                 my_list_append ((struct _node **) &info->childs, (struct _node *) part);
3927                         }
3928                 }
3929                 break;
3930         case CAMEL_MIME_PARSER_STATE_MESSAGE:
3931                 d (printf ("Summarising message\n"));
3932                 /* update attachments flag as we go */
3933                 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3934
3935                 part = summary_build_content_info (summary, msginfo, mp);
3936                 if (part) {
3937                         part->parent = info;
3938                         my_list_append ((struct _node **) &info->childs, (struct _node *) part);
3939                 }
3940                 state = camel_mime_parser_step (mp, &buffer, &len);
3941                 if (state != CAMEL_MIME_PARSER_STATE_MESSAGE_END) {
3942                         g_error ("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state);
3943                         camel_mime_parser_unstep (mp);
3944                 }
3945                 break;
3946         }
3947
3948         d (printf ("finished building content info\n"));
3949
3950         return info;
3951 }
3952
3953 /* build the content-info, from a message */
3954 /* this needs the filter lock since it uses filters to perform indexing */
3955 static CamelMessageContentInfo *
3956 summary_build_content_info_message (CamelFolderSummary *summary,
3957                                     CamelMessageInfo *msginfo,
3958                                     CamelMimePart *object)
3959 {
3960         CamelDataWrapper *containee;
3961         gint parts, i;
3962         CamelFolderSummaryPrivate *p = summary->priv;
3963         CamelMessageContentInfo *info = NULL, *child;
3964         CamelContentType *ct;
3965         const struct _camel_header_raw *header;
3966
3967         if (summary->priv->build_content)
3968                 info = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->content_info_new_from_message (summary, object);
3969
3970         containee = camel_medium_get_content (CAMEL_MEDIUM (object));
3971
3972         if (containee == NULL)
3973                 return info;
3974
3975         /* TODO: I find it odd that get_part and get_content do not
3976          * add a reference, probably need fixing for multithreading */
3977
3978         /* check for attachments */
3979         ct = ((CamelDataWrapper *) containee)->mime_type;
3980         if (camel_content_type_is (ct, "multipart", "*")) {
3981                 if (camel_content_type_is (ct, "multipart", "mixed"))
3982                         camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
3983                 if (camel_content_type_is (ct, "multipart", "signed")
3984                     || camel_content_type_is (ct, "multipart", "encrypted"))
3985                         camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3986         } else if (camel_content_type_is (ct, "application", "pgp-signature")
3987 #ifdef ENABLE_SMIME
3988                     || camel_content_type_is (ct, "application", "x-pkcs7-signature")
3989                     || camel_content_type_is (ct, "application", "pkcs7-signature")
3990 #endif
3991                 ) {
3992                 camel_message_info_set_flags (msginfo, CAMEL_MESSAGE_SECURE, CAMEL_MESSAGE_SECURE);
3993         }
3994
3995         for (header = object->headers; header; header = header->next) {
3996                 const gchar *value = header->value;
3997
3998                 /* skip preceding spaces in the value */
3999                 while (value && *value && isspace (*value))
4000                         value++;
4001
4002                 if (header->name && value && (
4003                     (g_ascii_strcasecmp (header->name, "Content-class") == 0 && g_ascii_strcasecmp (value, "urn:content-classes:calendarmessage") == 0) ||
4004                     (g_ascii_strcasecmp (header->name, "X-Calendar-Attachment") == 0)))
4005                         break;
4006         }
4007
4008         if (header || camel_content_type_is (ct, "text", "calendar"))
4009                 camel_message_info_set_user_flag (msginfo, "$has_cal", TRUE);
4010
4011         /* using the object types is more accurate than using the mime/types */
4012         if (CAMEL_IS_MULTIPART (containee)) {
4013                 parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
4014
4015                 for (i = 0; i < parts; i++) {
4016                         CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
4017                         g_assert (part);
4018                         child = summary_build_content_info_message (summary, msginfo, part);
4019                         if (child) {
4020                                 child->parent = info;
4021                                 my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4022                         }
4023                 }
4024         } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
4025                 /* for messages we only look at its contents */
4026                 child = summary_build_content_info_message (summary, msginfo, (CamelMimePart *) containee);
4027                 if (child) {
4028                         child->parent = info;
4029                         my_list_append ((struct _node **) &info->childs, (struct _node *) child);
4030                 }
4031         } else if (p->filter_stream
4032                    && camel_content_type_is (ct, "text", "*")) {
4033                 gint html_id = -1, idx_id = -1;
4034
4035                 /* pre-attach html filter if required, otherwise just index filter */
4036                 if (camel_content_type_is (ct, "text", "html")) {
4037                         if (p->filter_html == NULL)
4038                                 p->filter_html = camel_mime_filter_html_new ();
4039                         else
4040                                 camel_mime_filter_reset ((CamelMimeFilter *) p->filter_html);
4041                         html_id = camel_stream_filter_add (
4042                                 CAMEL_STREAM_FILTER (p->filter_stream),
4043                                 (CamelMimeFilter *) p->filter_html);
4044                 }
4045                 idx_id = camel_stream_filter_add (
4046                         CAMEL_STREAM_FILTER (p->filter_stream),
4047                         p->filter_index);
4048
4049                 /* FIXME Pass a GCancellable and GError here. */
4050                 camel_data_wrapper_decode_to_stream_sync (
4051                         containee, p->filter_stream, NULL, NULL);
4052                 camel_stream_flush (p->filter_stream, NULL, NULL);
4053
4054                 camel_stream_filter_remove (
4055                         CAMEL_STREAM_FILTER (p->filter_stream), idx_id);
4056                 camel_stream_filter_remove (
4057                         CAMEL_STREAM_FILTER (p->filter_stream), html_id);
4058         }
4059
4060         return info;
4061 }
4062
4063 /**
4064  * camel_flag_get:
4065  * @list: the address of a #CamelFlag list
4066  * @name: name of the flag to get
4067  *
4068  * Find the state of the flag @name in @list.
4069  *
4070  * Returns: the state of the flag (%TRUE or %FALSE)
4071  **/
4072 gboolean
4073 camel_flag_get (CamelFlag **list,
4074                 const gchar *name)
4075 {
4076         CamelFlag *flag;
4077         flag = *list;
4078         while (flag) {
4079                 if (!strcmp (flag->name, name))
4080                         return TRUE;
4081                 flag = flag->next;
4082         }
4083         return FALSE;
4084 }
4085
4086 /**
4087  * camel_flag_set:
4088  * @list: the address of a #CamelFlag list
4089  * @name: name of the flag to set or change
4090  * @value: the value to set on the flag
4091  *
4092  * Set the state of a flag @name in the list @list to @value.
4093  *
4094  * Returns: %TRUE if the value of the flag has been changed or %FALSE
4095  * otherwise
4096  **/
4097 gboolean
4098 camel_flag_set (CamelFlag **list,
4099                 const gchar *name,
4100                 gboolean value)
4101 {
4102         CamelFlag *flag, *tmp;
4103         gsize tmp_len = 0;
4104
4105         if (!name)
4106                 return TRUE;
4107
4108         /* this 'trick' works because flag->next is the first element */
4109         flag = (CamelFlag *) list;
4110         while (flag->next) {
4111                 tmp = flag->next;
4112                 if (!strcmp (flag->next->name, name)) {
4113                         if (!value) {
4114                                 flag->next = tmp->next;
4115                                 g_free (tmp);
4116                         }
4117                         return !value;
4118                 }
4119                 flag = tmp;
4120         }
4121
4122         if (value) {
4123                 tmp_len = sizeof (*tmp) + strlen (name);
4124                 tmp = g_malloc (tmp_len);
4125                 g_strlcpy (tmp->name, name, strlen (name) + 1);
4126                 tmp->next = NULL;
4127                 flag->next = tmp;
4128         }
4129         return value;
4130 }
4131
4132 /**
4133  * camel_flag_list_size:
4134  * @list: the address of a #CamelFlag list
4135  *
4136  * Get the length of the flag list.
4137  *
4138  * Returns: the number of flags in the list
4139  **/
4140 gint
4141 camel_flag_list_size (CamelFlag **list)
4142 {
4143         gint count = 0;
4144         CamelFlag *flag;
4145
4146         flag = *list;
4147         while (flag) {
4148                 count++;
4149                 flag = flag->next;
4150         }
4151         return count;
4152 }
4153
4154 /**
4155  * camel_flag_list_free:
4156  * @list: the address of a #CamelFlag list
4157  *
4158  * Free the memory associated with the flag list @list.
4159  **/
4160 void
4161 camel_flag_list_free (CamelFlag **list)
4162 {
4163         CamelFlag *flag, *tmp;
4164         flag = *list;
4165         while (flag) {
4166                 tmp = flag->next;
4167                 g_free (flag);
4168                 flag = tmp;
4169         }
4170         *list = NULL;
4171 }
4172
4173 /**
4174  * camel_flag_list_copy:
4175  * @to: the address of the #CamelFlag list to copy to
4176  * @from: the address of the #CamelFlag list to copy from
4177  *
4178  * Copy a flag list.
4179  *
4180  * Returns: %TRUE if @to is changed or %FALSE otherwise
4181  **/
4182 gboolean
4183 camel_flag_list_copy (CamelFlag **to,
4184                       CamelFlag **from)
4185 {
4186         CamelFlag *flag, *tmp;
4187         gboolean changed = FALSE;
4188
4189         if (*to == NULL && from == NULL)
4190                 return FALSE;
4191
4192         /* Remove any now-missing flags */
4193         flag = (CamelFlag *) to;
4194         while (flag->next) {
4195                 tmp = flag->next;
4196                 if (!camel_flag_get (from, tmp->name)) {
4197                         if (*tmp->name)
4198                                 changed = TRUE;
4199                         flag->next = tmp->next;
4200                         g_free (tmp);
4201                 } else {
4202                         flag = tmp;
4203                 }
4204         }
4205
4206         /* Add any new non-empty flags */
4207         flag = *from;
4208         while (flag) {
4209                 if (*flag->name)
4210                         changed |= camel_flag_set (to, flag->name, TRUE);
4211                 flag = flag->next;
4212         }
4213
4214         return changed;
4215 }
4216
4217 /**
4218  * camel_tag_get:
4219  * @list: the address of a #CamelTag list
4220  * @name: name of the tag to get
4221  *
4222  * Find the flag @name in @list and get the value.
4223  *
4224  * Returns: the value of the flag  or %NULL if unset
4225  **/
4226 const gchar *
4227 camel_tag_get (CamelTag **list,
4228                const gchar *name)
4229 {
4230         CamelTag *tag;
4231
4232         tag = *list;
4233         while (tag) {
4234                 if (!strcmp (tag->name, name))
4235                         return (const gchar *) tag->value;
4236                 tag = tag->next;
4237         }
4238         return NULL;
4239 }
4240
4241 /**
4242  * camel_tag_set:
4243  * @list: the address of a #CamelTag list
4244  * @name: name of the tag to set
4245  * @value: value to set on the tag
4246  *
4247  * Set the tag @name in the tag list @list to @value.
4248  *
4249  * Returns: %TRUE if the value on the tag changed or %FALSE otherwise
4250  **/
4251 gboolean
4252 camel_tag_set (CamelTag **list,
4253                const gchar *name,
4254                const gchar *value)
4255 {
4256         CamelTag *tag, *tmp;
4257
4258         /* this 'trick' works because tag->next is the first element */
4259         tag = (CamelTag *) list;
4260         while (tag->next) {
4261                 tmp = tag->next;
4262                 if (!strcmp (tmp->name, name)) {
4263                         if (value == NULL) { /* clear it? */
4264                                 tag->next = tmp->next;
4265                                 g_free (tmp->value);
4266                                 g_free (tmp);
4267                                 return TRUE;
4268                         } else if (strcmp (tmp->value, value)) { /* has it changed? */
4269                                 g_free (tmp->value);
4270                                 tmp->value = g_strdup (value);
4271                                 return TRUE;
4272                         }
4273                         return FALSE;
4274                 }
4275                 tag = tmp;
4276         }
4277
4278         if (value) {
4279                 tmp = g_malloc (sizeof (*tmp) + strlen (name));
4280                 g_strlcpy (tmp->name, name, strlen (name) + 1);
4281                 tmp->value = g_strdup (value);
4282                 tmp->next = NULL;
4283                 tag->next = tmp;
4284                 return TRUE;
4285         }
4286         return FALSE;
4287 }
4288
4289 /**
4290  * camel_tag_list_size:
4291  * @list: the address of a #CamelTag list
4292  *
4293  * Get the number of tags present in the tag list @list.
4294  *
4295  * Returns: the number of tags
4296  **/
4297 gint
4298 camel_tag_list_size (CamelTag **list)
4299 {
4300         gint count = 0;
4301         CamelTag *tag;
4302
4303         tag = *list;
4304         while (tag) {
4305                 count++;
4306                 tag = tag->next;
4307         }
4308         return count;
4309 }
4310
4311 static void
4312 rem_tag (gchar *key,
4313          gchar *value,
4314          CamelTag **to)
4315 {
4316         camel_tag_set (to, key, NULL);
4317 }
4318
4319 /**
4320  * camel_tag_list_copy:
4321  * @to: the address of the #CamelTag list to copy to
4322  * @from: the address of the #CamelTag list to copy from
4323  *
4324  * Copy a tag list.
4325  *
4326  * Returns: %TRUE if @to is changed or %FALSE otherwise
4327  **/
4328 gboolean
4329 camel_tag_list_copy (CamelTag **to,
4330                      CamelTag **from)
4331 {
4332         gint changed = FALSE;
4333         CamelTag *tag;
4334         GHashTable *left;
4335
4336         if (*to == NULL && from == NULL)
4337                 return FALSE;
4338
4339         left = g_hash_table_new (g_str_hash, g_str_equal);
4340         tag = *to;
4341         while (tag) {
4342                 g_hash_table_insert (left, tag->name, tag);
4343                 tag = tag->next;
4344         }
4345
4346         tag = *from;
4347         while (tag) {
4348                 changed |= camel_tag_set (to, tag->name, tag->value);
4349                 g_hash_table_remove (left, tag->name);
4350                 tag = tag->next;
4351         }
4352
4353         if (g_hash_table_size (left) > 0) {
4354                 g_hash_table_foreach (left, (GHFunc) rem_tag, to);
4355                 changed = TRUE;
4356         }
4357         g_hash_table_destroy (left);
4358
4359         return changed;
4360 }
4361
4362 /**
4363  * camel_tag_list_free:
4364  * @list: the address of a #CamelTag list
4365  *
4366  * Free the tag list @list.
4367  **/
4368 void
4369 camel_tag_list_free (CamelTag **list)
4370 {
4371         CamelTag *tag, *tmp;
4372         tag = *list;
4373         while (tag) {
4374                 tmp = tag->next;
4375                 g_free (tag->value);
4376                 g_free (tag);
4377                 tag = tmp;
4378         }
4379         *list = NULL;
4380 }
4381
4382 static struct flag_names_t {
4383         const gchar *name;
4384         guint32 value;
4385 } flag_names[] = {
4386         { "answered", CAMEL_MESSAGE_ANSWERED },
4387         { "deleted", CAMEL_MESSAGE_DELETED },
4388         { "draft", CAMEL_MESSAGE_DRAFT },
4389         { "flagged", CAMEL_MESSAGE_FLAGGED },
4390         { "seen", CAMEL_MESSAGE_SEEN },
4391         { "attachments", CAMEL_MESSAGE_ATTACHMENTS },
4392         { "junk", CAMEL_MESSAGE_JUNK },
4393         { "notjunk", CAMEL_MESSAGE_NOTJUNK },
4394         { "secure", CAMEL_MESSAGE_SECURE },
4395         { NULL, 0 }
4396 };
4397
4398 /**
4399  * camel_system_flag:
4400  * @name: name of a system flag
4401  *
4402  * Returns: the integer value of the system flag string
4403  **/
4404 CamelMessageFlags
4405 camel_system_flag (const gchar *name)
4406 {
4407         struct flag_names_t *flag;
4408
4409         g_return_val_if_fail (name != NULL, 0);
4410
4411         for (flag = flag_names; flag->name; flag++)
4412                 if (!g_ascii_strcasecmp (name, flag->name))
4413                         return flag->value;
4414
4415         return 0;
4416 }
4417
4418 /**
4419  * camel_system_flag_get:
4420  * @flags: bitwise system flags
4421  * @name: name of the flag to check for
4422  *
4423  * Find the state of the flag @name in @flags.
4424  *
4425  * Returns: %TRUE if the named flag is set or %FALSE otherwise
4426  **/
4427 gboolean
4428 camel_system_flag_get (CamelMessageFlags flags,
4429                        const gchar *name)
4430 {
4431         g_return_val_if_fail (name != NULL, FALSE);
4432
4433         return flags & camel_system_flag (name);
4434 }
4435
4436 /**
4437  * camel_message_info_new:
4438  * @summary: a #CamelFolderSummary object or %NULL
4439  *
4440  * Create a new #CamelMessageInfo.
4441  *
4442  * Returns: a new #CamelMessageInfo
4443  **/
4444 gpointer
4445 camel_message_info_new (CamelFolderSummary *summary)
4446 {
4447         CamelFolderSummaryClass *class;
4448         CamelMessageInfo *info;
4449         gsize message_info_size;
4450
4451         if (summary != NULL) {
4452                 class = CAMEL_FOLDER_SUMMARY_GET_CLASS (summary);
4453                 g_return_val_if_fail (class->message_info_size > 0, NULL);
4454                 message_info_size = class->message_info_size;
4455         } else {
4456                 message_info_size = sizeof (CamelMessageInfoBase);
4457         }
4458
4459         info = g_slice_alloc0 (message_info_size);
4460         info->refcount = 1;
4461         info->summary = summary;
4462
4463         /* We assume that mi is always dirty unless freshly read or just saved*/
4464         ((CamelMessageInfoBase *) info)->dirty = TRUE;
4465
4466         return info;
4467 }
4468
4469 /**
4470  * camel_message_info_ref:
4471  * @info: a #CamelMessageInfo
4472  *
4473  * Reference an info.
4474  **/
4475 gpointer
4476 camel_message_info_ref (gpointer o)
4477 {
4478         CamelMessageInfo *mi = o;
4479
4480         g_return_val_if_fail (mi != NULL, NULL);
4481         g_return_val_if_fail (mi->refcount > 0, NULL);
4482
4483         g_atomic_int_inc (&mi->refcount);
4484
4485         return o;
4486 }
4487
4488 /**
4489  * camel_message_info_new_from_header:
4490  * @summary: a #CamelFolderSummary object or %NULL
4491  * @header: raw header
4492  *
4493  * Create a new #CamelMessageInfo pre-populated with info from
4494  * @header.
4495  *
4496  * Returns: a new #CamelMessageInfo
4497  **/
4498 CamelMessageInfo *
4499 camel_message_info_new_from_header (CamelFolderSummary *summary,
4500                                     struct _camel_header_raw *header)
4501 {
4502         if (summary != NULL)
4503                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (summary)->
4504                         message_info_new_from_header (summary, header);
4505         else
4506                 return message_info_new_from_header (NULL, header);
4507 }
4508
4509 /**
4510  * camel_message_info_unref:
4511  * @info: a #CamelMessageInfo
4512  *
4513  * Unref's and potentially frees a #CamelMessageInfo and its contents.
4514  **/
4515 void
4516 camel_message_info_unref (gpointer o)
4517 {
4518         CamelMessageInfo *mi = o;
4519
4520         g_return_if_fail (mi != NULL);
4521         g_return_if_fail (mi->refcount > 0);
4522
4523         if (g_atomic_int_dec_and_test (&mi->refcount)) {
4524                 if (mi->summary != NULL) {
4525                         CamelFolderSummaryClass *class;
4526
4527                         /* FIXME This is kinda busted, should really
4528                          *       be handled by message_info_free(). */
4529                         if (mi->summary->priv->build_content
4530                             && ((CamelMessageInfoBase *) mi)->content) {
4531                                 camel_folder_summary_content_info_free (
4532                                         mi->summary,
4533                                         ((CamelMessageInfoBase *) mi)->content);
4534                                 ((CamelMessageInfoBase *) mi)->content = NULL;
4535                         }
4536
4537                         class = CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary);
4538                         g_return_if_fail (class->message_info_free != NULL);
4539
4540                         class->message_info_free (mi->summary, mi);
4541                 } else {
4542                         message_info_free (NULL, mi);
4543                 }
4544         }
4545 }
4546
4547 /**
4548  * camel_message_info_clone:
4549  * @info: a #CamelMessageInfo
4550  *
4551  * Duplicate a #CamelMessageInfo.
4552  *
4553  * Returns: the duplicated #CamelMessageInfo
4554  **/
4555 gpointer
4556 camel_message_info_clone (gconstpointer o)
4557 {
4558         const CamelMessageInfo *mi = o;
4559
4560         if (mi->summary)
4561                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (mi->summary)->message_info_clone (mi->summary, mi);
4562         else
4563                 return message_info_clone (NULL, mi);
4564 }
4565
4566 /**
4567  * camel_message_info_ptr:
4568  * @info: a #CamelMessageInfo
4569  * @id: info to get
4570  *
4571  * Generic accessor method for getting pointer data.
4572  *
4573  * Returns: the pointer data
4574  **/
4575 gconstpointer
4576 camel_message_info_ptr (const CamelMessageInfo *info,
4577                         gint id)
4578 {
4579         if (info->summary)
4580                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_ptr (info, id);
4581         else
4582                 return info_ptr (info, id);
4583 }
4584
4585 /**
4586  * camel_message_info_uint32:
4587  * @info: a #CamelMessageInfo
4588  * @id: info to get
4589  *
4590  * Generic accessor method for getting 32bit gint data.
4591  *
4592  * Returns: the gint data
4593  **/
4594 guint32
4595 camel_message_info_uint32 (const CamelMessageInfo *info,
4596                            gint id)
4597 {
4598         if (info->summary)
4599                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_uint32 (info, id);
4600         else
4601                 return info_uint32 (info, id);
4602 }
4603
4604 /**
4605  * camel_message_info_time:
4606  * @info: a #CamelMessageInfo
4607  * @id: info to get
4608  *
4609  * Generic accessor method for getting time_t data.
4610  *
4611  * Returns: the time_t data
4612  **/
4613 time_t
4614 camel_message_info_time (const CamelMessageInfo *info,
4615                          gint id)
4616 {
4617         if (info->summary)
4618                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_time (info, id);
4619         else
4620                 return info_time (info, id);
4621 }
4622
4623 /**
4624  * camel_message_info_user_flag:
4625  * @info: a #CamelMessageInfo
4626  * @id: user flag to get
4627  *
4628  * Get the state of a user flag named @id.
4629  *
4630  * Returns: the state of the user flag
4631  **/
4632 gboolean
4633 camel_message_info_user_flag (const CamelMessageInfo *info,
4634                               const gchar *id)
4635 {
4636         if (info->summary)
4637                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_user_flag (info, id);
4638         else
4639                 return info_user_flag (info, id);
4640 }
4641
4642 /**
4643  * camel_message_info_user_tag:
4644  * @info: a #CamelMessageInfo
4645  * @id: user tag to get
4646  *
4647  * Get the value of a user tag named @id.
4648  *
4649  * Returns: the value of the user tag
4650  **/
4651 const gchar *
4652 camel_message_info_user_tag (const CamelMessageInfo *info,
4653                              const gchar *id)
4654 {
4655         if (info->summary)
4656                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_user_tag (info, id);
4657         else
4658                 return info_user_tag (info, id);
4659 }
4660
4661 /**
4662  * camel_message_info_set_flags:
4663  * @info: a #CamelMessageInfo
4664  * @flags: mask of flags to change
4665  * @set: state the flags should be changed to
4666  *
4667  * Change the state of the system flags on the #CamelMessageInfo
4668  *
4669  * Returns: %TRUE if any of the flags changed or %FALSE otherwise
4670  **/
4671 gboolean
4672 camel_message_info_set_flags (CamelMessageInfo *info,
4673                               CamelMessageFlags flags,
4674                               guint32 set)
4675 {
4676         if (info->summary)
4677                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_flags (info, flags, set);
4678         else
4679                 return info_set_flags (info, flags, set);
4680 }
4681
4682 /**
4683  * camel_message_info_set_user_flag:
4684  * @info: a #CamelMessageInfo
4685  * @id: name of the user flag to set
4686  * @state: state to set the flag to
4687  *
4688  * Set the state of a user flag on a #CamelMessageInfo.
4689  *
4690  * Returns: %TRUE if the state changed or %FALSE otherwise
4691  **/
4692 gboolean
4693 camel_message_info_set_user_flag (CamelMessageInfo *info,
4694                                   const gchar *id,
4695                                   gboolean state)
4696 {
4697         if (info->summary)
4698                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_user_flag (info, id, state);
4699         else
4700                 return info_set_user_flag (info, id, state);
4701 }
4702
4703 /**
4704  * camel_message_info_set_user_tag:
4705  * @info: a #CamelMessageInfo
4706  * @id: name of the user tag to set
4707  * @val: value to set
4708  *
4709  * Set the value of a user tag on a #CamelMessageInfo.
4710  *
4711  * Returns: %TRUE if the value changed or %FALSE otherwise
4712  **/
4713 gboolean
4714 camel_message_info_set_user_tag (CamelMessageInfo *info,
4715                                  const gchar *id,
4716                                  const gchar *val)
4717 {
4718         if (info->summary)
4719                 return CAMEL_FOLDER_SUMMARY_GET_CLASS (info->summary)->info_set_user_tag (info, id, val);
4720         else
4721                 return info_set_user_tag (info, id, val);
4722 }
4723
4724 void
4725 camel_content_info_dump (CamelMessageContentInfo *ci,
4726                          gint depth)
4727 {
4728         gchar *p;
4729
4730         p = alloca (depth * 4 + 1);
4731         memset (p, ' ', depth * 4);
4732         p[depth * 4] = 0;
4733
4734         if (ci == NULL) {
4735                 printf ("%s<empty>\n", p);
4736                 return;
4737         }
4738
4739         if (ci->type)
4740                 printf (
4741                         "%scontent-type: %s/%s\n",
4742                         p, ci->type->type ? ci->type->type : "(null)",
4743                         ci->type->subtype ? ci->type->subtype : "(null)");
4744         else
4745                 printf ("%scontent-type: <unset>\n", p);
4746         printf (
4747                 "%scontent-transfer-encoding: %s\n",
4748                 p, ci->encoding ? ci->encoding : "(null)");
4749         printf (
4750                 "%scontent-description: %s\n",
4751                 p, ci->description ? ci->description : "(null)");
4752         printf ("%ssize: %lu\n", p, (gulong) ci->size);
4753         ci = ci->childs;
4754         while (ci) {
4755                 camel_content_info_dump (ci, depth + 1);
4756                 ci = ci->next;
4757         }
4758 }
4759
4760 void
4761 camel_message_info_dump (CamelMessageInfo *info)
4762 {
4763         if (info == NULL) {
4764                 printf ("No message?\n");
4765                 return;
4766         }
4767
4768         printf ("Subject: %s\n", camel_message_info_subject (info));
4769         printf ("To: %s\n", camel_message_info_to (info));
4770         printf ("Cc: %s\n", camel_message_info_cc (info));
4771         printf ("mailing list: %s\n", camel_message_info_mlist (info));
4772         printf ("From: %s\n", camel_message_info_from (info));
4773         printf ("UID: %s\n", camel_message_info_uid (info));
4774         printf ("Flags: %04x\n", camel_message_info_flags (info));
4775         camel_content_info_dump (((CamelMessageInfoBase *) info)->content, 0);
4776 }
4777
4778 /**
4779  * camel_folder_summary_lock:
4780  * @summary: a #CamelFolderSummary
4781  *
4782  * Locks @summary. Unlock it with camel_folder_summary_unlock().
4783  *
4784  * Since: 2.32
4785  **/
4786 void
4787 camel_folder_summary_lock (CamelFolderSummary *summary)
4788 {
4789         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4790
4791         g_rec_mutex_lock (&summary->priv->summary_lock);
4792 }
4793
4794 /**
4795  * camel_folder_summary_unlock:
4796  * @summary: a #CamelFolderSummary
4797  *
4798  * Unlocks @summary, previously locked with camel_folder_summary_lock().
4799  *
4800  * Since: 2.32
4801  **/
4802 void
4803 camel_folder_summary_unlock (CamelFolderSummary *summary)
4804 {
4805         g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
4806
4807         g_rec_mutex_unlock (&summary->priv->summary_lock);
4808 }
4809
4810 gint
4811 bdata_extract_digit (/* const */ gchar **part)
4812 {
4813         if (!part || !*part || !**part)
4814                 return 0;
4815
4816         if (**part == ' ')
4817                 *part += 1;
4818
4819         if (!**part)
4820                 return 0;
4821
4822         return strtoul (*part, part, 10);
4823 }
4824
4825 /* expecting "digit-value", where digit is length of the value */
4826 gchar *
4827 bdata_extract_string (/* const */ gchar **part)
4828 {
4829         gint len, has_len;
4830         gchar *val;
4831
4832         len = bdata_extract_digit (part);
4833
4834         /* might be a '-' sign */
4835         if (part && *part && **part)
4836                 *part += 1;
4837
4838         if (len <= 0 || !part || !*part || !**part)
4839                 return g_strdup ("");
4840
4841         if (!**part)
4842                 return g_strdup ("");
4843
4844         has_len = strlen (*part);
4845         if (has_len < len)
4846                 len = has_len;
4847
4848         val = g_strndup (*part, len);
4849         *part += len;
4850
4851         return val;
4852 }