1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
3 * Copyright (C) 2000 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
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.
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.
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.
33 #include <sys/types.h>
36 #include <glib/gi18n-lib.h>
37 #include <glib/gstdio.h>
39 #include "camel-file-utils.h"
40 #include "camel-mime-message.h"
41 #include "camel-operation.h"
42 #include "camel-private.h"
44 #include "camel-mbox-summary.h"
47 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
49 #define CAMEL_MBOX_SUMMARY_VERSION (1)
51 static int summary_header_load (CamelFolderSummary *, FILE *);
52 static int summary_header_save (CamelFolderSummary *, FILE *);
54 static CamelMessageInfo * message_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
55 static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *);
56 static CamelMessageInfo * message_info_load (CamelFolderSummary *, FILE *);
57 static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo *);
58 static int meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *mi);
59 /*static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);*/
61 static char *mbox_summary_encode_x_evolution (CamelLocalSummary *cls, const CamelLocalMessageInfo *mi);
63 static int mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
64 static int mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
66 static CamelMessageInfo *mbox_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex);
69 static int mbox_summary_sync_quick(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
70 static int mbox_summary_sync_full(CamelMboxSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
72 static void camel_mbox_summary_class_init (CamelMboxSummaryClass *klass);
73 static void camel_mbox_summary_init (CamelMboxSummary *obj);
74 static void camel_mbox_summary_finalise (CamelObject *obj);
77 /* Which status flags are stored in each separate header */
78 #define STATUS_XSTATUS (CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_ANSWERED|CAMEL_MESSAGE_DELETED)
79 #define STATUS_STATUS (CAMEL_MESSAGE_SEEN)
81 static void encode_status(guint32 flags, char status[8]);
82 static guint32 decode_status(const char *status);
85 static CamelLocalSummaryClass *camel_mbox_summary_parent;
88 camel_mbox_summary_get_type(void)
90 static CamelType type = CAMEL_INVALID_TYPE;
92 if (type == CAMEL_INVALID_TYPE) {
93 type = camel_type_register(camel_local_summary_get_type(), "CamelMboxSummary",
94 sizeof (CamelMboxSummary),
95 sizeof (CamelMboxSummaryClass),
96 (CamelObjectClassInitFunc) camel_mbox_summary_class_init,
98 (CamelObjectInitFunc) camel_mbox_summary_init,
99 (CamelObjectFinalizeFunc) camel_mbox_summary_finalise);
105 mbox_info_set_user_flag(CamelMessageInfo *mi, const char *name, gboolean value)
109 res = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->info_set_user_flag(mi, name, value);
111 ((CamelLocalMessageInfo *)mi)->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
117 mbox_info_set_user_tag(CamelMessageInfo *mi, const char *name, const char *value)
121 res = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->info_set_user_tag(mi, name, value);
123 ((CamelLocalMessageInfo *)mi)->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
130 mbox_info_set_flags(CamelMessageInfo *mi, guint32 flags, guint32 set)
132 /* Basically, if anything could change the Status line, presume it does */
133 if (((CamelMboxSummary *)mi->summary)->xstatus
134 && (flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_ANSWERED|CAMEL_MESSAGE_DELETED))) {
135 flags |= CAMEL_MESSAGE_FOLDER_XEVCHANGE|CAMEL_MESSAGE_FOLDER_FLAGGED;
136 set |= CAMEL_MESSAGE_FOLDER_XEVCHANGE|CAMEL_MESSAGE_FOLDER_FLAGGED;
139 return ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->info_set_flags(mi, flags, set);
144 camel_mbox_summary_class_init(CamelMboxSummaryClass *klass)
146 CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *)klass;
147 CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)klass;
149 camel_mbox_summary_parent = (CamelLocalSummaryClass *)camel_type_get_global_classfuncs(camel_local_summary_get_type());
151 sklass->summary_header_load = summary_header_load;
152 sklass->summary_header_save = summary_header_save;
154 sklass->message_info_new_from_header = message_info_new_from_header;
155 sklass->message_info_new_from_parser = message_info_new_from_parser;
156 sklass->message_info_load = message_info_load;
157 sklass->message_info_save = message_info_save;
158 sklass->meta_message_info_save = meta_message_info_save;
159 /*sklass->message_info_free = message_info_free;*/
161 sklass->info_set_user_flag = mbox_info_set_user_flag;
162 sklass->info_set_user_tag = mbox_info_set_user_tag;
164 sklass->info_set_flags = mbox_info_set_flags;
167 lklass->encode_x_evolution = mbox_summary_encode_x_evolution;
168 lklass->check = mbox_summary_check;
169 lklass->sync = mbox_summary_sync;
171 lklass->add = mbox_summary_add;
174 klass->sync_quick = mbox_summary_sync_quick;
175 klass->sync_full = mbox_summary_sync_full;
179 camel_mbox_summary_init(CamelMboxSummary *obj)
181 struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
183 /* subclasses need to set the right instance data sizes */
184 s->message_info_size = sizeof(CamelMboxMessageInfo);
185 s->content_info_size = sizeof(CamelMboxMessageContentInfo);
187 /* and a unique file version */
188 s->version += CAMEL_MBOX_SUMMARY_VERSION;
192 camel_mbox_summary_finalise(CamelObject *obj)
194 /*CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(obj);*/
198 * camel_mbox_summary_new:
200 * Create a new CamelMboxSummary object.
202 * Return value: A new CamelMboxSummary widget.
205 camel_mbox_summary_new(struct _CamelFolder *folder, const char *filename, const char *mbox_name, CamelIndex *index)
207 CamelMboxSummary *new = (CamelMboxSummary *)camel_object_new(camel_mbox_summary_get_type());
209 ((CamelFolderSummary *)new)->folder = folder;
211 camel_local_summary_construct((CamelLocalSummary *)new, filename, mbox_name, index);
215 void camel_mbox_summary_xstatus(CamelMboxSummary *mbs, int state)
217 mbs->xstatus = state;
221 mbox_summary_encode_x_evolution (CamelLocalSummary *cls, const CamelLocalMessageInfo *mi)
223 const char *p, *uidstr;
226 /* This is busted, it is supposed to encode ALL DATA */
227 p = uidstr = camel_message_info_uid(mi);
228 while (*p && isdigit(*p))
231 if (*p == 0 && sscanf(uidstr, "%u", &uid) == 1) {
232 return g_strdup_printf("%08x-%04x", uid, mi->info.flags & 0xffff);
234 return g_strdup_printf("%s-%04x", uidstr, mi->info.flags & 0xffff);
239 summary_header_load(CamelFolderSummary *s, FILE *in)
241 CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(s);
243 if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->summary_header_load(s, in) == -1)
247 if (s->version == 0x120c)
248 return camel_file_util_decode_uint32(in, (guint32 *) &mbs->folder_size);
251 if (camel_file_util_decode_fixed_int32(in, &mbs->version) == -1
252 || camel_file_util_decode_size_t(in, &mbs->folder_size) == -1)
259 summary_header_save(CamelFolderSummary *s, FILE *out)
261 CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(s);
263 if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->summary_header_save(s, out) == -1)
266 camel_file_util_encode_fixed_int32(out, CAMEL_MBOX_SUMMARY_VERSION);
268 return camel_file_util_encode_size_t(out, mbs->folder_size);
271 static CamelMessageInfo *
272 message_info_new_from_header(CamelFolderSummary *s, struct _camel_header_raw *h)
274 CamelMboxMessageInfo *mi;
275 CamelMboxSummary *mbs = (CamelMboxSummary *)s;
277 mi = (CamelMboxMessageInfo *)((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_new_from_header(s, h);
279 const char *xev, *uid;
280 CamelMboxMessageInfo *info = NULL;
281 int add = 0; /* bitmask of things to add, 1 assign uid, 2, just add as new, 4 = recent */
283 const char *status = NULL, *xstatus = NULL;
287 /* check for existance of status & x-status headers */
288 status = camel_header_raw_find(&h, "Status", NULL);
290 flags = decode_status(status);
291 xstatus = camel_header_raw_find(&h, "X-Status", NULL);
293 flags |= decode_status(xstatus);
296 /* if we have an xev header, use it, else assign a new one */
297 xev = camel_header_raw_find(&h, "X-Evolution", NULL);
299 && camel_local_summary_decode_x_evolution((CamelLocalSummary *)s, xev, &mi->info) == 0) {
300 uid = camel_message_info_uid(mi);
301 d(printf("found valid x-evolution: %s\n", uid));
302 info = (CamelMboxMessageInfo *)camel_folder_summary_uid(s, uid);
304 if ((info->info.info.flags & CAMEL_MESSAGE_FOLDER_NOTSEEN)) {
305 info->info.info.flags &= ~CAMEL_MESSAGE_FOLDER_NOTSEEN;
306 camel_message_info_free(mi);
310 d(printf("seen '%s' before, adding anew\n", uid));
311 camel_message_info_free(info);
315 d(printf("but isn't present in summary\n"));
318 d(printf("didn't find x-evolution\n"));
323 mi->info.info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV;
324 g_free (mi->info.info.uid);
325 mi->info.info.uid = camel_folder_summary_next_uid_string(s);
327 camel_folder_summary_set_uid(s, strtoul(camel_message_info_uid(mi), NULL, 10));
330 if (mbs->xstatus && add&2) {
331 /* use the status as the flags when we read it the first time */
333 mi->info.info.flags = (mi->info.info.flags & ~(STATUS_STATUS)) | (flags & STATUS_STATUS);
335 mi->info.info.flags = (mi->info.info.flags & ~(STATUS_XSTATUS)) | (flags & STATUS_XSTATUS);
340 camel_folder_change_info_add_uid(mbs->changes, camel_message_info_uid(mi));
341 if ((add&4) && status == NULL)
342 camel_folder_change_info_recent_uid(mbs->changes, camel_message_info_uid(mi));
348 return (CamelMessageInfo *)mi;
351 static CamelMessageInfo *
352 message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
354 CamelMessageInfo *mi;
356 mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_new_from_parser(s, mp);
358 CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
360 mbi->frompos = camel_mime_parser_tell_start_from(mp);
366 static CamelMessageInfo *
367 message_info_load(CamelFolderSummary *s, FILE *in)
369 CamelMessageInfo *mi;
371 io(printf("loading mbox message info\n"));
373 mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_load(s, in);
375 CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
377 if (camel_file_util_decode_off_t(in, &mbi->frompos) == -1)
383 camel_message_info_free(mi);
388 meta_message_info_save(CamelFolderSummary *s, FILE *out_meta, FILE *out, CamelMessageInfo *mi)
390 CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
392 io(printf("saving mbox message info\n"));
394 if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->meta_message_info_save(s, out_meta, out, mi) == -1
395 || camel_file_util_encode_off_t(out_meta, mbi->frompos) == -1)
402 message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
404 CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
406 io(printf("saving mbox message info\n"));
408 if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_save(s, out, mi) == -1
409 || camel_file_util_encode_off_t(out, mbi->frompos) == -1)
415 /* like summary_rebuild, but also do changeinfo stuff (if supplied) */
417 summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex)
420 CamelFolderSummary *s = (CamelFolderSummary *)cls;
421 CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
423 CamelMboxMessageInfo *mi;
429 d(printf("Calling summary update, from pos %d\n", (int)offset));
431 cls->index_force = FALSE;
433 camel_operation_start(NULL, _("Storing folder"));
435 fd = g_open(cls->folder_path, O_LARGEFILE | O_RDONLY | O_BINARY, 0);
437 d(printf("%s failed to open: %s\n", cls->folder_path, strerror (errno)));
438 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
439 _("Could not open folder: %s: %s"),
440 cls->folder_path, g_strerror (errno));
441 camel_operation_end(NULL);
445 if (fstat(fd, &st) == 0)
448 mp = camel_mime_parser_new();
449 camel_mime_parser_init_with_fd(mp, fd);
450 camel_mime_parser_scan_from(mp, TRUE);
451 camel_mime_parser_seek(mp, offset, SEEK_SET);
454 if (camel_mime_parser_step(mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM
455 && camel_mime_parser_tell_start_from(mp) == offset) {
456 camel_mime_parser_unstep(mp);
458 g_warning("The next message didn't start where I expected, building summary from start");
459 camel_mime_parser_drop_step(mp);
461 camel_mime_parser_seek(mp, offset, SEEK_SET);
465 /* we mark messages as to whether we've seen them or not.
466 If we're not starting from the start, we must be starting
467 from the old end, so everything must be treated as new */
468 count = camel_folder_summary_count(s);
469 for (i=0;i<count;i++) {
470 mi = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
472 mi->info.info.flags |= CAMEL_MESSAGE_FOLDER_NOTSEEN;
474 mi->info.info.flags &= ~CAMEL_MESSAGE_FOLDER_NOTSEEN;
475 camel_message_info_free(mi);
477 mbs->changes = changeinfo;
479 while (camel_mime_parser_step(mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
480 CamelMessageInfo *info;
481 off_t pc = camel_mime_parser_tell_start_from (mp) + 1;
483 camel_operation_progress (NULL, (int) (((float) pc / size) * 100));
485 info = camel_folder_summary_add_from_parser(s, mp);
487 camel_exception_setv(ex, 1, _("Fatal mail parser error near position %ld in folder %s"),
488 camel_mime_parser_tell(mp), cls->folder_path);
493 g_assert(camel_mime_parser_step(mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM_END);
496 camel_object_unref(CAMEL_OBJECT (mp));
498 count = camel_folder_summary_count(s);
499 for (i=0;i<count;i++) {
500 mi = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
501 /* must've dissapeared from the file? */
502 if (mi->info.info.flags & CAMEL_MESSAGE_FOLDER_NOTSEEN) {
503 d(printf("uid '%s' vanished, removing", camel_message_info_uid(mi)));
505 camel_folder_change_info_remove_uid(changeinfo, camel_message_info_uid(mi));
506 camel_folder_summary_remove(s, (CamelMessageInfo *)mi);
510 camel_message_info_free(mi);
514 /* update the file size/mtime in the summary */
516 if (g_stat(cls->folder_path, &st) == 0) {
517 camel_folder_summary_touch(s);
518 mbs->folder_size = st.st_size;
519 s->time = st.st_mtime;
523 camel_operation_end(NULL);
529 mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex)
531 CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
532 CamelFolderSummary *s = (CamelFolderSummary *)cls;
537 d(printf("Checking summary\n"));
539 /* check if the summary is up-to-date */
540 if (g_stat(cls->folder_path, &st) == -1) {
541 camel_folder_summary_clear(s);
542 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
543 _("Cannot check folder: %s: %s"),
544 cls->folder_path, g_strerror (errno));
548 if (cls->check_force)
549 mbs->folder_size = 0;
550 cls->check_force = 0;
552 if (st.st_size == 0) {
553 /* empty? No need to scan at all */
554 d(printf("Empty mbox, clearing summary\n"));
555 count= camel_folder_summary_count(s);
556 for (i=0;i<count;i++) {
557 CamelMessageInfo *info = camel_folder_summary_index(s, i);
560 camel_folder_change_info_remove_uid(changes, camel_message_info_uid(info));
561 camel_message_info_free(info);
564 camel_folder_summary_clear(s);
567 /* is the summary uptodate? */
568 if (st.st_size != mbs->folder_size || st.st_mtime != s->time) {
569 if (mbs->folder_size < st.st_size) {
570 /* this will automatically rescan from 0 if there is a problem */
571 d(printf("folder grew, attempting to rebuild from %d\n", mbs->folder_size));
572 ret = summary_update(cls, mbs->folder_size, changes, ex);
574 d(printf("folder shrank! rebuilding from start\n"));
575 ret = summary_update(cls, 0, changes, ex);
578 d(printf("Folder unchanged, do nothing\n"));
582 /* FIXME: move upstream? */
585 if (mbs->folder_size != st.st_size || s->time != st.st_mtime) {
586 mbs->folder_size = st.st_size;
587 s->time = st.st_mtime;
588 camel_folder_summary_touch(s);
595 /* perform a full sync */
597 mbox_summary_sync_full(CamelMboxSummary *mbs, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
599 CamelLocalSummary *cls = (CamelLocalSummary *)mbs;
600 int fd = -1, fdout = -1;
601 char *tmpname = NULL;
602 guint32 flags = (expunge?1:0);
604 d(printf("performing full summary/sync\n"));
606 camel_operation_start(NULL, _("Storing folder"));
608 fd = g_open(cls->folder_path, O_LARGEFILE | O_RDONLY | O_BINARY, 0);
610 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
611 _("Could not open file: %s: %s"),
612 cls->folder_path, g_strerror (errno));
613 camel_operation_end(NULL);
617 tmpname = g_alloca (strlen (cls->folder_path) + 5);
618 sprintf (tmpname, "%s.tmp", cls->folder_path);
619 d(printf("Writing temporary file to %s\n", tmpname));
620 fdout = g_open(tmpname, O_LARGEFILE|O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600);
622 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
623 _("Cannot open temporary mailbox: %s"),
628 if (camel_mbox_summary_sync_mbox((CamelMboxSummary *)cls, flags, changeinfo, fd, fdout, ex) == -1)
631 d(printf("Closing folders\n"));
633 if (close(fd) == -1) {
634 g_warning("Cannot close source folder: %s", strerror (errno));
635 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
636 _("Could not close source folder %s: %s"),
637 cls->folder_path, g_strerror (errno));
642 if (close(fdout) == -1) {
643 g_warning("Cannot close temporary folder: %s", strerror (errno));
644 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
645 _("Could not close temporary folder: %s"),
651 /* this should probably either use unlink/link/unlink, or recopy over
652 the original mailbox, for various locking reasons/etc */
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));
657 if (g_rename(tmpname, cls->folder_path) == -1) {
658 g_warning("Cannot rename folder: %s", strerror (errno));
659 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
660 _("Could not rename folder: %s"),
666 camel_operation_end(NULL);
679 camel_operation_end(NULL);
684 /* perform a quick sync - only system flags have changed */
686 mbox_summary_sync_quick(CamelMboxSummary *mbs, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
688 CamelLocalSummary *cls = (CamelLocalSummary *)mbs;
689 CamelFolderSummary *s = (CamelFolderSummary *)mbs;
690 CamelMimeParser *mp = NULL;
692 CamelMboxMessageInfo *info = NULL;
694 char *xevnew, *xevtmp;
699 d(printf("Performing quick summary sync\n"));
701 camel_operation_start(NULL, _("Storing folder"));
703 fd = g_open(cls->folder_path, O_LARGEFILE|O_RDWR|O_BINARY, 0);
705 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
706 _("Could not open file: %s: %s"),
707 cls->folder_path, g_strerror (errno));
709 camel_operation_end(NULL);
713 /* need to dup since mime parser closes its fd once it is finalised */
716 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
717 _("Could not store folder: %s"),
723 mp = camel_mime_parser_new();
724 camel_mime_parser_scan_from(mp, TRUE);
725 camel_mime_parser_scan_pre_from(mp, TRUE);
726 camel_mime_parser_init_with_fd(mp, pfd);
728 count = camel_folder_summary_count(s);
729 for (i = 0; i < count; i++) {
731 int pc = (i+1)*100/count;
733 camel_operation_progress(NULL, pc);
735 info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
739 d(printf("Checking message %s %08x\n", camel_message_info_uid(info), info->info.flags));
741 if ((info->info.info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) {
742 camel_message_info_free((CamelMessageInfo *)info);
747 d(printf("Updating message %s\n", camel_message_info_uid(info)));
749 camel_mime_parser_seek(mp, info->frompos, SEEK_SET);
751 if (camel_mime_parser_step(mp, 0, 0) != CAMEL_MIME_PARSER_STATE_FROM) {
752 g_warning("Expected a From line here, didn't get it");
753 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
754 _("Summary and folder mismatch, even after a sync"));
758 if (camel_mime_parser_tell_start_from(mp) != info->frompos) {
759 g_warning("Didn't get the next message where I expected (%d) got %d instead",
760 (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp));
761 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
762 _("Summary and folder mismatch, even after a sync"));
766 if (camel_mime_parser_step(mp, 0, 0) == CAMEL_MIME_PARSER_STATE_FROM_END) {
767 g_warning("camel_mime_parser_step failed (2)");
771 xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset);
772 if (xev == NULL || camel_local_summary_decode_x_evolution(cls, xev, NULL) == -1) {
773 g_warning("We're supposed to have a valid x-ev header, but we dont");
776 xevnew = camel_local_summary_encode_x_evolution(cls, &info->info);
777 /* SIGH: encode_param_list is about the only function which folds headers by itself.
778 This should be fixed somehow differently (either parser doesn't fold headers,
779 or param_list doesn't, or something */
780 xevtmp = camel_header_unfold(xevnew);
781 /* the raw header contains a leading ' ', so (dis)count that too */
782 if (strlen(xev)-1 != strlen(xevtmp)) {
785 g_warning("Hmm, the xev headers shouldn't have changed size, but they did");
790 /* we write out the xevnew string, assuming its been folded identically to the original too! */
792 lastpos = lseek(fd, 0, SEEK_CUR);
793 lseek(fd, xevoffset+strlen("X-Evolution: "), SEEK_SET);
795 len = write(fd, xevnew, strlen(xevnew));
796 } while (len == -1 && errno == EINTR);
797 lseek(fd, lastpos, SEEK_SET);
800 camel_mime_parser_drop_step(mp);
801 camel_mime_parser_drop_step(mp);
803 info->info.info.flags &= 0xffff;
804 camel_message_info_free((CamelMessageInfo *)info);
807 d(printf("Closing folders\n"));
809 if (close(fd) == -1) {
810 g_warning ("Cannot close source folder: %s", strerror (errno));
811 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
812 _("Could not close source folder %s: %s"),
813 cls->folder_path, g_strerror (errno));
818 camel_object_unref((CamelObject *)mp);
820 camel_operation_end(NULL);
827 camel_object_unref((CamelObject *)mp);
829 camel_message_info_free((CamelMessageInfo *)info);
831 camel_operation_end(NULL);
837 mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
840 CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
841 CamelFolderSummary *s = (CamelFolderSummary *)cls;
843 int quick = TRUE, work=FALSE;
846 /* first, sync ourselves up, just to make sure */
847 if (camel_local_summary_check(cls, changeinfo, ex) == -1)
850 count = camel_folder_summary_count(s);
854 /* check what work we have to do, if any */
855 for (i=0;quick && i<count; i++) {
856 CamelMboxMessageInfo *info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
859 if ((expunge && (info->info.info.flags & CAMEL_MESSAGE_DELETED)) ||
860 (info->info.info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_XEVCHANGE)))
863 work |= (info->info.info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0;
864 camel_message_info_free(info);
867 /* yuck i hate this logic, but its to simplify the 'all ok, update summary' and failover cases */
871 ret = ((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_quick(mbs, expunge, changeinfo, ex);
873 g_warning("failed a quick-sync, trying a full sync");
874 camel_exception_clear(ex);
882 ret = ((CamelMboxSummaryClass *)((CamelObject *)cls)->klass)->sync_full(mbs, expunge, changeinfo, ex);
886 if (g_stat(cls->folder_path, &st) == -1) {
887 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
888 _("Unknown error: %s"), g_strerror (errno));
892 if (mbs->folder_size != st.st_size || s->time != st.st_mtime) {
893 s->time = st.st_mtime;
894 mbs->folder_size = st.st_size;
895 camel_folder_summary_touch(s);
898 return ((CamelLocalSummaryClass *)camel_mbox_summary_parent)->sync(cls, expunge, changeinfo, ex);
902 camel_mbox_summary_sync_mbox(CamelMboxSummary *cls, guint32 flags, CamelFolderChangeInfo *changeinfo, int fd, int fdout, CamelException *ex)
904 CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
905 CamelFolderSummary *s = (CamelFolderSummary *)mbs;
906 CamelMimeParser *mp = NULL;
908 CamelMboxMessageInfo *info = NULL;
909 char *buffer, *xevnew = NULL;
911 const char *fromline;
914 char statnew[8], xstatnew[8];
917 d(printf("performing full summary/sync\n"));
919 /* need to dup this because the mime-parser owns the fd after we give it to it */
922 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
923 _("Could not store folder: %s"),
928 mp = camel_mime_parser_new();
929 camel_mime_parser_scan_from(mp, TRUE);
930 camel_mime_parser_scan_pre_from(mp, TRUE);
931 camel_mime_parser_init_with_fd(mp, fd);
933 count = camel_folder_summary_count(s);
934 for (i = 0; i < count; i++) {
935 int pc = (i + 1) * 100 / count;
937 camel_operation_progress(NULL, pc);
939 info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
943 d(printf("Looking at message %s\n", camel_message_info_uid(info)));
945 /* only need to seek past deleted messages, otherwise we should be at the right spot/state already */
947 d(printf("seeking to %d\n", (int)info->frompos));
948 camel_mime_parser_seek(mp, info->frompos, SEEK_SET);
951 if (camel_mime_parser_step(mp, &buffer, &len) != CAMEL_MIME_PARSER_STATE_FROM) {
952 g_warning("Expected a From line here, didn't get it");
953 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
954 _("Summary and folder mismatch, even after a sync"));
958 if (camel_mime_parser_tell_start_from(mp) != info->frompos) {
959 g_warning("Didn't get the next message where I expected (%d) got %d instead",
960 (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp));
961 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
962 _("Summary and folder mismatch, even after a sync"));
967 if ((flags&1) && info->info.info.flags & CAMEL_MESSAGE_DELETED) {
968 const char *uid = camel_message_info_uid(info);
970 d(printf("Deleting %s\n", uid));
972 if (((CamelLocalSummary *)cls)->index)
973 camel_index_delete_name(((CamelLocalSummary *)cls)->index, uid);
975 /* remove it from the change list */
976 camel_folder_change_info_remove_uid(changeinfo, uid);
977 camel_folder_summary_remove(s, (CamelMessageInfo *)info);
978 camel_message_info_free((CamelMessageInfo *)info);
984 /* otherwise, the message is staying, copy its From_ line across */
987 write(fdout, "\n", 1);
989 info->frompos = lseek(fdout, 0, SEEK_CUR);
990 fromline = camel_mime_parser_from_line(mp);
991 write(fdout, fromline, strlen(fromline));
994 if (info && info->info.info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) {
995 d(printf("Updating header for %s flags = %08x\n", camel_message_info_uid(info), info->info.flags));
997 if (camel_mime_parser_step(mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_FROM_END) {
998 g_warning("camel_mime_parser_step failed (2)");
1002 xevnew = camel_local_summary_encode_x_evolution((CamelLocalSummary *)cls, &info->info);
1005 encode_status(info->info.info.flags & STATUS_STATUS, statnew);
1006 encode_status(info->info.info.flags & STATUS_XSTATUS, xstatnew);
1007 len = camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew, statnew, xstatnew);
1010 len = camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew, NULL, NULL);
1015 d(printf("Writing to temporary mailbox failed\n"));
1016 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1017 _("Writing to temporary mailbox failed: %s"),
1018 g_strerror (errno));
1021 info->info.info.flags &= 0xffff;
1024 camel_mime_parser_drop_step(mp);
1027 camel_mime_parser_drop_step(mp);
1029 d(printf("looking for message content to copy across from %d\n", (int)camel_mime_parser_tell(mp)));
1030 while (camel_mime_parser_step(mp, &buffer, &len) == CAMEL_MIME_PARSER_STATE_PRE_FROM) {
1031 /*d(printf("copying mbox contents to temporary: '%.*s'\n", len, buffer));*/
1032 if (write(fdout, buffer, len) != len) {
1033 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1034 _("Writing to temporary mailbox failed: %s: %s"),
1035 ((CamelLocalSummary *)cls)->folder_path,
1036 g_strerror (errno));
1041 if (write(fdout, "\n", 1) != 1) {
1042 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1043 _("Writing to temporary mailbox failed: %s"),
1044 g_strerror (errno));
1048 d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp),
1049 (int)camel_mime_parser_tell_start_from(mp)));
1050 camel_mime_parser_unstep(mp);
1051 camel_message_info_free((CamelMessageInfo *)info);
1057 /* if last was deleted, append the \n we removed */
1058 if (lastdel && count > 0)
1059 write(fdout, "\n", 1);
1062 camel_object_unref((CamelObject *)mp);
1064 /* clear working flags */
1065 for (i=0; i<count; i++) {
1066 info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
1068 if (info->info.info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE)) {
1069 info->info.info.flags &= ~(CAMEL_MESSAGE_FOLDER_NOXEV
1070 |CAMEL_MESSAGE_FOLDER_FLAGGED
1071 |CAMEL_MESSAGE_FOLDER_XEVCHANGE);
1072 camel_folder_summary_touch(s);
1074 camel_message_info_free((CamelMessageInfo *)info);
1083 camel_object_unref((CamelObject *)mp);
1085 camel_message_info_free((CamelMessageInfo *)info);
1091 static CamelMessageInfo *
1092 mbox_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex)
1094 CamelMboxMessageInfo *mi;
1096 mi = (CamelMboxMessageInfo *)((CamelLocalSummaryClass *)camel_mbox_summary_parent)->add(cls, msg, info, ci, ex);
1097 if (mi && ((CamelMboxSummary *)cls)->xstatus) {
1100 /* we snoop and add status/x-status headers to suit */
1101 encode_status(mi->info.info.flags & STATUS_STATUS, status);
1102 camel_medium_set_header((CamelMedium *)msg, "Status", status);
1103 encode_status(mi->info.info.flags & STATUS_XSTATUS, status);
1104 camel_medium_set_header((CamelMedium *)msg, "X-Status", status);
1107 return (CamelMessageInfo *)mi;
1113 } status_flags[] = {
1114 { 'F', CAMEL_MESSAGE_FLAGGED },
1115 { 'A', CAMEL_MESSAGE_ANSWERED },
1116 { 'D', CAMEL_MESSAGE_DELETED },
1117 { 'R', CAMEL_MESSAGE_SEEN },
1121 encode_status(guint32 flags, char status[8])
1127 for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
1128 if (status_flags[i].flag & flags)
1129 *p++ = status_flags[i].tag;
1135 decode_status(const char *status)
1143 while ((c = *p++)) {
1144 for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
1145 if (status_flags[i].tag == c)
1146 flags |= status_flags[i].flag;
1152 #endif /* STATUS_PINE */