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  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Michael Zucchi <notzed@ximian.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 
31 #include <glib/gi18n-lib.h>
32 #include <glib/gstdio.h>
33 
34 #include "camel-mbox-message-info.h"
35 #include "camel-mbox-summary.h"
36 #include "camel-local-private.h"
37 
38 #define io(x)
39 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
40 
41 /* This uses elm/pine style "Status" & "X-Status" headers */
42 
43 #define CAMEL_MBOX_SUMMARY_VERSION (1)
44 
45 #define CHECK_CALL(x) G_STMT_START { \
46 	if ((x) == -1) { \
47 		g_debug ("%s: Call of '" #x "' failed: %s", G_STRFUNC, g_strerror (errno)); \
48 	} \
49 	} G_STMT_END
50 
51 typedef struct _CamelMboxMessageContentInfo CamelMboxMessageContentInfo;
52 
53 struct _CamelMboxMessageContentInfo {
54 	CamelMessageContentInfo info;
55 };
56 
57 static CamelFIRecord *
58 		summary_header_save		(CamelFolderSummary *,
59 						 GError **error);
60 static gboolean	summary_header_load		(CamelFolderSummary *,
61 						 CamelFIRecord *);
62 static CamelMessageInfo *
63 		message_info_new_from_headers	(CamelFolderSummary *,
64 						 const CamelNameValueArray *);
65 static CamelMessageInfo *
66 		message_info_new_from_parser	(CamelFolderSummary *,
67 						 CamelMimeParser *);
68 
69 static gchar *	mbox_summary_encode_x_evolution	(CamelLocalSummary *cls,
70 						 const CamelMessageInfo *mi);
71 
72 static gint	mbox_summary_check		(CamelLocalSummary *cls,
73 						 CamelFolderChangeInfo *changeinfo,
74 						 GCancellable *cancellable,
75 						 GError **error);
76 static gint	mbox_summary_sync		(CamelLocalSummary *cls,
77 						 gboolean expunge,
78 						 CamelFolderChangeInfo *changeinfo,
79 						 GCancellable *cancellable,
80 						 GError **error);
81 static CamelMessageInfo *
82 		mbox_summary_add		(CamelLocalSummary *cls,
83 						 CamelMimeMessage *msg,
84 						 const CamelMessageInfo *info,
85 						 CamelFolderChangeInfo *ci,
86 						 GError **error);
87 static gint	mbox_summary_sync_quick		(CamelMboxSummary *cls,
88 						 gboolean expunge,
89 						 CamelFolderChangeInfo *changeinfo,
90 						 GCancellable *cancellable,
91 						 GError **error);
92 static gint	mbox_summary_sync_full		(CamelMboxSummary *cls,
93 						 gboolean expunge,
94 						 CamelFolderChangeInfo *changeinfo,
95 						 GCancellable *cancellable,
96 						 GError **error);
97 
98 /* Which status flags are stored in each separate header */
99 #define STATUS_XSTATUS \
100 	(CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_DELETED)
101 #define STATUS_STATUS (CAMEL_MESSAGE_SEEN)
102 
103 static void encode_status (guint32 flags, gchar status[8]);
104 static guint32 decode_status (const gchar *status);
105 
G_DEFINE_TYPE(CamelMboxSummary,camel_mbox_summary,CAMEL_TYPE_LOCAL_SUMMARY)106 G_DEFINE_TYPE (
107 	CamelMboxSummary,
108 	camel_mbox_summary,
109 	CAMEL_TYPE_LOCAL_SUMMARY)
110 
111 static void
112 camel_mbox_summary_class_init (CamelMboxSummaryClass *class)
113 {
114 	CamelFolderSummaryClass *folder_summary_class;
115 	CamelLocalSummaryClass *local_summary_class;
116 
117 	folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (class);
118 	folder_summary_class->message_info_type = CAMEL_TYPE_MBOX_MESSAGE_INFO;
119 	folder_summary_class->sort_by = "bdata";
120 	folder_summary_class->collate = "mbox_frompos_sort";
121 	folder_summary_class->summary_header_load = summary_header_load;
122 	folder_summary_class->summary_header_save = summary_header_save;
123 	folder_summary_class->message_info_new_from_headers = message_info_new_from_headers;
124 	folder_summary_class->message_info_new_from_parser = message_info_new_from_parser;
125 
126 	local_summary_class = CAMEL_LOCAL_SUMMARY_CLASS (class);
127 	local_summary_class->encode_x_evolution = mbox_summary_encode_x_evolution;
128 	local_summary_class->check = mbox_summary_check;
129 	local_summary_class->sync = mbox_summary_sync;
130 	local_summary_class->add = mbox_summary_add;
131 
132 	class->sync_quick = mbox_summary_sync_quick;
133 	class->sync_full = mbox_summary_sync_full;
134 }
135 
136 static void
camel_mbox_summary_init(CamelMboxSummary * mbox_summary)137 camel_mbox_summary_init (CamelMboxSummary *mbox_summary)
138 {
139 	CamelFolderSummary *folder_summary;
140 
141 	folder_summary = CAMEL_FOLDER_SUMMARY (mbox_summary);
142 
143 	/* and a unique file version */
144 	camel_folder_summary_set_version (folder_summary, camel_folder_summary_get_version (folder_summary) + CAMEL_MBOX_SUMMARY_VERSION);
145 }
146 
147 /**
148  * camel_mbox_summary_new:
149  * @folder: a parent #CamelFolder
150  * @mbox_name: filename of the mbox
151  * @index: (nullable): an optional #CamelIndex to use, or %NULL
152  *
153  * Create a new #CamelMboxSummary object.
154  *
155  * Returns: (transfer full): A new #CamelMboxSummary object
156  **/
157 CamelMboxSummary *
camel_mbox_summary_new(CamelFolder * folder,const gchar * mbox_name,CamelIndex * index)158 camel_mbox_summary_new (CamelFolder *folder,
159                         const gchar *mbox_name,
160                         CamelIndex *index)
161 {
162 	CamelMboxSummary *new;
163 
164 	new = g_object_new (CAMEL_TYPE_MBOX_SUMMARY, "folder", folder, NULL);
165 	if (folder) {
166 		CamelStore *parent_store;
167 
168 		parent_store = camel_folder_get_parent_store (folder);
169 
170 		/* Set the functions for db sorting */
171 		camel_db_set_collate (camel_store_get_db (parent_store), "bdata", "mbox_frompos_sort", (CamelDBCollate) camel_local_frompos_sort);
172 	}
173 	camel_local_summary_construct ((CamelLocalSummary *) new, mbox_name, index);
174 	return new;
175 }
176 
camel_mbox_summary_xstatus(CamelMboxSummary * mbs,gint state)177 void camel_mbox_summary_xstatus (CamelMboxSummary *mbs, gint state)
178 {
179 	mbs->xstatus = state;
180 }
181 
182 static gchar *
mbox_summary_encode_x_evolution(CamelLocalSummary * cls,const CamelMessageInfo * mi)183 mbox_summary_encode_x_evolution (CamelLocalSummary *cls,
184                                  const CamelMessageInfo *mi)
185 {
186 	const gchar *p, *uidstr;
187 	guint32 uid, flags;
188 
189 	/* This is busted, it is supposed to encode ALL DATA */
190 	p = uidstr = camel_message_info_get_uid (mi);
191 	while (*p && isdigit (*p))
192 		p++;
193 
194 	flags = camel_message_info_get_flags (mi);
195 
196 	if (*p == 0 && sscanf (uidstr, "%u", &uid) == 1) {
197 		return g_strdup_printf ("%08x-%04x", uid, flags & 0xffff);
198 	} else {
199 		return g_strdup_printf ("%s-%04x", uidstr, flags & 0xffff);
200 	}
201 }
202 
203 static gboolean
summary_header_load(CamelFolderSummary * s,struct _CamelFIRecord * fir)204 summary_header_load (CamelFolderSummary *s,
205 		     struct _CamelFIRecord *fir)
206 {
207 	CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY (s);
208 	gchar *part;
209 
210 	if (!CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_parent_class)->summary_header_load (s, fir))
211 		return FALSE;
212 
213 	part = fir->bdata;
214 	if (part) {
215 		mbs->version = camel_util_bdata_get_number (&part, 0);
216 		mbs->folder_size = camel_util_bdata_get_number (&part, 0);
217 	}
218 
219 	return TRUE;
220 }
221 
222 static CamelFIRecord *
summary_header_save(CamelFolderSummary * s,GError ** error)223 summary_header_save (CamelFolderSummary *s,
224 		     GError **error)
225 {
226 	CamelFolderSummaryClass *folder_summary_class;
227 	CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY (s);
228 	struct _CamelFIRecord *fir;
229 	gchar *tmp;
230 
231 	/* Chain up to parent's summary_header_save() method. */
232 	folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_parent_class);
233 	fir = folder_summary_class->summary_header_save (s, error);
234 	if (fir) {
235 		tmp = fir->bdata;
236 		fir->bdata = g_strdup_printf ("%s %d %d", tmp ? tmp : "", CAMEL_MBOX_SUMMARY_VERSION, (gint) mbs->folder_size);
237 		g_free (tmp);
238 	}
239 
240 	return fir;
241 }
242 
243 static CamelMessageInfo *
message_info_new_from_headers(CamelFolderSummary * summary,const CamelNameValueArray * headers)244 message_info_new_from_headers (CamelFolderSummary *summary,
245 			       const CamelNameValueArray *headers)
246 {
247 	CamelMessageInfo *mi;
248 	CamelMboxSummary *mbs = (CamelMboxSummary *) summary;
249 
250 	mi = CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_parent_class)->message_info_new_from_headers (summary, headers);
251 	if (mi) {
252 		const gchar *xev, *uid;
253 		CamelMessageInfo *info = NULL;
254 		gint add = 0;	/* bitmask of things to add, 1 assign uid, 2, just add as new, 4 = recent */
255 		const gchar *status = NULL, *xstatus = NULL;
256 		guint32 flags = 0;
257 
258 		if (mbs->xstatus) {
259 			/* check for existance of status & x-status headers */
260 			status = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "Status");
261 			if (status)
262 				flags = decode_status (status);
263 			xstatus = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "X-Status");
264 			if (xstatus)
265 				flags |= decode_status (xstatus);
266 		}
267 
268 		/* if we have an xev header, use it, else assign a new one */
269 		xev = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "X-Evolution");
270 		if (xev != NULL
271 		    && camel_local_summary_decode_x_evolution ((CamelLocalSummary *) summary, xev, mi) == 0) {
272 			uid = camel_message_info_get_uid (mi);
273 			d (printf ("found valid x-evolution: %s\n", uid));
274 			/* If one is there, it should be there already */
275 			info = camel_folder_summary_peek_loaded (summary, uid);
276 			if (info) {
277 				if ((camel_message_info_get_flags (info) & CAMEL_MESSAGE_FOLDER_NOTSEEN)) {
278 					camel_message_info_set_flags (info, CAMEL_MESSAGE_FOLDER_NOTSEEN, 0);
279 					g_clear_object (&mi);
280 					mi = info;
281 				} else {
282 					add = 7;
283 					d (printf ("seen '%s' before, adding anew\n", uid));
284 					g_clear_object (&info);
285 				}
286 			} else {
287 				add = 2;
288 				d (printf ("but isn't present in summary\n"));
289 			}
290 		} else {
291 			d (printf ("didn't find x-evolution\n"));
292 			add = 7;
293 		}
294 
295 		if ((add & 1) != 0) {
296 			gchar *new_uid = camel_folder_summary_next_uid_string (summary);
297 
298 			camel_message_info_set_flags (mi, CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV, CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV);
299 			camel_message_info_set_uid (mi, new_uid);
300 
301 			g_free (new_uid);
302 		} else {
303 			camel_folder_summary_set_next_uid (summary, strtoul (camel_message_info_get_uid (mi), NULL, 10));
304 		}
305 
306 		if (mbs->xstatus && (add & 2) != 0) {
307 			/* use the status as the flags when we read it the first time */
308 			if (status)
309 				camel_message_info_set_flags (mi, STATUS_STATUS, flags);
310 			if (xstatus)
311 				camel_message_info_set_flags (mi, STATUS_XSTATUS, flags);
312 		}
313 
314 		if (mbs->changes) {
315 			if (add&2)
316 				camel_folder_change_info_add_uid (mbs->changes, camel_message_info_get_uid (mi));
317 			if ((add&4) && status == NULL)
318 				camel_folder_change_info_recent_uid (mbs->changes, camel_message_info_get_uid (mi));
319 		}
320 
321 		camel_mbox_message_info_set_offset (CAMEL_MBOX_MESSAGE_INFO (mi), -1);
322 	}
323 
324 	return (CamelMessageInfo *) mi;
325 }
326 
327 static CamelMessageInfo *
message_info_new_from_parser(CamelFolderSummary * s,CamelMimeParser * mp)328 message_info_new_from_parser (CamelFolderSummary *s,
329                               CamelMimeParser *mp)
330 {
331 	CamelMessageInfo *mi;
332 
333 	mi = CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_parent_class)->message_info_new_from_parser (s, mp);
334 	if (mi) {
335 		camel_mbox_message_info_set_offset (CAMEL_MBOX_MESSAGE_INFO (mi), camel_mime_parser_tell_start_from (mp));
336 	}
337 
338 	return mi;
339 }
340 
341 /* like summary_rebuild, but also do changeinfo stuff (if supplied) */
342 static gint
summary_update(CamelLocalSummary * cls,goffset offset,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)343 summary_update (CamelLocalSummary *cls,
344                 goffset offset,
345                 CamelFolderChangeInfo *changeinfo,
346                 GCancellable *cancellable,
347                 GError **error)
348 {
349 	gint i;
350 	CamelFolderSummary *s = (CamelFolderSummary *) cls;
351 	CamelMboxSummary *mbs = (CamelMboxSummary *) cls;
352 	CamelMimeParser *mp;
353 	CamelMessageInfo *mi;
354 	CamelStore *parent_store;
355 	const gchar *full_name;
356 	gint fd;
357 	gint ok = 0;
358 	struct stat st;
359 	goffset size = 0;
360 	GList *del = NULL;
361 	GPtrArray *known_uids;
362 
363 	d (printf ("Calling summary update, from pos %d\n", (gint) offset));
364 
365 	cls->index_force = FALSE;
366 
367 	camel_operation_push_message (cancellable, _("Storing folder"));
368 
369 	camel_folder_summary_lock (s);
370 	fd = g_open (cls->folder_path, O_LARGEFILE | O_RDONLY | O_BINARY, 0);
371 	if (fd == -1) {
372 		d (printf ("%s failed to open: %s\n", cls->folder_path, g_strerror (errno)));
373 		camel_folder_summary_unlock (s);
374 		g_set_error (
375 			error, G_IO_ERROR,
376 			g_io_error_from_errno (errno),
377 			_("Could not open folder: %s: %s"),
378 			cls->folder_path, g_strerror (errno));
379 		camel_operation_pop_message (cancellable);
380 		return -1;
381 	}
382 
383 	if (fstat (fd, &st) == 0)
384 		size = st.st_size;
385 
386 	mp = camel_mime_parser_new ();
387 	camel_mime_parser_init_with_fd (mp, fd);
388 	camel_mime_parser_scan_from (mp, TRUE);
389 	camel_mime_parser_seek (mp, offset, SEEK_SET);
390 
391 	if (offset > 0) {
392 		if (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM
393 		    && camel_mime_parser_tell_start_from (mp) == offset) {
394 			camel_mime_parser_unstep (mp);
395 		} else {
396 			g_warning ("The next message didn't start where I expected, building summary from start");
397 			camel_mime_parser_drop_step (mp);
398 			offset = 0;
399 			camel_mime_parser_seek (mp, offset, SEEK_SET);
400 		}
401 	}
402 
403 	/* we mark messages as to whether we've seen them or not.
404 	 * If we're not starting from the start, we must be starting
405 	 * from the old end, so everything must be treated as new */
406 	camel_folder_summary_prepare_fetch_all (s, NULL);
407 	known_uids = camel_folder_summary_get_array (s);
408 	for (i = 0; known_uids && i < known_uids->len; i++) {
409 		mi = camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
410 		camel_message_info_set_flags (mi, CAMEL_MESSAGE_FOLDER_NOTSEEN, offset == 0 ? CAMEL_MESSAGE_FOLDER_NOTSEEN : 0);
411 		g_clear_object (&mi);
412 	}
413 	camel_folder_summary_free_array (known_uids);
414 	mbs->changes = changeinfo;
415 
416 	while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
417 		CamelMessageInfo *info;
418 		goffset pc = camel_mime_parser_tell_start_from (mp) + 1;
419 		gint progress;
420 
421 		/* To avoid a division by zero if the fstat()
422 		 * call above failed. */
423 		size = MAX (size, pc);
424 		progress = (size > 0) ? (gint) (((gfloat) pc / size) * 100) : 0;
425 
426 		camel_operation_progress (cancellable, progress);
427 
428 		info = camel_folder_summary_info_new_from_parser (s, mp);
429 		camel_folder_summary_add (s, info, FALSE);
430 		g_clear_object (&info);
431 
432 		g_warn_if_fail (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM_END);
433 	}
434 
435 	g_object_unref (mp);
436 
437 	known_uids = camel_folder_summary_get_array (s);
438 	for (i = 0; known_uids && i < known_uids->len; i++) {
439 		const gchar *uid;
440 
441 		uid = g_ptr_array_index (known_uids, i);
442 		if (!uid)
443 			continue;
444 
445 		mi = camel_folder_summary_get (s, uid);
446 		/* must've dissapeared from the file? */
447 		if (!mi || (camel_message_info_get_flags (mi) & CAMEL_MESSAGE_FOLDER_NOTSEEN) != 0) {
448 			d (printf ("uid '%s' vanished, removing", uid));
449 			if (changeinfo)
450 				camel_folder_change_info_remove_uid (changeinfo, uid);
451 			del = g_list_prepend (del, (gpointer) camel_pstring_strdup (uid));
452 			if (mi)
453 				camel_folder_summary_remove (s, mi);
454 		}
455 
456 		g_clear_object (&mi);
457 	}
458 
459 	if (known_uids)
460 		camel_folder_summary_free_array (known_uids);
461 
462 	/* Delete all in one transaction */
463 	full_name = camel_folder_get_full_name (camel_folder_summary_get_folder (s));
464 	parent_store = camel_folder_get_parent_store (camel_folder_summary_get_folder (s));
465 	camel_db_delete_uids (camel_store_get_db (parent_store), full_name, del, NULL);
466 	g_list_foreach (del, (GFunc) camel_pstring_free, NULL);
467 	g_list_free (del);
468 
469 	mbs->changes = NULL;
470 
471 	/* update the file size/mtime in the summary */
472 	if (ok != -1) {
473 		if (g_stat (cls->folder_path, &st) == 0) {
474 			camel_folder_summary_touch (s);
475 			mbs->folder_size = st.st_size;
476 			camel_folder_summary_set_timestamp (s, st.st_mtime);
477 		}
478 	}
479 
480 	camel_operation_pop_message (cancellable);
481 	camel_folder_summary_unlock (s);
482 
483 	return ok;
484 }
485 
486 static gint
mbox_summary_check(CamelLocalSummary * cls,CamelFolderChangeInfo * changes,GCancellable * cancellable,GError ** error)487 mbox_summary_check (CamelLocalSummary *cls,
488                     CamelFolderChangeInfo *changes,
489                     GCancellable *cancellable,
490                     GError **error)
491 {
492 	CamelMboxSummary *mbs = (CamelMboxSummary *) cls;
493 	CamelFolderSummary *s = (CamelFolderSummary *) cls;
494 	struct stat st;
495 	gint ret = 0;
496 	gint i;
497 
498 	d (printf ("Checking summary\n"));
499 
500 	camel_folder_summary_lock (s);
501 
502 	/* check if the summary is up-to-date */
503 	if (g_stat (cls->folder_path, &st) == -1) {
504 		camel_folder_summary_clear (s, NULL);
505 		camel_folder_summary_unlock (s);
506 		g_set_error (
507 			error, G_IO_ERROR,
508 			g_io_error_from_errno (errno),
509 			_("Cannot check folder: %s: %s"),
510 			cls->folder_path, g_strerror (errno));
511 		return -1;
512 	}
513 
514 	if (cls->check_force)
515 		mbs->folder_size = 0;
516 	cls->check_force = 0;
517 
518 	if (st.st_size == 0) {
519 		GPtrArray *known_uids;
520 
521 		/* empty?  No need to scan at all */
522 		d (printf ("Empty mbox, clearing summary\n"));
523 		camel_folder_summary_prepare_fetch_all (s, NULL);
524 		known_uids = camel_folder_summary_get_array (s);
525 		for (i = 0; known_uids && i < known_uids->len; i++) {
526 			CamelMessageInfo *info = camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
527 
528 			if (info) {
529 				camel_folder_change_info_remove_uid (changes, camel_message_info_get_uid (info));
530 				g_clear_object (&info);
531 			}
532 		}
533 		camel_folder_summary_free_array (known_uids);
534 		camel_folder_summary_clear (s, NULL);
535 		ret = 0;
536 	} else {
537 		/* is the summary uptodate? */
538 		if (st.st_size != mbs->folder_size || st.st_mtime != camel_folder_summary_get_timestamp (s)) {
539 			if (mbs->folder_size < st.st_size) {
540 				/* this will automatically rescan from 0 if there is a problem */
541 				d (printf ("folder grew, attempting to rebuild from %d\n", mbs->folder_size));
542 				ret = summary_update (cls, mbs->folder_size, changes, cancellable, error);
543 			} else {
544 				d (printf ("folder shrank!  rebuilding from start\n"));
545 				 ret = summary_update (cls, 0, changes, cancellable, error);
546 			}
547 		} else {
548 			d (printf ("Folder unchanged, do nothing\n"));
549 		}
550 	}
551 
552 	/* FIXME: move upstream? */
553 
554 	if (ret != -1) {
555 		if (mbs->folder_size != st.st_size || camel_folder_summary_get_timestamp (s) != st.st_mtime) {
556 			mbs->folder_size = st.st_size;
557 			camel_folder_summary_set_timestamp (s, st.st_mtime);
558 			camel_folder_summary_touch (s);
559 		}
560 	}
561 
562 	camel_folder_summary_unlock (s);
563 
564 	return ret;
565 }
566 
567 /* perform a full sync */
568 static gint
mbox_summary_sync_full(CamelMboxSummary * mbs,gboolean expunge,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)569 mbox_summary_sync_full (CamelMboxSummary *mbs,
570                         gboolean expunge,
571                         CamelFolderChangeInfo *changeinfo,
572                         GCancellable *cancellable,
573                         GError **error)
574 {
575 	CamelLocalSummary *cls = (CamelLocalSummary *) mbs;
576 	CamelFolderSummary *s = CAMEL_FOLDER_SUMMARY (mbs);
577 	gint fd = -1, fdout = -1;
578 	gchar *tmpname = NULL;
579 	gsize tmpname_len = 0;
580 	guint32 flags = (expunge ? 1 : 0), filemode = 0600;
581 	struct stat st;
582 
583 	d (printf ("performing full summary/sync\n"));
584 
585 	camel_operation_push_message (cancellable, _("Storing folder"));
586 	camel_folder_summary_lock (s);
587 
588 	fd = g_open (cls->folder_path, O_LARGEFILE | O_RDONLY | O_BINARY, 0);
589 	if (fd == -1) {
590 		camel_folder_summary_unlock (s);
591 		g_set_error (
592 			error, G_IO_ERROR,
593 			g_io_error_from_errno (errno),
594 			_("Could not open file: %s: %s"),
595 			cls->folder_path, g_strerror (errno));
596 		camel_operation_pop_message (cancellable);
597 		return -1;
598 	}
599 
600 	/* preserve attributes as set on the file previously */
601 	if (fstat (fd, &st) == 0)
602 		filemode = 07777 & st.st_mode;
603 
604 	tmpname_len = strlen (cls->folder_path) + 5;
605 	tmpname = g_alloca (tmpname_len);
606 	g_snprintf (tmpname, tmpname_len, "%s.tmp", cls->folder_path);
607 	d (printf ("Writing temporary file to %s\n", tmpname));
608 	fdout = g_open (tmpname, O_LARGEFILE | O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, filemode);
609 	if (fdout == -1) {
610 		g_set_error (
611 			error, G_IO_ERROR,
612 			g_io_error_from_errno (errno),
613 			_("Cannot open temporary mailbox: %s"),
614 			g_strerror (errno));
615 		goto error;
616 	}
617 
618 	if (camel_mbox_summary_sync_mbox (
619 		(CamelMboxSummary *) cls, flags, changeinfo,
620 		fd, fdout, cancellable, error) == -1)
621 		goto error;
622 
623 	d (printf ("Closing folders\n"));
624 
625 	if (close (fd) == -1) {
626 		g_warning ("Cannot close source folder: %s", g_strerror (errno));
627 		g_set_error (
628 			error, G_IO_ERROR,
629 			g_io_error_from_errno (errno),
630 			_("Could not close source folder %s: %s"),
631 			cls->folder_path, g_strerror (errno));
632 		fd = -1;
633 		goto error;
634 	}
635 
636 	fd = -1;
637 
638 	if (close (fdout) == -1) {
639 		g_warning ("Cannot close temporary folder: %s", g_strerror (errno));
640 		g_set_error (
641 			error, G_IO_ERROR,
642 			g_io_error_from_errno (errno),
643 			_("Could not close temporary folder: %s"),
644 			g_strerror (errno));
645 		fdout = -1;
646 		goto error;
647 	}
648 
649 	fdout = -1;
650 
651 	/* this should probably either use unlink/link/unlink, or recopy over
652 	 * the original mailbox, for various locking reasons/etc */
653 #ifdef G_OS_WIN32
654 	if (g_file_test (cls->folder_path,G_FILE_TEST_IS_REGULAR) && g_remove (cls->folder_path) == -1)
655 		g_warning ("Cannot remove %s: %s", cls->folder_path, g_strerror (errno));
656 #endif
657 	if (g_rename (tmpname, cls->folder_path) == -1) {
658 		g_warning ("Cannot rename folder: %s", g_strerror (errno));
659 		g_set_error (
660 			error, G_IO_ERROR,
661 			g_io_error_from_errno (errno),
662 			_("Could not rename folder: %s"),
663 			g_strerror (errno));
664 		goto error;
665 	}
666 
667 	camel_operation_pop_message (cancellable);
668 	camel_folder_summary_unlock (s);
669 
670 	return 0;
671  error:
672 	if (fd != -1)
673 		close (fd);
674 
675 	if (fdout != -1)
676 		close (fdout);
677 
678 	g_unlink (tmpname);
679 
680 	camel_operation_pop_message (cancellable);
681 	camel_folder_summary_unlock (s);
682 
683 	return -1;
684 }
685 
686 static gint
cms_sort_frompos(gconstpointer a,gconstpointer b,gpointer data)687 cms_sort_frompos (gconstpointer a,
688                   gconstpointer b,
689                   gpointer data)
690 {
691 	CamelFolderSummary *summary = (CamelFolderSummary *) data;
692 	CamelMboxMessageInfo *info1, *info2;
693 	goffset afrompos, bfrompos;
694 	gint ret = 0;
695 
696 	/* Things are in memory already. Sorting speeds up syncing, if things are sorted by from pos. */
697 	info1 = (CamelMboxMessageInfo *) camel_folder_summary_get (summary, *(gchar **) a);
698 	info2 = (CamelMboxMessageInfo *) camel_folder_summary_get (summary, *(gchar **) b);
699 
700 	afrompos = camel_mbox_message_info_get_offset (info1);
701 	bfrompos = camel_mbox_message_info_get_offset (info2);
702 
703 	if (afrompos > bfrompos)
704 		ret = 1;
705 	else if  (afrompos < bfrompos)
706 		ret = -1;
707 	else
708 		ret = 0;
709 
710 	g_clear_object (&info1);
711 	g_clear_object (&info2);
712 
713 	return ret;
714 
715 }
716 
717 /* perform a quick sync - only system flags have changed */
718 static gint
mbox_summary_sync_quick(CamelMboxSummary * mbs,gboolean expunge,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)719 mbox_summary_sync_quick (CamelMboxSummary *mbs,
720                          gboolean expunge,
721                          CamelFolderChangeInfo *changeinfo,
722                          GCancellable *cancellable,
723                          GError **error)
724 {
725 	CamelLocalSummary *cls = (CamelLocalSummary *) mbs;
726 	CamelFolderSummary *s = (CamelFolderSummary *) mbs;
727 	CamelMimeParser *mp = NULL;
728 	gint i;
729 	CamelMessageInfo *info = NULL;
730 	gint fd = -1, pfd;
731 	gchar *xevnew, *xevtmp;
732 	const gchar *xev;
733 	gint len;
734 	goffset lastpos;
735 	GPtrArray *summary = NULL;
736 
737 	d (printf ("Performing quick summary sync\n"));
738 
739 	camel_operation_push_message (cancellable, _("Storing folder"));
740 	camel_folder_summary_lock (s);
741 
742 	fd = g_open (cls->folder_path, O_LARGEFILE | O_RDWR | O_BINARY, 0);
743 	if (fd == -1) {
744 		camel_folder_summary_unlock (s);
745 		g_set_error (
746 			error, G_IO_ERROR,
747 			g_io_error_from_errno (errno),
748 			_("Could not open file: %s: %s"),
749 			cls->folder_path, g_strerror (errno));
750 
751 		camel_operation_pop_message (cancellable);
752 		return -1;
753 	}
754 
755 	/* need to dup since mime parser closes its fd once it is finalized */
756 	pfd = dup (fd);
757 	if (pfd == -1) {
758 		camel_folder_summary_unlock (s);
759 		g_set_error (
760 			error, G_IO_ERROR,
761 			g_io_error_from_errno (errno),
762 			_("Could not store folder: %s"),
763 			g_strerror (errno));
764 		close (fd);
765 		return -1;
766 	}
767 
768 	mp = camel_mime_parser_new ();
769 	camel_mime_parser_scan_from (mp, TRUE);
770 	camel_mime_parser_scan_pre_from (mp, TRUE);
771 	camel_mime_parser_init_with_fd (mp, pfd);
772 
773 	/* Sync only the changes */
774 	summary = camel_folder_summary_get_changed ((CamelFolderSummary *) mbs);
775 	if (summary->len)
776 		g_ptr_array_sort_with_data (summary, cms_sort_frompos, mbs);
777 
778 	for (i = 0; i < summary->len; i++) {
779 		goffset frompos;
780 		gint xevoffset;
781 		gint pc = (i + 1) * 100 / summary->len;
782 
783 		camel_operation_progress (cancellable, pc);
784 
785 		info = camel_folder_summary_get (s, summary->pdata[i]);
786 
787 		d (printf ("Checking message %s %08x\n", camel_message_info_get_uid (info), camel_message_info_get_flags (info)));
788 
789 		if (!camel_message_info_get_folder_flagged (info)) {
790 			g_clear_object (&info);
791 			continue;
792 		}
793 
794 		frompos = camel_mbox_message_info_get_offset (CAMEL_MBOX_MESSAGE_INFO (info));
795 
796 		d (printf ("Updating message %s: %d\n", camel_message_info_get_uid (info), (gint) frompos));
797 
798 		camel_mime_parser_seek (mp, frompos, SEEK_SET);
799 
800 		if (camel_mime_parser_step (mp, NULL, NULL) != CAMEL_MIME_PARSER_STATE_FROM) {
801 			g_set_error (
802 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
803 				_("MBOX file is corrupted, please fix it. (Expected a From line, but didn’t get it.)"));
804 			goto error;
805 		}
806 
807 		if (camel_mime_parser_tell_start_from (mp) != frompos) {
808 			g_warning (
809 				"Didn't get the next message where I expected (%d) got %d instead",
810 				(gint) frompos, (gint) camel_mime_parser_tell_start_from (mp));
811 			g_set_error (
812 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
813 				_("Summary and folder mismatch, even after a sync"));
814 			goto error;
815 		}
816 
817 		if (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM_END) {
818 			g_warning ("camel_mime_parser_step failed (2)");
819 			goto error;
820 		}
821 
822 		xev = camel_mime_parser_header (mp, "X-Evolution", &xevoffset);
823 		if (xev == NULL || camel_local_summary_decode_x_evolution (cls, xev, NULL) == -1) {
824 			g_warning ("We're supposed to have a valid x-ev header, but we dont");
825 			goto error;
826 		}
827 		xevnew = camel_local_summary_encode_x_evolution (cls, info);
828 		/* SIGH: encode_param_list is about the only function which folds headers by itself.
829 		 * This should be fixed somehow differently (either parser doesn't fold headers,
830 		 * or param_list doesn't, or something */
831 		xevtmp = camel_header_unfold (xevnew);
832 		/* the raw header contains a leading ' ', so (dis)count that too */
833 		if (strlen (xev) - 1 != strlen (xevtmp)) {
834 			g_free (xevnew);
835 			g_free (xevtmp);
836 			g_warning ("Hmm, the xev headers shouldn't have changed size, but they did");
837 			goto error;
838 		}
839 		g_free (xevtmp);
840 
841 		/* we write out the xevnew string, assuming its been folded identically to the original too! */
842 
843 		lastpos = lseek (fd, 0, SEEK_CUR);
844 		CHECK_CALL (lseek (fd, xevoffset + strlen ("X-Evolution: "), SEEK_SET));
845 		do {
846 			len = write (fd, xevnew, strlen (xevnew));
847 		} while (len == -1 && errno == EINTR);
848 
849 		if (lastpos != -1 && lseek (fd, lastpos, SEEK_SET) == (off_t) -1) {
850 			g_warning (
851 				"%s: Failed to rewind file to last position: %s",
852 				G_STRFUNC, g_strerror (errno));
853 		}
854 		g_free (xevnew);
855 
856 		camel_mime_parser_drop_step (mp);
857 		camel_mime_parser_drop_step (mp);
858 
859 		camel_message_info_set_flags (info, 0xffff, camel_message_info_get_flags (info));
860 		g_clear_object (&info);
861 	}
862 
863 	d (printf ("Closing folders\n"));
864 
865 	if (close (fd) == -1) {
866 		g_warning ("Cannot close source folder: %s", g_strerror (errno));
867 		g_set_error (
868 			error, G_IO_ERROR,
869 			g_io_error_from_errno (errno),
870 			_("Could not close source folder %s: %s"),
871 			cls->folder_path, g_strerror (errno));
872 		fd = -1;
873 		goto error;
874 	}
875 
876 	g_ptr_array_foreach (summary, (GFunc) camel_pstring_free, NULL);
877 	g_ptr_array_free (summary, TRUE);
878 	g_object_unref (mp);
879 
880 	camel_operation_pop_message (cancellable);
881 	camel_folder_summary_unlock (s);
882 
883 	return 0;
884  error:
885 	g_ptr_array_foreach (summary, (GFunc) camel_pstring_free, NULL);
886 	g_ptr_array_free (summary, TRUE);
887 	g_object_unref (mp);
888 	if (fd != -1)
889 		close (fd);
890 	g_clear_object (&info);
891 
892 	camel_operation_pop_message (cancellable);
893 	camel_folder_summary_unlock (s);
894 
895 	return -1;
896 }
897 
898 static gint
mbox_summary_sync(CamelLocalSummary * cls,gboolean expunge,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)899 mbox_summary_sync (CamelLocalSummary *cls,
900                    gboolean expunge,
901                    CamelFolderChangeInfo *changeinfo,
902                    GCancellable *cancellable,
903                    GError **error)
904 {
905 	struct stat st;
906 	CamelMboxSummary *mbs = (CamelMboxSummary *) cls;
907 	CamelFolderSummary *s = (CamelFolderSummary *) cls;
908 	CamelStore *parent_store;
909 	const gchar *full_name;
910 	gint i;
911 	gint quick = TRUE, work = FALSE;
912 	gint ret;
913 	GPtrArray *summary = NULL;
914 
915 	camel_folder_summary_lock (s);
916 
917 	/* first, sync ourselves up, just to make sure */
918 	if (camel_local_summary_check (cls, changeinfo, cancellable, error) == -1) {
919 		camel_folder_summary_unlock (s);
920 		return -1;
921 	}
922 
923 	full_name = camel_folder_get_full_name (camel_folder_summary_get_folder (s));
924 	parent_store = camel_folder_get_parent_store (camel_folder_summary_get_folder (s));
925 
926 	/* Sync only the changes */
927 
928 	summary = camel_folder_summary_get_changed ((CamelFolderSummary *) mbs);
929 	for (i = 0; i < summary->len; i++) {
930 		CamelMessageInfo *info = camel_folder_summary_get (s, summary->pdata[i]);
931 
932 		if ((expunge && (camel_message_info_get_flags (info) & CAMEL_MESSAGE_DELETED) != 0) ||
933 		    (camel_message_info_get_flags (info) & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_XEVCHANGE)) != 0)
934 			quick = FALSE;
935 		else
936 			work |= camel_message_info_get_folder_flagged (info);
937 		g_clear_object (&info);
938 	}
939 
940 	g_ptr_array_foreach (summary, (GFunc) camel_pstring_free, NULL);
941 	g_ptr_array_free (summary, TRUE);
942 
943 	if (quick && expunge) {
944 		guint32 dcount =0;
945 
946 		if (camel_db_count_deleted_message_info (camel_store_get_db (parent_store), full_name, &dcount, error) == -1) {
947 			camel_folder_summary_unlock (s);
948 			return -1;
949 		}
950 		if (dcount)
951 			quick = FALSE;
952 	}
953 
954 	/* yuck i hate this logic, but its to simplify the 'all ok, update summary' and failover cases */
955 	ret = -1;
956 	if (quick) {
957 		if (work) {
958 			ret = CAMEL_MBOX_SUMMARY_GET_CLASS (cls)->sync_quick (
959 				mbs, expunge, changeinfo, cancellable, NULL);
960 			if (ret == -1)
961 				g_warning ("failed a quick-sync, trying a full sync");
962 		} else {
963 			ret = 0;
964 		}
965 	}
966 
967 	if (ret == -1)
968 		ret = CAMEL_MBOX_SUMMARY_GET_CLASS (cls)->sync_full (
969 			mbs, expunge, changeinfo, cancellable, error);
970 	if (ret == -1) {
971 		camel_folder_summary_unlock (s);
972 		return -1;
973 	}
974 
975 	if (g_stat (cls->folder_path, &st) == -1) {
976 		g_set_error (
977 			error, G_IO_ERROR,
978 			g_io_error_from_errno (errno),
979 			_("Unknown error: %s"), g_strerror (errno));
980 		camel_folder_summary_unlock (s);
981 		return -1;
982 	}
983 
984 	if (mbs->folder_size != st.st_size || camel_folder_summary_get_timestamp (s) != st.st_mtime) {
985 		camel_folder_summary_set_timestamp (s, st.st_mtime);
986 		mbs->folder_size = st.st_size;
987 		camel_folder_summary_touch (s);
988 	}
989 
990 	ret = CAMEL_LOCAL_SUMMARY_CLASS (camel_mbox_summary_parent_class)->sync (cls, expunge, changeinfo, cancellable, error);
991 	camel_folder_summary_unlock (s);
992 
993 	return ret;
994 }
995 
996 gint
camel_mbox_summary_sync_mbox(CamelMboxSummary * cls,guint32 flags,CamelFolderChangeInfo * changeinfo,gint fd,gint fdout,GCancellable * cancellable,GError ** error)997 camel_mbox_summary_sync_mbox (CamelMboxSummary *cls,
998                               guint32 flags,
999                               CamelFolderChangeInfo *changeinfo,
1000                               gint fd,
1001                               gint fdout,
1002                               GCancellable *cancellable,
1003                               GError **error)
1004 {
1005 	CamelMboxSummary *mbs = (CamelMboxSummary *) cls;
1006 	CamelFolderSummary *s = (CamelFolderSummary *) mbs;
1007 	CamelMimeParser *mp = NULL;
1008 	CamelStore *parent_store;
1009 	const gchar *full_name;
1010 	gint i;
1011 	CamelMessageInfo *info = NULL;
1012 	gchar *buffer, *xevnew = NULL;
1013 	gsize len;
1014 	const gchar *fromline;
1015 	gint lastdel = FALSE;
1016 	gboolean touched = FALSE;
1017 	GList *del = NULL;
1018 	GPtrArray *known_uids = NULL;
1019 	gchar statnew[8], xstatnew[8];
1020 
1021 	d (printf ("performing full summary/sync\n"));
1022 
1023 	camel_folder_summary_lock (s);
1024 
1025 	/* need to dup this because the mime-parser owns the fd after we give it to it */
1026 	fd = dup (fd);
1027 	if (fd == -1) {
1028 		camel_folder_summary_unlock (s);
1029 		g_set_error (
1030 			error, G_IO_ERROR,
1031 			g_io_error_from_errno (errno),
1032 			_("Could not store folder: %s"),
1033 			g_strerror (errno));
1034 		return -1;
1035 	}
1036 
1037 	mp = camel_mime_parser_new ();
1038 	camel_mime_parser_scan_from (mp, TRUE);
1039 	camel_mime_parser_scan_pre_from (mp, TRUE);
1040 	camel_mime_parser_init_with_fd (mp, fd);
1041 
1042 	camel_folder_summary_prepare_fetch_all (s, NULL);
1043 	known_uids = camel_folder_summary_get_array (s);
1044 	/* walk them in the same order as stored in the file */
1045 	if (known_uids && known_uids->len)
1046 		g_ptr_array_sort_with_data (known_uids, cms_sort_frompos, mbs);
1047 	for (i = 0; known_uids && i < known_uids->len; i++) {
1048 		gint pc = (i + 1) * 100 / known_uids->len;
1049 		goffset frompos;
1050 
1051 		camel_operation_progress (cancellable, pc);
1052 
1053 		info = camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
1054 
1055 		if (!info)
1056 			continue;
1057 
1058 		d (printf (
1059 			"Looking at message %s\n",
1060 			camel_message_info_get_uid (info)));
1061 
1062 		frompos = camel_mbox_message_info_get_offset (CAMEL_MBOX_MESSAGE_INFO (info));
1063 
1064 		d (printf (
1065 			"seeking (%s) to %d\n",
1066 			camel_message_info_get_uid (info),
1067 			(gint) frompos));
1068 
1069 		if (lastdel)
1070 			camel_mime_parser_seek (mp, frompos, SEEK_SET);
1071 
1072 		if (camel_mime_parser_step (mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM) {
1073 			g_set_error (
1074 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1075 				_("MBOX file is corrupted, please fix it. "
1076 				"(Expected a From line, but didn’t get it.)"));
1077 			goto error;
1078 		}
1079 
1080 		if (camel_mime_parser_tell_start_from (mp) != frompos) {
1081 			g_warning (
1082 				"Didn't get the next message where I expected (%d) got %d instead",
1083 				(gint) frompos,
1084 				(gint) camel_mime_parser_tell_start_from (mp));
1085 			g_set_error (
1086 				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1087 				_("Summary and folder mismatch, even after a sync"));
1088 			goto error;
1089 		}
1090 
1091 		lastdel = FALSE;
1092 		if ((flags & 1) && (camel_message_info_get_flags (info) & CAMEL_MESSAGE_DELETED) != 0) {
1093 			const gchar *uid = camel_message_info_get_uid (info);
1094 			d (printf ("Deleting %s\n", uid));
1095 
1096 			if (((CamelLocalSummary *) cls)->index)
1097 				camel_index_delete_name (((CamelLocalSummary *) cls)->index, uid);
1098 
1099 			/* remove it from the change list */
1100 			camel_folder_change_info_remove_uid (changeinfo, uid);
1101 			camel_folder_summary_remove (s, (CamelMessageInfo *) info);
1102 			del = g_list_prepend (del, (gpointer) camel_pstring_strdup (uid));
1103 			g_clear_object (&info);
1104 			lastdel = TRUE;
1105 			touched = TRUE;
1106 		} else {
1107 			/* otherwise, the message is staying, copy its From_ line across */
1108 #if 0
1109 			if (i > 0)
1110 				write (fdout, "\n", 1);
1111 #endif
1112 			frompos = lseek (fdout, 0, SEEK_CUR);
1113 			camel_mbox_message_info_set_offset (CAMEL_MBOX_MESSAGE_INFO (info), frompos);
1114 			camel_message_info_set_dirty (info, TRUE);
1115 			fromline = camel_mime_parser_from_line (mp);
1116 			d (printf ("Saving %s:%d\n", camel_message_info_get_uid (info), frompos));
1117 			g_warn_if_fail (write (fdout, fromline, strlen (fromline)) != -1);
1118 		}
1119 
1120 		if (info && (camel_message_info_get_flags (info) & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) != 0) {
1121 			CamelNameValueArray *header = NULL;
1122 			d (printf ("Updating header for %s flags = %08x\n", camel_message_info_get_uid (info), camel_message_info_get_flags (info)));
1123 
1124 			if (camel_mime_parser_step (mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM_END) {
1125 				g_warning ("camel_mime_parser_step failed (2)");
1126 				goto error;
1127 			}
1128 
1129 			header = camel_mime_parser_dup_headers (mp);
1130 			xevnew = camel_local_summary_encode_x_evolution ((CamelLocalSummary *) cls, info);
1131 			if (mbs->xstatus) {
1132 				guint32 info_flags = camel_message_info_get_flags (info);
1133 
1134 				encode_status (info_flags & STATUS_STATUS, statnew);
1135 				encode_status (info_flags & STATUS_XSTATUS, xstatnew);
1136 
1137 				len = camel_local_summary_write_headers (fdout, header, xevnew, statnew, xstatnew);
1138 			} else {
1139 				len = camel_local_summary_write_headers (fdout, header, xevnew, NULL, NULL);
1140 			}
1141 
1142 			camel_name_value_array_free (header);
1143 			if (len == -1) {
1144 				d (printf ("Error writing to temporary mailbox\n"));
1145 				g_set_error (
1146 					error, G_IO_ERROR,
1147 					g_io_error_from_errno (errno),
1148 					_("Writing to temporary mailbox failed: %s"),
1149 					g_strerror (errno));
1150 				goto error;
1151 			}
1152 			camel_message_info_set_flags (info, 0xffff, camel_message_info_get_flags (info));
1153 			g_free (xevnew);
1154 			xevnew = NULL;
1155 			camel_mime_parser_drop_step (mp);
1156 		}
1157 
1158 		camel_mime_parser_drop_step (mp);
1159 		if (info) {
1160 			d (printf ("looking for message content to copy across from %d\n", (gint) camel_mime_parser_tell (mp)));
1161 			while (camel_mime_parser_step (mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_PRE_FROM) {
1162 				/*d(printf("copying mbox contents to temporary: '%.*s'\n", len, buffer));*/
1163 				if (write (fdout, buffer, len) != len) {
1164 					g_set_error (
1165 						error, G_IO_ERROR,
1166 						g_io_error_from_errno (errno),
1167 						_("Writing to temporary mailbox failed: %s: %s"),
1168 						((CamelLocalSummary *) cls)->folder_path,
1169 						g_strerror (errno));
1170 					goto error;
1171 				}
1172 			}
1173 
1174 			if (write (fdout, "\n", 1) != 1) {
1175 				g_set_error (
1176 					error, G_IO_ERROR,
1177 					g_io_error_from_errno (errno),
1178 					_("Writing to temporary mailbox failed: %s"),
1179 					g_strerror (errno));
1180 				goto error;
1181 			}
1182 
1183 			d (printf (
1184 				"we are now at %d, from = %d\n",
1185 				(gint) camel_mime_parser_tell (mp),
1186 				(gint) camel_mime_parser_tell_start_from (mp)));
1187 			camel_mime_parser_unstep (mp);
1188 			g_clear_object (&info);
1189 		}
1190 	}
1191 
1192 	full_name = camel_folder_get_full_name (camel_folder_summary_get_folder (s));
1193 	parent_store = camel_folder_get_parent_store (camel_folder_summary_get_folder (s));
1194 	camel_db_delete_uids (camel_store_get_db (parent_store), full_name, del, NULL);
1195 	g_list_foreach (del, (GFunc) camel_pstring_free, NULL);
1196 	g_list_free (del);
1197 
1198 #if 0
1199 	/* if last was deleted, append the \n we removed */
1200 	if (lastdel && count > 0)
1201 		write (fdout, "\n", 1);
1202 #endif
1203 
1204 	g_object_unref (mp);
1205 
1206 	/* clear working flags */
1207 	for (i = 0; known_uids && i < known_uids->len; i++) {
1208 		info = camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
1209 		if (info) {
1210 			camel_message_info_set_flags (info, CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_XEVCHANGE, 0);
1211 			g_clear_object (&info);
1212 		}
1213 	}
1214 
1215 	camel_folder_summary_free_array (known_uids);
1216 
1217 	if (touched)
1218 		camel_folder_summary_header_save (s, NULL);
1219 
1220 	camel_folder_summary_unlock (s);
1221 
1222 	return 0;
1223  error:
1224 	g_free (xevnew);
1225 	g_object_unref (mp);
1226 	g_clear_object (&info);
1227 
1228 	camel_folder_summary_free_array (known_uids);
1229 	camel_folder_summary_unlock (s);
1230 
1231 	return -1;
1232 }
1233 
1234 static CamelMessageInfo *
mbox_summary_add(CamelLocalSummary * cls,CamelMimeMessage * msg,const CamelMessageInfo * info,CamelFolderChangeInfo * ci,GError ** error)1235 mbox_summary_add (CamelLocalSummary *cls,
1236                   CamelMimeMessage *msg,
1237                   const CamelMessageInfo *info,
1238                   CamelFolderChangeInfo *ci,
1239                   GError **error)
1240 {
1241 	CamelLocalSummaryClass *local_summary_class;
1242 	CamelMessageInfo *mi;
1243 
1244 	/* Chain up to parent's add() method. */
1245 	local_summary_class = CAMEL_LOCAL_SUMMARY_CLASS (camel_mbox_summary_parent_class);
1246 	mi = local_summary_class->add (cls, msg, info, ci, error);
1247 	if (mi && ((CamelMboxSummary *) cls)->xstatus) {
1248 		gchar status[8];
1249 		guint32 flags = camel_message_info_get_flags (mi);
1250 
1251 		/* we snoop and add status/x-status headers to suit */
1252 		encode_status (flags & STATUS_STATUS, status);
1253 		camel_medium_set_header ((CamelMedium *) msg, "Status", status);
1254 		encode_status (flags & STATUS_XSTATUS, status);
1255 		camel_medium_set_header ((CamelMedium *) msg, "X-Status", status);
1256 	}
1257 
1258 	return mi;
1259 }
1260 
1261 static struct {
1262 	gchar tag;
1263 	guint32 flag;
1264 } status_flags[] = {
1265 	{ 'F', CAMEL_MESSAGE_FLAGGED },
1266 	{ 'A', CAMEL_MESSAGE_ANSWERED },
1267 	{ 'D', CAMEL_MESSAGE_DELETED },
1268 	{ 'R', CAMEL_MESSAGE_SEEN },
1269 };
1270 
1271 static void
encode_status(guint32 flags,gchar status[8])1272 encode_status (guint32 flags,
1273                gchar status[8])
1274 {
1275 	gsize i;
1276 	gchar *p;
1277 
1278 	p = status;
1279 	for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
1280 		if (status_flags[i].flag & flags)
1281 			*p++ = status_flags[i].tag;
1282 	*p++ = 'O';
1283 	*p = '\0';
1284 }
1285 
1286 static guint32
decode_status(const gchar * status)1287 decode_status (const gchar *status)
1288 {
1289 	const gchar *p;
1290 	guint32 flags = 0;
1291 	gsize i;
1292 	gchar c;
1293 
1294 	p = status;
1295 	while ((c = *p++)) {
1296 		for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
1297 			if (status_flags[i].tag == c)
1298 				flags |= status_flags[i].flag;
1299 	}
1300 
1301 	return flags;
1302 }
1303