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