Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / imap / camel-imap-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Copyright (C) 2000 Ximian Inc.
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-file-utils.h"
35
36 #include "camel-imap-summary.h"
37 #include "camel-imap-utils.h"
38
39 #define CAMEL_IMAP_SUMMARY_VERSION (3)
40
41 static int summary_header_load (CamelFolderSummary *, FILE *);
42 static int summary_header_save (CamelFolderSummary *, FILE *);
43
44 static CamelMessageInfo *message_info_load (CamelFolderSummary *s, FILE *in);
45 static int message_info_save (CamelFolderSummary *s, FILE *out,
46                               CamelMessageInfo *info);
47 static gboolean info_set_user_tag(CamelMessageInfo *info, const char *name, const char  *value);
48 static CamelMessageContentInfo *content_info_load (CamelFolderSummary *s, FILE *in);
49 static int content_info_save (CamelFolderSummary *s, FILE *out,
50                               CamelMessageContentInfo *info);
51
52 static void camel_imap_summary_class_init (CamelImapSummaryClass *klass);
53 static void camel_imap_summary_init       (CamelImapSummary *obj);
54
55 static CamelFolderSummaryClass *camel_imap_summary_parent;
56
57 CamelType
58 camel_imap_summary_get_type (void)
59 {
60         static CamelType type = CAMEL_INVALID_TYPE;
61
62         if (type == CAMEL_INVALID_TYPE) {
63                 type = camel_type_register(
64                         camel_folder_summary_get_type(), "CamelImapSummary",
65                         sizeof (CamelImapSummary),
66                         sizeof (CamelImapSummaryClass),
67                         (CamelObjectClassInitFunc) camel_imap_summary_class_init,
68                         NULL,
69                         (CamelObjectInitFunc) camel_imap_summary_init,
70                         NULL);
71         }
72
73         return type;
74 }
75
76 static CamelMessageInfo *
77 imap_message_info_clone(CamelFolderSummary *s, const CamelMessageInfo *mi)
78 {
79         CamelImapMessageInfo *to;
80         const CamelImapMessageInfo *from = (const CamelImapMessageInfo *)mi;
81
82         to = (CamelImapMessageInfo *)camel_imap_summary_parent->message_info_clone(s, mi);
83         to->server_flags = from->server_flags;
84
85         /* FIXME: parent clone should do this */
86         to->info.content = camel_folder_summary_content_info_new(s);
87
88         return (CamelMessageInfo *)to;
89 }
90
91 static void
92 camel_imap_summary_class_init (CamelImapSummaryClass *klass)
93 {
94         CamelFolderSummaryClass *cfs_class = (CamelFolderSummaryClass *) klass;
95
96         camel_imap_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS (camel_type_get_global_classfuncs (camel_folder_summary_get_type()));
97
98         cfs_class->message_info_clone = imap_message_info_clone;
99
100         cfs_class->summary_header_load = summary_header_load;
101         cfs_class->summary_header_save = summary_header_save;
102         cfs_class->message_info_load = message_info_load;
103         cfs_class->message_info_save = message_info_save;
104         cfs_class->content_info_load = content_info_load;
105         cfs_class->content_info_save = content_info_save;
106
107         cfs_class->info_set_user_tag = info_set_user_tag;
108 }
109
110 static void
111 camel_imap_summary_init (CamelImapSummary *obj)
112 {
113         CamelFolderSummary *s = (CamelFolderSummary *)obj;
114
115         /* subclasses need to set the right instance data sizes */
116         s->message_info_size = sizeof(CamelImapMessageInfo);
117         s->content_info_size = sizeof(CamelImapMessageContentInfo);
118 }
119
120 /**
121  * camel_imap_summary_new:
122  * @folder: Parent folder.
123  * @filename: the file to store the summary in.
124  *
125  * This will create a new CamelImapSummary object and read in the
126  * summary data from disk, if it exists.
127  *
128  * Return value: A new CamelImapSummary object.
129  **/
130 CamelFolderSummary *
131 camel_imap_summary_new (struct _CamelFolder *folder, const char *filename)
132 {
133         CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (camel_object_new (camel_imap_summary_get_type ()));
134
135         summary->folder = folder;
136
137         camel_folder_summary_set_build_content (summary, TRUE);
138         camel_folder_summary_set_filename (summary, filename);
139
140         if (camel_folder_summary_load (summary) == -1) {
141                 camel_folder_summary_clear (summary);
142                 camel_folder_summary_touch (summary);
143         }
144
145         return summary;
146 }
147
148 static int
149 summary_header_load (CamelFolderSummary *s, FILE *in)
150 {
151         CamelImapSummary *ims = CAMEL_IMAP_SUMMARY (s);
152         
153         if (camel_imap_summary_parent->summary_header_load (s, in) == -1)
154                 return -1;
155
156         /* Legacy version */
157         if (s->version == 0x30c)
158                 return camel_file_util_decode_uint32(in, &ims->validity);
159
160         /* Version 1 */
161         if (camel_file_util_decode_fixed_int32(in, &ims->version) == -1)
162                 return -1;
163         
164         if (ims->version == 2) {
165                 /* Version 2: for compat with version 2 of the imap4 summary files */
166                 int have_mlist;
167                 
168                 if (camel_file_util_decode_fixed_int32 (in, &have_mlist) == -1)
169                         return -1;
170         }
171         
172         if (camel_file_util_decode_fixed_int32(in, &ims->validity) == -1)
173                 return -1;
174         
175         if (ims->version > CAMEL_IMAP_SUMMARY_VERSION) {
176                 g_warning("Unkown summary version\n");
177                 errno = EINVAL;
178                 return -1;
179         }
180
181         return 0;
182 }
183
184 static int
185 summary_header_save (CamelFolderSummary *s, FILE *out)
186 {
187         CamelImapSummary *ims = CAMEL_IMAP_SUMMARY(s);
188
189         if (camel_imap_summary_parent->summary_header_save (s, out) == -1)
190                 return -1;
191
192         camel_file_util_encode_fixed_int32(out, CAMEL_IMAP_SUMMARY_VERSION);
193
194         return camel_file_util_encode_fixed_int32(out, ims->validity);
195 }
196
197 /* We snoop the setting of the 'label' tag, so we can store it on
198    the server, also in a mozilla compatiable way as a bonus */
199 static void
200 label_to_flags(CamelImapMessageInfo *info)
201 {
202         guint32 flags;
203
204         flags = imap_label_to_flags((CamelMessageInfo *)info);
205         if ((info->info.flags & CAMEL_IMAP_MESSAGE_LABEL_MASK) != flags)
206                 info->info.flags = (info->info.flags & ~CAMEL_IMAP_MESSAGE_LABEL_MASK) | flags | CAMEL_MESSAGE_FOLDER_FLAGGED;
207 }
208
209 static CamelMessageInfo *
210 message_info_load (CamelFolderSummary *s, FILE *in)
211 {
212         CamelMessageInfo *info;
213         CamelImapMessageInfo *iinfo;
214
215         info = camel_imap_summary_parent->message_info_load (s, in);
216         if (info) {
217                 iinfo = (CamelImapMessageInfo *)info;
218
219                 if (camel_file_util_decode_uint32 (in, &iinfo->server_flags) == -1)
220                         goto error;
221
222                 label_to_flags(iinfo);
223         }
224
225         return info;
226 error:
227         camel_message_info_free(info);
228         return NULL;
229 }
230
231 static int
232 message_info_save (CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
233 {
234         CamelImapMessageInfo *iinfo = (CamelImapMessageInfo *)info;
235
236         if (camel_imap_summary_parent->message_info_save (s, out, info) == -1)
237                 return -1;
238
239         return camel_file_util_encode_uint32 (out, iinfo->server_flags);
240 }
241
242 static gboolean
243 info_set_user_tag(CamelMessageInfo *info, const char *name, const char  *value)
244 {
245         int res;
246
247         res = camel_imap_summary_parent->info_set_user_tag(info, name, value);
248
249         if (!strcmp(name, "label"))
250                 label_to_flags((CamelImapMessageInfo *)info);
251
252         return res;
253 }
254
255 static CamelMessageContentInfo *
256 content_info_load (CamelFolderSummary *s, FILE *in)
257 {
258         if (fgetc (in))
259                 return camel_imap_summary_parent->content_info_load (s, in);
260         else
261                 return camel_folder_summary_content_info_new (s);
262 }
263
264 static int
265 content_info_save (CamelFolderSummary *s, FILE *out,
266                    CamelMessageContentInfo *info)
267 {
268         if (info->type) {
269                 fputc (1, out);
270                 return camel_imap_summary_parent->content_info_save (s, out, info);
271         } else
272                 return fputc (0, out);
273 }
274
275 void
276 camel_imap_summary_add_offline (CamelFolderSummary *summary, const char *uid,
277                                 CamelMimeMessage *message,
278                                 const CamelMessageInfo *info)
279 {
280         CamelImapMessageInfo *mi;
281         const CamelFlag *flag;
282         const CamelTag *tag;
283
284         /* Create summary entry */
285         mi = (CamelImapMessageInfo *)camel_folder_summary_info_new_from_message (summary, message);
286
287         /* Copy flags 'n' tags */
288         mi->info.flags = camel_message_info_flags(info);
289
290         flag = camel_message_info_user_flags(info);
291         while (flag) {
292                 camel_message_info_set_user_flag((CamelMessageInfo *)mi, flag->name, TRUE);
293                 flag = flag->next;
294         }
295         tag = camel_message_info_user_tags(info);
296         while (tag) {
297                 camel_message_info_set_user_tag((CamelMessageInfo *)mi, tag->name, tag->value);
298                 tag = tag->next;
299         }
300
301         mi->info.size = camel_message_info_size(info);
302         mi->info.uid = g_strdup (uid);
303
304         label_to_flags(mi);
305
306         camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
307 }
308
309 void
310 camel_imap_summary_add_offline_uncached (CamelFolderSummary *summary, const char *uid,
311                                          const CamelMessageInfo *info)
312 {
313         CamelImapMessageInfo *mi;
314
315         mi = camel_message_info_clone(info);
316         mi->info.uid = g_strdup(uid);
317
318         label_to_flags(mi);
319
320         camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
321 }