Split camel-imapx library and merge into camel so that providers can be written on...
[platform/upstream/evolution-data-server.git] / camel / camel-imapx-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:
6  *    Michael Zucchi <notzed@ximian.com>
7  *    Dan Winship <danw@ximian.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33
34
35 #include <camel/camel-db.h>
36 #include <camel/camel-store.h>
37 #include <camel/camel-string-utils.h>
38
39 #include "camel-imapx-summary.h"
40
41 #define CAMEL_IMAPX_SUMMARY_VERSION (4)
42
43 static gboolean info_set_user_flag (CamelMessageInfo *info, const gchar *id, gboolean state);
44
45 static gboolean summary_header_from_db (CamelFolderSummary *s, CamelFIRecord *mir);
46 static CamelFIRecord * summary_header_to_db (CamelFolderSummary *s, GError **error);
47 static CamelMIRecord * message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info);
48 static CamelMessageInfo * message_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir);
49 static gboolean content_info_to_db (CamelFolderSummary *s, CamelMessageContentInfo *info, CamelMIRecord *mir);
50 static CamelMessageContentInfo * content_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir);
51
52 G_DEFINE_TYPE (CamelIMAPXSummary, camel_imapx_summary, CAMEL_TYPE_FOLDER_SUMMARY)
53
54 static CamelMessageInfo *
55 imapx_message_info_clone (CamelFolderSummary *s,
56                           const CamelMessageInfo *mi)
57 {
58         CamelIMAPXMessageInfo *to;
59         CamelFolderSummaryClass *folder_summary_class;
60         const CamelIMAPXMessageInfo *from = (const CamelIMAPXMessageInfo *) mi;
61
62         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
63                 camel_imapx_summary_parent_class);
64
65         to = (CamelIMAPXMessageInfo *)
66                 folder_summary_class->message_info_clone (s, mi);
67         to->server_flags = from->server_flags;
68
69         /* FIXME: parent clone should do this */
70         to->info.content = camel_folder_summary_content_info_new (s);
71
72         return (CamelMessageInfo *) to;
73 }
74
75 static void
76 camel_imapx_summary_class_init (CamelIMAPXSummaryClass *class)
77 {
78         CamelFolderSummaryClass *folder_summary_class;
79
80         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (class);
81         folder_summary_class->message_info_size = sizeof (CamelIMAPXMessageInfo);
82         folder_summary_class->content_info_size = sizeof (CamelIMAPXMessageContentInfo);
83         folder_summary_class->message_info_clone = imapx_message_info_clone;
84         folder_summary_class->summary_header_to_db = summary_header_to_db;
85         folder_summary_class->summary_header_from_db = summary_header_from_db;
86         folder_summary_class->message_info_to_db = message_info_to_db;
87         folder_summary_class->message_info_from_db = message_info_from_db;
88         folder_summary_class->content_info_to_db = content_info_to_db;
89         folder_summary_class->content_info_from_db = content_info_from_db;
90         folder_summary_class->info_set_user_flag = info_set_user_flag;
91 }
92
93 static void
94 camel_imapx_summary_init (CamelIMAPXSummary *obj)
95 {
96 }
97
98 static gint
99 sort_uid_cmp (gpointer enc,
100               gint len1,
101               gpointer data1,
102               gint len2,
103               gpointer data2)
104 {
105         static gchar *sa1 = NULL, *sa2 = NULL;
106         static gint l1 = 0, l2 = 0;
107         gint a1, a2;
108
109         if (l1 < len1 + 1) {
110                 sa1 = g_realloc (sa1, len1 + 1);
111                 l1 = len1 + 1;
112         }
113         if (l2 < len2 + 1) {
114                 sa2 = g_realloc (sa2, len2 + 1);
115                 l2 = len2 + 1;
116         }
117         strncpy (sa1, data1, len1); sa1[len1] = 0;
118         strncpy (sa2, data2, len2); sa2[len2] = 0;
119
120         a1 = strtoul (sa1, NULL, 10);
121         a2 = strtoul (sa2, NULL, 10);
122
123         return (a1 < a1) ? -1 : (a1 > a2) ? 1 : 0;
124 }
125
126 /**
127  * camel_imapx_summary_new:
128  * @folder: Parent folder.
129  * @filename: the file to store the summary in.
130  *
131  * This will create a new CamelIMAPXSummary object and read in the
132  * summary data from disk, if it exists.
133  *
134  * Returns: A new CamelIMAPXSummary object.
135  **/
136 CamelFolderSummary *
137 camel_imapx_summary_new (CamelFolder *folder)
138 {
139         CamelStore *parent_store;
140         CamelFolderSummary *summary;
141         GError *local_error = NULL;
142
143         parent_store = camel_folder_get_parent_store (folder);
144
145         summary = g_object_new (CAMEL_TYPE_IMAPX_SUMMARY, "folder", folder, NULL);
146
147         /* Don't do DB sort. Its pretty slow to load */
148         if (folder && 0) {
149                 camel_db_set_collate (parent_store->cdb_r, "uid", "imapx_uid_sort", (CamelDBCollate)sort_uid_cmp);
150                 summary->sort_by = "uid";
151                 summary->collate = "imapx_uid_sort";
152         }
153
154         camel_folder_summary_set_build_content (summary, TRUE);
155
156         if (!camel_folder_summary_load_from_db (summary, &local_error)) {
157                 /* FIXME: Isn't this dangerous ? We clear the summary
158                 if it cannot be loaded, for some random reason.
159                 We need to pass the error and find out why it is not loaded etc. ? */
160                 camel_folder_summary_clear (summary, NULL);
161                 g_message ("Unable to load summary: %s\n", local_error->message);
162                 g_clear_error (&local_error);
163         }
164
165         return summary;
166 }
167
168 static gboolean
169 summary_header_from_db (CamelFolderSummary *s,
170                         CamelFIRecord *mir)
171 {
172         CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY (s);
173         CamelFolderSummaryClass *folder_summary_class;
174         gchar *part;
175
176         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
177                 camel_imapx_summary_parent_class);
178
179         if (!folder_summary_class->summary_header_from_db (s, mir))
180                 return FALSE;
181
182         part = mir->bdata;
183
184         ims->version = bdata_extract_digit (&part);
185         ims->validity = bdata_extract_digit (&part);
186
187         if (ims->version >= 4) {
188                 ims->uidnext = bdata_extract_digit (&part);
189                 ims->modseq = bdata_extract_digit (&part);
190         }
191
192         if (ims->version > CAMEL_IMAPX_SUMMARY_VERSION) {
193                 g_warning("Unknown summary version\n");
194                 errno = EINVAL;
195                 return FALSE;
196         }
197
198         return TRUE;
199 }
200
201 static CamelFIRecord *
202 summary_header_to_db (CamelFolderSummary *s,
203                       GError **error)
204 {
205         CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY (s);
206         CamelFolderSummaryClass *folder_summary_class;
207         struct _CamelFIRecord *fir;
208
209         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
210                 camel_imapx_summary_parent_class);
211
212         fir = folder_summary_class->summary_header_to_db (s, error);
213         if (!fir)
214                 return NULL;
215         fir->bdata = g_strdup_printf ("%d %" G_GUINT64_FORMAT " %u %" G_GUINT64_FORMAT, CAMEL_IMAPX_SUMMARY_VERSION,
216                                       (guint64) ims->validity, ims->uidnext,
217                                       (guint64) ims->modseq);
218         return fir;
219 }
220
221 static CamelMessageInfo *
222 message_info_from_db (CamelFolderSummary *s,
223                       CamelMIRecord *mir)
224 {
225         CamelMessageInfo *info;
226         CamelIMAPXMessageInfo *iinfo;
227         CamelFolderSummaryClass *folder_summary_class;
228
229         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
230                 camel_imapx_summary_parent_class);
231
232         info = folder_summary_class->message_info_from_db (s, mir);
233         if (info) {
234                 gchar *part = mir->bdata;
235
236                 iinfo = (CamelIMAPXMessageInfo *) info;
237                 iinfo->server_flags = bdata_extract_digit (&part);
238         }
239
240         return info;
241 }
242
243 static CamelMIRecord *
244 message_info_to_db (CamelFolderSummary *s,
245                     CamelMessageInfo *info)
246 {
247         CamelIMAPXMessageInfo *iinfo = (CamelIMAPXMessageInfo *) info;
248         CamelFolderSummaryClass *folder_summary_class;
249         struct _CamelMIRecord *mir;
250
251         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
252                 camel_imapx_summary_parent_class);
253
254         mir = folder_summary_class->message_info_to_db (s, info);
255         if (mir)
256                 mir->bdata = g_strdup_printf ("%u", iinfo->server_flags);
257
258         return mir;
259 }
260
261 static gboolean
262 info_set_user_flag (CamelMessageInfo *info,
263                     const gchar *id,
264                     gboolean state)
265 {
266         CamelFolderSummaryClass *folder_summary_class;
267         gboolean res;
268
269         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
270                 camel_imapx_summary_parent_class);
271
272         res = folder_summary_class->info_set_user_flag (info, id, state);
273
274         /* there was a change, so do not forget to store it to server */
275         if (res)
276                 ((CamelIMAPXMessageInfo *) info)->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
277
278         return res;
279 }
280
281 static CamelMessageContentInfo *
282 content_info_from_db (CamelFolderSummary *s,
283                       CamelMIRecord *mir)
284 {
285         CamelFolderSummaryClass *folder_summary_class;
286         gchar *part = mir->cinfo;
287         guint32 type = 0;
288
289         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
290                 camel_imapx_summary_parent_class);
291
292         if (part) {
293                 if (*part == ' ')
294                         part++;
295                 if (part) {
296                         type = bdata_extract_digit (&part);
297                 }
298         }
299         mir->cinfo = part;
300         if (type)
301                 return folder_summary_class->content_info_from_db (s, mir);
302         else
303                 return camel_folder_summary_content_info_new (s);
304 }
305
306 static gboolean
307 content_info_to_db (CamelFolderSummary *s,
308                     CamelMessageContentInfo *info,
309                     CamelMIRecord *mir)
310 {
311         CamelFolderSummaryClass *folder_summary_class;
312         gchar *oldr;
313
314         folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (
315                 camel_imapx_summary_parent_class);
316
317         if (info->type) {
318                 oldr = mir->cinfo;
319                 mir->cinfo = oldr ? g_strdup_printf("%s 1", oldr) : g_strdup ("1");
320                 g_free (oldr);
321                 return folder_summary_class->content_info_to_db (s, info, mir);
322         } else {
323                 oldr = mir->cinfo;
324                 mir->cinfo = oldr ? g_strdup_printf("%s 0", oldr) : g_strdup ("0");
325                 g_free (oldr);
326                 return TRUE;
327         }
328 }
329
330 void
331 camel_imapx_summary_add_offline (CamelFolderSummary *summary,
332                                  const gchar *uid,
333                                  CamelMimeMessage *message,
334                                  const CamelMessageInfo *info)
335 {
336         CamelIMAPXMessageInfo *mi;
337         const CamelFlag *flag;
338         const CamelTag *tag;
339
340         /* Create summary entry */
341         mi = (CamelIMAPXMessageInfo *) camel_folder_summary_info_new_from_message (summary, message, NULL);
342
343         /* Copy flags 'n' tags */
344         mi->info.flags = camel_message_info_flags (info);
345
346         flag = camel_message_info_user_flags (info);
347         while (flag) {
348                 camel_message_info_set_user_flag ((CamelMessageInfo *) mi, flag->name, TRUE);
349                 flag = flag->next;
350         }
351         tag = camel_message_info_user_tags (info);
352         while (tag) {
353                 camel_message_info_set_user_tag ((CamelMessageInfo *) mi, tag->name, tag->value);
354                 tag = tag->next;
355         }
356
357         mi->info.size = camel_message_info_size (info);
358         mi->info.uid = camel_pstring_strdup (uid);
359
360         camel_folder_summary_add (summary, (CamelMessageInfo *) mi);
361 }
362
363 void
364 camel_imapx_summary_add_offline_uncached (CamelFolderSummary *summary,
365                                           const gchar *uid,
366                                           const CamelMessageInfo *info)
367 {
368         CamelIMAPXMessageInfo *mi;
369
370         mi = camel_message_info_clone (info);
371         mi->info.uid = camel_pstring_strdup (uid);
372
373         camel_folder_summary_add (summary, (CamelMessageInfo *) mi);
374 }