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