Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / local / camel-spool-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
2  *
3  *  Copyright (C) 2001 Ximian Inc. (www.ximian.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 <errno.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include <glib/gi18n-lib.h>
36
37 #include "camel-file-utils.h"
38 #include "camel-mime-message.h"
39 #include "camel-operation.h"
40
41 #include "camel-spool-summary.h"
42
43 #define io(x)
44 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
45
46 #define CAMEL_SPOOL_SUMMARY_VERSION (0x400)
47
48 static int spool_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex);
49 static int spool_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
50
51 static int spool_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
52
53 static void camel_spool_summary_class_init (CamelSpoolSummaryClass *klass);
54 static void camel_spool_summary_init       (CamelSpoolSummary *obj);
55 static void camel_spool_summary_finalise   (CamelObject *obj);
56
57 static CamelFolderSummaryClass *camel_spool_summary_parent;
58
59 CamelType
60 camel_spool_summary_get_type(void)
61 {
62         static CamelType type = CAMEL_INVALID_TYPE;
63         
64         if (type == CAMEL_INVALID_TYPE) {
65                 type = camel_type_register(camel_mbox_summary_get_type(), "CamelSpoolSummary",
66                                            sizeof (CamelSpoolSummary),
67                                            sizeof (CamelSpoolSummaryClass),
68                                            (CamelObjectClassInitFunc) camel_spool_summary_class_init,
69                                            NULL,
70                                            (CamelObjectInitFunc) camel_spool_summary_init,
71                                            (CamelObjectFinalizeFunc) camel_spool_summary_finalise);
72         }
73         
74         return type;
75 }
76
77 static void
78 camel_spool_summary_class_init(CamelSpoolSummaryClass *klass)
79 {
80         CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)klass;
81         CamelMboxSummaryClass *mklass = (CamelMboxSummaryClass *)klass;
82
83         camel_spool_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_mbox_summary_get_type());
84
85         lklass->load = spool_summary_load;
86         lklass->check = spool_summary_check;
87
88         mklass->sync_full = spool_summary_sync_full;
89 }
90
91 static void
92 camel_spool_summary_init(CamelSpoolSummary *obj)
93 {
94         struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
95         
96         /* message info size is from mbox parent */
97
98         /* and a unique file version */
99         s->version += CAMEL_SPOOL_SUMMARY_VERSION;
100 }
101
102 static void
103 camel_spool_summary_finalise(CamelObject *obj)
104 {
105         /*CamelSpoolSummary *mbs = CAMEL_SPOOL_SUMMARY(obj);*/
106 }
107
108 CamelSpoolSummary *
109 camel_spool_summary_new(struct _CamelFolder *folder, const char *mbox_name)
110 {
111         CamelSpoolSummary *new = (CamelSpoolSummary *)camel_object_new(camel_spool_summary_get_type());
112
113         ((CamelFolderSummary *)new)->folder = folder;
114
115         camel_local_summary_construct((CamelLocalSummary *)new, NULL, mbox_name, NULL);
116         return new;
117 }
118
119 static int
120 spool_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex)
121 {
122         g_warning("spool summary - not loading anything\n");
123         return 0;
124 }
125
126 /* perform a full sync */
127 static int
128 spool_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
129 {
130         int fd = -1, fdout = -1;
131         char tmpname[64] = { '\0' };
132         char *buffer, *p;
133         off_t spoollen, outlen;
134         int size, sizeout;
135         struct stat st;
136         guint32 flags = (expunge?1:0);
137
138         d(printf("performing full summary/sync\n"));
139
140         camel_operation_start(NULL, _("Storing folder"));
141
142         fd = open(((CamelLocalSummary *)cls)->folder_path, O_RDWR);
143         if (fd == -1) {
144                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
145                                       _("Could not open file: %s: %s"),
146                                       ((CamelLocalSummary *)cls)->folder_path,
147                                       g_strerror (errno));
148                 camel_operation_end(NULL);
149                 return -1;
150         }
151
152         sprintf (tmpname, "/tmp/spool.camel.XXXXXX");
153         fdout = g_mkstemp (tmpname);
154
155         d(printf("Writing tmp file to %s\n", tmpname));
156         if (fdout == -1) {
157                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
158                                       _("Cannot open temporary mailbox: %s"),
159                                       g_strerror (errno));
160                 goto error;
161         }
162
163         if (camel_mbox_summary_sync_mbox((CamelMboxSummary *)cls, flags, changeinfo, fd, fdout, ex) == -1)
164                 goto error;
165
166
167         /* sync out content */
168         if (fsync(fdout) == -1) {
169                 g_warning("Cannot sync temporary folder: %s", strerror (errno));
170                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
171                                       _("Could not sync temporary folder %s: %s"),
172                                       ((CamelLocalSummary *)cls)->folder_path,
173                                       g_strerror (errno));
174                 goto error;
175         }
176
177         /* see if we can write this much to the spool file */
178         if (fstat(fd, &st) == -1) {
179                 g_warning("Cannot sync temporary folder: %s", strerror (errno));
180                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
181                                       _("Could not sync temporary folder %s: %s"),
182                                       ((CamelLocalSummary *)cls)->folder_path,
183                                       g_strerror (errno));
184                 goto error;
185         }
186         spoollen = st.st_size;
187
188         if (fstat(fdout, &st) == -1) {
189                 g_warning("Cannot sync temporary folder: %s", strerror (errno));
190                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
191                                       _("Could not sync temporary folder %s: %s"),
192                                       ((CamelLocalSummary *)cls)->folder_path,
193                                       g_strerror (errno));
194                 goto error;
195         }
196         outlen = st.st_size;
197
198         /* I think this is the right way to do this - checking that the file will fit the new data */
199         if (outlen>0
200             && (lseek(fd, outlen-1, SEEK_SET) == -1
201                 || write(fd, "", 1) != 1
202                 || fsync(fd) == -1
203                 || lseek(fd, 0, SEEK_SET) == -1
204                 || lseek(fdout, 0, SEEK_SET) == -1)) {
205                 g_warning("Cannot sync spool folder: %s", strerror (errno));
206                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
207                                       _("Could not sync spool folder %s: %s"),
208                                       ((CamelLocalSummary *)cls)->folder_path,
209                                       g_strerror (errno));
210                 /* incase we ran out of room, remove any trailing space first */
211                 ftruncate(fd, spoollen);
212                 goto error;
213         }
214
215
216         /* now copy content back */
217         buffer = g_malloc(8192);
218         size = 1;
219         while (size>0) {
220                 do {
221                         size = read(fdout, buffer, 8192);
222                 } while (size == -1 && errno == EINTR);
223
224                 if (size > 0) {
225                         p = buffer;
226                         do {
227                                 sizeout = write(fd, p, size);
228                                 if (sizeout > 0) {
229                                         p+= sizeout;
230                                         size -= sizeout;
231                                 }
232                         } while ((sizeout == -1 && errno == EINTR) && size > 0);
233                         size = sizeout;
234                 }
235
236                 if (size == -1) {
237                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
238                                               _("Could not sync spool folder %s: %s\n"
239                                                 "Folder may be corrupt, copy saved in `%s'"),
240                                               ((CamelLocalSummary *)cls)->folder_path,
241                                               g_strerror (errno), tmpname);
242                         /* so we dont delete it */
243                         tmpname[0] = '\0';
244                         g_free(buffer);
245                         goto error;
246                 }
247         }
248
249         g_free(buffer);
250
251         d(printf("Closing folders\n"));
252
253         if (ftruncate(fd, outlen) == -1) {
254                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
255                                       _("Could not sync spool folder %s: %s\n"
256                                         "Folder may be corrupt, copy saved in `%s'"),
257                                       ((CamelLocalSummary *)cls)->folder_path,
258                                       g_strerror (errno), tmpname);
259                 tmpname[0] = '\0';
260                 goto error;
261         }
262
263         if (close(fd) == -1) {
264                 g_warning("Cannot close source folder: %s", strerror (errno));
265                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
266                                       _("Could not sync spool folder %s: %s\n"
267                                         "Folder may be corrupt, copy saved in `%s'"),
268                                       ((CamelLocalSummary *)cls)->folder_path,
269                                       g_strerror (errno), tmpname);
270                 tmpname[0] = '\0';
271                 fd = -1;
272                 goto error;
273         }
274
275         close(fdout);
276
277         if (tmpname[0] != '\0')
278                 unlink(tmpname);
279
280         camel_operation_end(NULL);
281                 
282         return 0;
283  error:
284         if (fd != -1)
285                 close(fd);
286         
287         if (fdout != -1)
288                 close(fdout);
289         
290         if (tmpname[0] != '\0')
291                 unlink(tmpname);
292
293         camel_operation_end(NULL);
294
295         return -1;
296 }
297
298 static int
299 spool_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex)
300 {
301         int i, work, count;
302         struct stat st;
303         CamelFolderSummary *s = (CamelFolderSummary *)cls;
304
305         if (((CamelLocalSummaryClass *)camel_spool_summary_parent)->check(cls, changeinfo, ex) == -1)
306                 return -1;
307
308         /* check to see if we need to copy/update the file; missing xev headers prompt this */
309         work = FALSE;
310         count = camel_folder_summary_count(s);
311         for (i=0;!work && i<count; i++) {
312                 CamelMboxMessageInfo *info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
313                 g_assert(info);
314                 work = (info->info.info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV)) != 0;
315                 camel_message_info_free((CamelMessageInfo *)info);
316         }
317
318         /* if we do, then write out the headers using sync_full, etc */
319         if (work) {
320                 d(printf("Have to add new headers, re-syncing from the start to accomplish this\n"));
321                 if (((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_full((CamelMboxSummary *)cls, FALSE, changeinfo, ex) == -1)
322                         return -1;
323                 
324                 if (stat(cls->folder_path, &st) == -1) {
325                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
326                                               _("Unknown error: %s"),
327                                               g_strerror (errno));
328                         return -1;
329                 }
330
331                 ((CamelMboxSummary *)cls)->folder_size = st.st_size;
332                 ((CamelFolderSummary *)cls)->time = st.st_mtime;
333         }
334
335         return 0;
336 }