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