updated changelog
[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 library is free software you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32
33 #include <glib/gstdio.h>
34 #include <glib/gi18n-lib.h>
35
36 #include "camel-spool-summary.h"
37 #include "camel-local-private.h"
38 #include "camel-win32.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,
46                                                  gint forceindex,
47                                                  GError **error);
48 static gint     spool_summary_check             (CamelLocalSummary *cls,
49                                                  CamelFolderChangeInfo *changeinfo,
50                                                  GCancellable *cancellable,
51                                                  GError **error);
52
53 static gint     spool_summary_sync_full         (CamelMboxSummary *cls,
54                                                  gboolean expunge,
55                                                  CamelFolderChangeInfo *changeinfo,
56                                                  GCancellable *cancellable,
57                                                  GError **error);
58 static gint     spool_summary_need_index        (void);
59
60 G_DEFINE_TYPE (CamelSpoolSummary, camel_spool_summary, CAMEL_TYPE_MBOX_SUMMARY)
61
62 static void
63 camel_spool_summary_class_init (CamelSpoolSummaryClass *class)
64 {
65         CamelLocalSummaryClass *local_summary_class;
66         CamelMboxSummaryClass *mbox_summary_class;
67
68         local_summary_class = CAMEL_LOCAL_SUMMARY_CLASS (class);
69         local_summary_class->load = spool_summary_load;
70         local_summary_class->check = spool_summary_check;
71         local_summary_class->need_index = spool_summary_need_index;
72
73         mbox_summary_class = CAMEL_MBOX_SUMMARY_CLASS (class);
74         mbox_summary_class->sync_full = spool_summary_sync_full;
75 }
76
77 static void
78 camel_spool_summary_init (CamelSpoolSummary *spool_summary)
79 {
80         CamelFolderSummary *folder_summary;
81
82         folder_summary = CAMEL_FOLDER_SUMMARY (spool_summary);
83
84         /* message info size is from mbox parent */
85
86         /* and a unique file version */
87         folder_summary->version += CAMEL_SPOOL_SUMMARY_VERSION;
88 }
89
90 CamelSpoolSummary *
91 camel_spool_summary_new (CamelFolder *folder,
92                          const gchar *mbox_name)
93 {
94         CamelSpoolSummary *new;
95
96         new = g_object_new (CAMEL_TYPE_SPOOL_SUMMARY, "folder", folder, NULL);
97         if (folder) {
98                 CamelStore *parent_store;
99
100                 parent_store = camel_folder_get_parent_store (folder);
101                 camel_db_set_collate (parent_store->cdb_r, "bdata", "spool_frompos_sort", (CamelDBCollate) camel_local_frompos_sort);
102                 ((CamelFolderSummary *) new)->sort_by = "bdata";
103                 ((CamelFolderSummary *) new)->collate = "spool_frompos_sort";
104         }
105         camel_local_summary_construct ((CamelLocalSummary *) new, mbox_name, NULL);
106         camel_folder_summary_load_from_db ((CamelFolderSummary *) new, NULL);
107         return new;
108 }
109
110 static gint
111 spool_summary_load (CamelLocalSummary *cls,
112                     gint forceindex,
113                     GError **error)
114 {
115         /* if not loading, then rescan mbox file content */
116         camel_local_summary_check_force (cls);
117
118         return 0;
119 }
120
121 /* perform a full sync */
122 static gint
123 spool_summary_sync_full (CamelMboxSummary *cls,
124                          gboolean expunge,
125                          CamelFolderChangeInfo *changeinfo,
126                          GCancellable *cancellable,
127                          GError **error)
128 {
129         gint fd = -1, fdout = -1;
130         gchar tmpname[64] = { '\0' };
131         gchar *buffer, *p;
132         goffset spoollen, outlen;
133         gint size, sizeout;
134         struct stat st;
135         guint32 flags = (expunge ? 1 : 0);
136
137         d (printf ("performing full summary/sync\n"));
138
139         camel_operation_push_message (cancellable, _("Storing folder"));
140
141         fd = open (((CamelLocalSummary *) cls)->folder_path, O_RDWR | O_LARGEFILE);
142         if (fd == -1) {
143                 g_set_error (
144                         error, G_IO_ERROR,
145                         g_io_error_from_errno (errno),
146                         _("Could not open file: %s: %s"),
147                         ((CamelLocalSummary *) cls)->folder_path,
148                         g_strerror (errno));
149                 camel_operation_pop_message (cancellable);
150                 return -1;
151         }
152
153         g_snprintf (tmpname, sizeof (tmpname), "/tmp/spool.camel.XXXXXX");
154         fdout = g_mkstemp (tmpname);
155
156         d (printf ("Writing tmp file to %s\n", tmpname));
157         if (fdout == -1) {
158                 g_set_error (
159                         error, G_IO_ERROR,
160                         g_io_error_from_errno (errno),
161                         _("Cannot open temporary mailbox: %s"),
162                         g_strerror (errno));
163                 goto error;
164         }
165
166         if (camel_mbox_summary_sync_mbox (
167                 (CamelMboxSummary *) cls, flags, changeinfo,
168                 fd, fdout, cancellable, error) == -1)
169                 goto error;
170
171         /* sync out content */
172         if (fsync (fdout) == -1) {
173                 g_warning ("Cannot synchronize temporary folder: %s", g_strerror (errno));
174                 g_set_error (
175                         error, G_IO_ERROR,
176                         g_io_error_from_errno (errno),
177                         _("Could not synchronize temporary folder %s: %s"),
178                         ((CamelLocalSummary *) cls)->folder_path,
179                         g_strerror (errno));
180                 goto error;
181         }
182
183         /* see if we can write this much to the spool file */
184         if (fstat (fd, &st) == -1) {
185                 g_warning ("Cannot synchronize temporary folder: %s", g_strerror (errno));
186                 g_set_error (
187                         error, G_IO_ERROR,
188                         g_io_error_from_errno (errno),
189                         _("Could not synchronize temporary folder %s: %s"),
190                         ((CamelLocalSummary *) cls)->folder_path,
191                         g_strerror (errno));
192                 goto error;
193         }
194         spoollen = st.st_size;
195
196         if (fstat (fdout, &st) == -1) {
197                 g_warning ("Cannot synchronize temporary folder: %s", g_strerror (errno));
198                 g_set_error (
199                         error, G_IO_ERROR,
200                         g_io_error_from_errno (errno),
201                         _("Could not synchronize temporary folder %s: %s"),
202                         ((CamelLocalSummary *) cls)->folder_path,
203                         g_strerror (errno));
204                 goto error;
205         }
206         outlen = st.st_size;
207
208         /* I think this is the right way to do this - checking that the file will fit the new data */
209         if (outlen > 0
210             && (lseek (fd, outlen - 1, SEEK_SET) == -1
211                 || write (fd, "", 1) != 1
212                 || fsync (fd) == -1
213                 || lseek (fd, 0, SEEK_SET) == -1
214                 || lseek (fdout, 0, SEEK_SET) == -1)) {
215                 g_warning ("Cannot synchronize spool folder: %s", g_strerror (errno));
216                 g_set_error (
217                         error, G_IO_ERROR,
218                         g_io_error_from_errno (errno),
219                         _("Could not synchronize spool folder %s: %s"),
220                         ((CamelLocalSummary *) cls)->folder_path,
221                         g_strerror (errno));
222                 /* incase we ran out of room, remove any trailing space first */
223                 if (ftruncate (fd, spoollen) == -1) {
224                         g_debug ("%s: Failed to call ftruncate: %s", G_STRFUNC, g_strerror (errno));
225                 }
226                 goto error;
227         }
228
229         /* now copy content back */
230         buffer = g_malloc (8192);
231         size = 1;
232         while (size > 0) {
233                 do {
234                         size = read (fdout, buffer, 8192);
235                 } while (size == -1 && errno == EINTR);
236
237                 if (size > 0) {
238                         p = buffer;
239                         do {
240                                 sizeout = write (fd, p, size);
241                                 if (sizeout > 0) {
242                                         p+= sizeout;
243                                         size -= sizeout;
244                                 }
245                         } while ((sizeout == -1 && errno == EINTR) && size > 0);
246                         size = sizeout;
247                 }
248
249                 if (size == -1) {
250                         g_set_error (
251                                 error, G_IO_ERROR,
252                                 g_io_error_from_errno (errno),
253                                 _("Could not synchronize spool folder %s: %s\n"
254                                 "Folder may be corrupt, copy saved in '%s'"),
255                                 ((CamelLocalSummary *) cls)->folder_path,
256                                 g_strerror (errno), tmpname);
257                         /* so we dont delete it */
258                         tmpname[0] = '\0';
259                         g_free (buffer);
260                         goto error;
261                 }
262         }
263
264         g_free (buffer);
265
266         d (printf ("Closing folders\n"));
267
268         if (ftruncate (fd, outlen) == -1) {
269                 g_set_error (
270                         error, G_IO_ERROR,
271                         g_io_error_from_errno (errno),
272                         _("Could not synchronize spool folder %s: %s\n"
273                         "Folder may be corrupt, copy saved in '%s'"),
274                         ((CamelLocalSummary *) cls)->folder_path,
275                         g_strerror (errno), tmpname);
276                 tmpname[0] = '\0';
277                 goto error;
278         }
279
280         if (close (fd) == -1) {
281                 g_warning ("Cannot close source folder: %s", g_strerror (errno));
282                 g_set_error (
283                         error, G_IO_ERROR,
284                         g_io_error_from_errno (errno),
285                         _("Could not synchronize spool folder %s: %s\n"
286                         "Folder may be corrupt, copy saved in '%s'"),
287                         ((CamelLocalSummary *) cls)->folder_path,
288                         g_strerror (errno), tmpname);
289                 tmpname[0] = '\0';
290                 fd = -1;
291                 goto error;
292         }
293
294         close (fdout);
295
296         if (tmpname[0] != '\0')
297                 unlink (tmpname);
298
299         camel_operation_pop_message (cancellable);
300
301         return 0;
302  error:
303         if (fd != -1)
304                 close (fd);
305
306         if (fdout != -1)
307                 close (fdout);
308
309         if (tmpname[0] != '\0')
310                 unlink (tmpname);
311
312         camel_operation_pop_message (cancellable);
313
314         return -1;
315 }
316
317 static gint
318 spool_summary_check (CamelLocalSummary *cls,
319                      CamelFolderChangeInfo *changeinfo,
320                      GCancellable *cancellable,
321                      GError **error)
322 {
323         gint i;
324         gboolean work;
325         struct stat st;
326         CamelFolderSummary *s = (CamelFolderSummary *) cls;
327         GPtrArray *known_uids;
328
329         if (CAMEL_LOCAL_SUMMARY_CLASS (camel_spool_summary_parent_class)->check (cls, changeinfo, cancellable, error) == -1)
330                 return -1;
331
332         /* check to see if we need to copy/update the file; missing xev headers prompt this */
333         work = FALSE;
334         camel_folder_summary_prepare_fetch_all (s, error);
335         known_uids = camel_folder_summary_get_array (s);
336         for (i = 0; !work && known_uids && i < known_uids->len; i++) {
337                 CamelMboxMessageInfo *info = (CamelMboxMessageInfo *) camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
338                 g_assert (info);
339                 work = (info->info.info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV)) != 0;
340                 camel_message_info_unref (info);
341         }
342         camel_folder_summary_free_array (known_uids);
343
344         /* if we do, then write out the headers using sync_full, etc */
345         if (work) {
346                 d (printf ("Have to add new headers, re-syncing from the start to accomplish this\n"));
347                 if (CAMEL_MBOX_SUMMARY_GET_CLASS (cls)->sync_full (
348                         CAMEL_MBOX_SUMMARY (cls), FALSE,
349                         changeinfo, cancellable, error) == -1)
350                         return -1;
351
352                 if (g_stat (cls->folder_path, &st) == -1) {
353                         g_set_error (
354                                 error, G_IO_ERROR,
355                                 g_io_error_from_errno (errno),
356                                 _("Unknown error: %s"),
357                                 g_strerror (errno));
358                         return -1;
359                 }
360
361                 ((CamelMboxSummary *) cls)->folder_size = st.st_size;
362                 ((CamelFolderSummary *) cls)->time = st.st_mtime;
363         }
364
365         return 0;
366 }
367
368 static gint
369 spool_summary_need_index (void)
370 {
371         return 0;
372 }