diff options
Diffstat (limited to 'camel/providers/local')
-rw-r--r-- | camel/providers/local/camel-local-folder.c | 6 | ||||
-rw-r--r-- | camel/providers/local/camel-local-summary.c | 182 | ||||
-rw-r--r-- | camel/providers/local/camel-local-summary.h | 4 | ||||
-rw-r--r-- | camel/providers/local/camel-mbox-folder.c | 28 | ||||
-rw-r--r-- | camel/providers/local/camel-mbox-summary.c | 590 | ||||
-rw-r--r-- | camel/providers/local/camel-mh-folder.c | 2 | ||||
-rw-r--r-- | camel/providers/local/camel-mh-summary.c | 90 |
7 files changed, 561 insertions, 341 deletions
diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index bec2d7fb56..16ca2d8b2e 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -42,7 +42,7 @@ #include "camel-mime-filter-from.h" #include "camel-exception.h" -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ static CamelFolderClass *parent_class = NULL; @@ -455,7 +455,7 @@ local_set_message_user_flag(CamelFolder *folder, const char *uid, const char *na g_return_if_fail(info != NULL); camel_flag_set(&info->user_flags, name, value); - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary)); camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); } @@ -482,7 +482,7 @@ local_set_message_user_tag(CamelFolder *folder, const char *uid, const char *nam g_return_if_fail(info != NULL); camel_tag_set(&info->user_tags, name, value); - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary)); camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); } diff --git a/camel/providers/local/camel-local-summary.c b/camel/providers/local/camel-local-summary.c index 7455e96cf8..5ba470990b 100644 --- a/camel/providers/local/camel-local-summary.c +++ b/camel/providers/local/camel-local-summary.c @@ -23,6 +23,8 @@ #include "camel-local-summary.h" #include <camel/camel-mime-message.h> +#include <ctype.h> + #include <sys/stat.h> #include <sys/uio.h> #include <unistd.h> @@ -31,7 +33,7 @@ #include <stdlib.h> #define io(x) -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ #define CAMEL_LOCAL_SUMMARY_VERSION (0x100) @@ -166,10 +168,114 @@ camel_local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->decode_x_evolution(cls, xev, info); } +#define DOSTATS +#ifdef DOSTATS +struct _stat_info { + int mitotal; + int micount; + int citotal; + int cicount; + int msgid; + int msgcount; +}; + +static void +do_stat_ci(CamelLocalSummary *cls, struct _stat_info *info, CamelMessageContentInfo *ci) +{ + info->cicount++; + info->citotal += ((CamelFolderSummary *)cls)->content_info_size /*+ 4 memchunks are 1/4 byte overhead per mi */; + if (ci->id) + info->citotal += strlen(ci->id) + 4; + if (ci->description) + info->citotal += strlen(ci->description) + 4; + if (ci->encoding) + info->citotal += strlen(ci->encoding) + 4; + if (ci->type) { + struct _header_content_type *ct = ci->type; + struct _header_param *param; + + info->citotal += sizeof(*ct) + 4; + if (ct->type) + info->citotal += strlen(ct->type) + 4; + if (ct->subtype) + info->citotal += strlen(ct->subtype) + 4; + param = ct->params; + while (param) { + info->citotal += sizeof(*param) + 4; + if (param->name) + info->citotal += strlen(param->name)+4; + if (param->value) + info->citotal += strlen(param->value)+4; + param = param->next; + } + } + ci = ci->childs; + while (ci) { + do_stat_ci(cls, info, ci); + ci = ci->next; + } +} + +static void +do_stat_mi(CamelLocalSummary *cls, struct _stat_info *info, CamelMessageInfo *mi) +{ + info->micount++; + info->mitotal += ((CamelFolderSummary *)cls)->content_info_size /*+ 4*/; + + if (mi->subject) + info->mitotal += strlen(mi->subject) + 4; + if (mi->to) + info->mitotal += strlen(mi->to) + 4; + if (mi->from) + info->mitotal += strlen(mi->from) + 4; + if (mi->cc) + info->mitotal += strlen(mi->cc) + 4; + if (mi->uid) + info->mitotal += strlen(mi->uid) + 4; + + if (mi->references) { + info->mitotal += (mi->references->size-1) * sizeof(CamelSummaryMessageID) + sizeof(CamelSummaryReferences) + 4; + info->msgid += (mi->references->size) * sizeof(CamelSummaryMessageID); + info->msgcount += mi->references->size; + } + + /* dont have any user flags yet */ + + if (mi->content) { + do_stat_ci(cls, info, mi->content); + } +} + +#endif + int camel_local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->check(cls, changeinfo, ex); + int ret; + + ret = ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->check(cls, changeinfo, ex); + +#ifdef DOSTATS + if (ret != -1) { + int i; + CamelFolderSummary *s = (CamelFolderSummary *)cls; + struct _stat_info stats = { 0 }; + + for (i=0;i<camel_folder_summary_count(s);i++) { + CamelMessageInfo *info = camel_folder_summary_index(s, i); + do_stat_mi(cls, &stats, info); + } + + printf("\nMemory used by summary:\n\n"); + printf("Total of %d messages\n", camel_folder_summary_count(s)); + printf("Total: %d bytes (ave %f)\n", stats.citotal + stats.mitotal, + (double)(stats.citotal+stats.mitotal)/(double)camel_folder_summary_count(s)); + printf("Message Info: %d (ave %f)\n", stats.mitotal, (double)stats.mitotal/(double)stats.micount); + printf("Content Info; %d (ave %f) count %d\n", stats.citotal, (double)stats.citotal/(double)stats.cicount, stats.cicount); + printf("message id's: %d (ave %f) count %d\n", stats.msgid, (double)stats.msgid/(double)stats.msgcount, stats.msgcount); + } +#endif + return ret; } int @@ -184,6 +290,64 @@ camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const Cam return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->add(cls, msg, info, ci, ex); } +/** + * camel_local_summary_write_headers: + * @fd: + * @header: + * @xevline: + * + * Write a bunch of headers to the file @fd. IF xevline is non NULL, then + * an X-Evolution header line is created at the end of all of the headers. + * The headers written are termianted with a blank line. + * + * Return value: -1 on error, otherwise the number of bytes written. + **/ +int +camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevline) +{ + int outlen = 0, len; + int newfd; + FILE *out; + + /* dum de dum, maybe the whole sync function should just use stdio for output */ + newfd = dup(fd); + if (newfd == -1) + return -1; + + out = fdopen(newfd, "w"); + if (out == NULL) { + close(newfd); + errno = EINVAL; + return -1; + } + + while (header) { + if (strcasecmp(header->name, "X-Evolution")) { + len = fprintf(out, "%s:%s\n", header->name, header->value); + if (len == -1) { + fclose(out); + return -1; + } + outlen += len; + } + header = header->next; + } + + if (xevline) { + len = fprintf(out, "X-Evolution: %s\n\n", xevline); + if (len == -1) { + fclose(out); + return -1; + } + outlen += len; + } + + if (fclose(out) == -1) + return -1; + + return outlen; +} + #if 0 static int summary_header_load(CamelFolderSummary *s, FILE *in) @@ -271,6 +435,7 @@ local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMess mi->flags = mi->flags | (info->flags & 0xffff); } + mi->flags &= ~(CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED); xev = camel_local_summary_encode_x_evolution(cls, mi); camel_medium_set_header((CamelMedium *)msg, "X-Evolution", xev); g_free(xev); @@ -351,16 +516,21 @@ local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelM if (header && strlen(header) == strlen("00000000-0000") && sscanf(header, "%08x-%04x", &uid, &flags) == 2) { char uidstr[20]; - sprintf(uidstr, "%u", uid); - g_free(mi->uid); - mi->uid = g_strdup(uidstr); - mi->flags = flags; + if (mi) { + sprintf(uidstr, "%u", uid); + g_free(mi->uid); + mi->uid = g_strdup(uidstr); + mi->flags = flags; + } } else { g_free(header); return -1; } g_free(header); + if (mi == NULL) + return 0; + /* check for additional data */ header = strchr(xev, ';'); if (header) { diff --git a/camel/providers/local/camel-local-summary.h b/camel/providers/local/camel-local-summary.h index f1816e06e5..5349194edf 100644 --- a/camel/providers/local/camel-local-summary.h +++ b/camel/providers/local/camel-local-summary.h @@ -37,6 +37,7 @@ typedef struct _CamelLocalSummaryClass CamelLocalSummaryClass; /* extra summary flags */ enum { CAMEL_MESSAGE_FOLDER_NOXEV = 1<<17, + CAMEL_MESSAGE_FOLDER_XEVCHANGE = 1<<18, }; struct _CamelLocalSummary { @@ -77,5 +78,8 @@ CamelMessageInfo *camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessa char *camel_local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *info); int camel_local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *info); +/* utility functions - write headers to a file with optional X-Evolution header */ +int camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevline); + #endif /* ! _CAMEL_LOCAL_SUMMARY_H */ diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index cfffbf7c16..d5e76f7cab 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -42,7 +42,7 @@ #include "camel-mime-filter-from.h" #include "camel-exception.h" -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ static CamelLocalFolderClass *parent_class = NULL; @@ -118,7 +118,7 @@ camel_mbox_folder_new(CamelStore *parent_store, const char *full_name, guint32 f { CamelFolder *folder; - d(printf("Creating mbox folder: %s\n", full_name)); + d(printf("Creating mbox folder: %s in %s\n", full_name, camel_local_store_get_toplevel_dir((CamelLocalStore *)parent_store))); folder = (CamelFolder *)camel_object_new(CAMEL_MBOX_FOLDER_TYPE); folder = (CamelFolder *)camel_local_folder_construct((CamelLocalFolder *)folder, @@ -169,7 +169,9 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel char *fromline = NULL; int fd; struct stat st; - +#if 0 + char *xev; +#endif /* If we can't lock, dont do anything */ if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) return; @@ -186,9 +188,6 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel if (camel_exception_is_set(ex)) goto fail; - /* and we need to set the frompos explicitly */ - ((CamelMboxMessageInfo *)mi)->frompos = mbs->folder_size?mbs->folder_size+1:0; - d(printf("Appending message: uid is %s\n", mi->uid)); output_stream = camel_stream_fs_new_with_name(lf->folder_path, O_WRONLY|O_APPEND, 0600); @@ -197,6 +196,18 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel goto fail; } + /* and we need to set the frompos/XEV explicitly */ + ((CamelMboxMessageInfo *)mi)->frompos = mbs->folder_size?mbs->folder_size+1:0; +#if 0 + xev = camel_local_summary_encode_x_evolution(lf->summary, mi); + if (xev) { + /* the x-ev header should match the 'current' flags, no problem, so store as much */ + camel_medium_set_header((CamelMedium *)message, "X-Evolution", xev); + mi->flags &= ~ CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED; + g_free(xev); + } +#endif + /* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */ fromline = camel_mbox_summary_build_from(((CamelMimePart *)message)->headers); if (camel_stream_printf(output_stream, mbs->folder_size==0?"%s":"\n%s", fromline) == -1) @@ -330,8 +341,9 @@ retry: || camel_mime_parser_tell_start_from(parser) != info->frompos) { g_warning("Summary doesn't match the folder contents! eek!\n" - " expecting offset %ld got %ld", (long int)info->frompos, - (long int)camel_mime_parser_tell_start_from(parser)); + " expecting offset %ld got %ld, state = %d", (long int)info->frompos, + (long int)camel_mime_parser_tell_start_from(parser), + camel_mime_parser_state(parser)); camel_object_unref((CamelObject *)parser); diff --git a/camel/providers/local/camel-mbox-summary.c b/camel/providers/local/camel-mbox-summary.c index adff872cc4..b4c2d151a5 100644 --- a/camel/providers/local/camel-mbox-summary.c +++ b/camel/providers/local/camel-mbox-summary.c @@ -31,7 +31,7 @@ #include <stdlib.h> #define io(x) -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ #define CAMEL_MBOX_SUMMARY_VERSION (0x1000) @@ -157,31 +157,6 @@ summary_header_save(CamelFolderSummary *s, FILE *out) return camel_folder_summary_encode_uint32(out, mbs->folder_size); } -static int -header_evolution_decode(const char *in, guint32 *uid, guint32 *flags) -{ - char *header; - - if (in && (header = header_token_decode(in))) { - if (strlen (header) == strlen ("00000000-0000") - && sscanf (header, "%08x-%04x", uid, flags) == 2) { - g_free(header); - return *uid; - } - g_free(header); - } - - return -1; -} - -/* we still use our own version here, as we dont grok the flag stuff yet, during an expunge - anyway */ -static char * -header_evolution_encode(guint32 uid, guint32 flags) -{ - return g_strdup_printf("%08x-%04x", uid, flags & 0xffff); -} - static CamelMessageInfo * message_info_new(CamelFolderSummary *s, struct _header_raw *h) { @@ -250,6 +225,9 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) int fd; int ok = 0; + /* FIXME: If there is a failure, it shouldn't clear the summary and restart, + it should try and merge the summary info's. This is a bit tricky. */ + fd = open(cls->folder_path, O_RDONLY); if (fd == -1) { printf("%s failed to open: %s", cls->folder_path, strerror(errno)); @@ -275,9 +253,10 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) camel_mime_parser_unstep(mp); } } else { + d(printf("mime parser state ran out? state is %d\n", camel_mime_parser_state(mp))); camel_object_unref(CAMEL_OBJECT(mp)); - /* end of file - no content? */ - return -1; + /* end of file - no content? no error either */ + return 0; } } @@ -302,6 +281,7 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) struct stat st; if (stat(cls->folder_path, &st) == 0) { + camel_folder_summary_touch(s); mbs->folder_size = st.st_size; s->time = st.st_mtime; } @@ -312,11 +292,13 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) /* like summary_rebuild, but also do changeinfo stuff (if supplied) */ static int -summary_update(CamelMboxSummary *mbs, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex) +summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex) { int ret, i, count; - CamelFolderSummary *s = (CamelFolderSummary *)mbs; - CamelLocalSummary *cls = (CamelLocalSummary *)mbs; + CamelFolderSummary *s = (CamelFolderSummary *)cls; + CamelMboxSummary *mbs = (CamelMboxSummary *)cls; + + d(printf("Calling summary update, from pos %d\n", (int)offset)); if (changeinfo) { /* we use the diff function of the change_info to build the update list. */ @@ -368,18 +350,18 @@ mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Camel } else if (s->messages->len == 0) { /* if we are empty, then we rebuilt from scratch */ d(printf("Empty summary, rebuilding from start\n")); - ret = summary_update(mbs, 0, changes, ex); + ret = summary_update(cls, 0, changes, ex); } else { /* is the summary uptodate? */ if (st.st_size != mbs->folder_size || st.st_mtime != s->time) { if (mbs->folder_size < st.st_size) { /* this will automatically rescan from 0 if there is a problem */ d(printf("folder grew, attempting to rebuild from %d\n", mbs->folder_size)); - ret = summary_update(mbs, mbs->folder_size, changes, ex); + ret = summary_update(cls, mbs->folder_size, changes, ex); } else { d(printf("folder shrank! rebuilding from start\n")); camel_folder_summary_clear(s); - ret = summary_update(mbs, 0, changes, ex); + ret = summary_update(cls, 0, changes, ex); } } } @@ -401,104 +383,6 @@ mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Camel return ret; } -static int -header_write(int fd, struct _header_raw *header, char *xevline) -{ - struct iovec iv[4]; - int outlen = 0, len; - - iv[1].iov_base = ":"; - iv[1].iov_len = 1; - iv[3].iov_base = "\n"; - iv[3].iov_len = 1; - - while (header) { - if (strcasecmp(header->name, "X-Evolution")) { - iv[0].iov_base = header->name; - iv[0].iov_len = strlen(header->name); - iv[2].iov_base = header->value; - iv[2].iov_len = strlen(header->value); - - do { - len = writev(fd, iv, 4); - } while (len == -1 && errno == EINTR); - - if (len == -1) - return -1; - outlen += len; - } - header = header->next; - } - - iv[0].iov_base = "X-Evolution: "; - iv[0].iov_len = strlen(iv[0].iov_base); - iv[1].iov_base = xevline; - iv[1].iov_len = strlen(xevline); - iv[2].iov_base = "\n\n"; - iv[2].iov_len = 2; - - do { - len = writev(fd, iv, 3); - } while (len == -1 && errno == EINTR); - - if (len == -1) - return -1; - - outlen += 1; - - d(printf("Wrote %d bytes of headers\n", outlen)); - - return outlen; -} - -static int -copy_block(int fromfd, int tofd, off_t start, size_t bytes) -{ - char buffer[4096]; - int written = 0; - - d(printf("writing %d bytes ... \n", bytes)); - - if (lseek(fromfd, start, SEEK_SET) != start) - return -1; - - while (bytes > 0) { - int toread, towrite; - - toread = bytes; - if (bytes > 4096) - toread = 4096; - else - toread = bytes; - do { - towrite = read(fromfd, buffer, toread); - } while (towrite == -1 && errno == EINTR); - - if (towrite == -1) - return -1; - - /* check for 'end of file' */ - if (towrite == 0) { - d(printf("end of file?\n")); - break; - } - - do { - toread = write(tofd, buffer, towrite); - } while (toread == -1 && errno == EINTR); - - if (toread == -1) - return -1; - - written += toread; - bytes -= toread; - } - - d(printf("written %d bytes\n", written)); - - return written; -} - static char *tz_months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" @@ -567,8 +451,9 @@ camel_mbox_summary_build_from(struct _header_raw *header) return ret; } +/* perform a full sync */ static int -mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) { CamelMboxSummary *mbs = (CamelMboxSummary *)cls; CamelFolderSummary *s = (CamelFolderSummary *)mbs; @@ -576,41 +461,15 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf int i, count; CamelMboxMessageInfo *info; int fd = -1, fdout = -1; - off_t offset = 0; char *tmpname = NULL; char *buffer, *xevnew = NULL; - const char *xev; int len; - guint32 uid, flags; - int quick = TRUE, work = FALSE; - struct stat st; - char *fromline; - - /* make sure we're in sync, after this point we at least have a complete list of id's */ - summary_update(mbs, mbs->folder_size, changeinfo, ex); - - if (camel_exception_is_set(ex)) - return -1; - - /* FIXME: This needs to take the user flags and tags fields into account */ - - /* check if we have any work to do */ - d(printf("Performing sync, %d messages in inbox\n", count)); - for (i = 0; quick && i < count; i++) { - info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); - if ((expunge && (info->info.flags & CAMEL_MESSAGE_DELETED)) || - (info->info.flags & CAMEL_MESSAGE_FOLDER_NOXEV)) - quick = FALSE; - else - work |= (info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0; - } + const char *fromline; + int lastdel = FALSE; - d(printf("Options: %s %s %s\n", expunge ? "expunge" : "", quick ? "quick" : "", work ? "Work" : "")); + d(printf("performing full summary/sync\n")); - if (quick && !work) - return 0; - - fd = open(cls->folder_path, O_RDWR); + fd = open(cls->folder_path, O_RDONLY); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not open folder to summarise: %s: %s"), @@ -620,46 +479,52 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf mp = camel_mime_parser_new(); camel_mime_parser_scan_from(mp, TRUE); + camel_mime_parser_scan_pre_from(mp, TRUE); camel_mime_parser_init_with_fd(mp, fd); - if (!quick) { - tmpname = alloca(strlen (cls->folder_path) + 5); - sprintf(tmpname, "%s.tmp", cls->folder_path); - d(printf("Writing tmp file to %s\n", tmpname)); - retry_out: - fdout = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, 0600); - if (fdout == -1) { - if (errno == EEXIST) - if (unlink(tmpname) != -1) - goto retry_out; - - tmpname = NULL; - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot open temporary mailbox: %s"), strerror(errno)); - goto error; - } + tmpname = alloca(strlen (cls->folder_path) + 5); + sprintf(tmpname, "%s.tmp", cls->folder_path); + d(printf("Writing tmp file to %s\n", tmpname)); + fdout = open(tmpname, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fdout == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot open temporary mailbox: %s"), strerror(errno)); + goto error; } + count = camel_folder_summary_count(s); for (i = 0; i < count; i++) { - off_t frompos, bodypos, lastpos; - /* This has to be an int, not an off_t, because that's - * what camel_mime_parser_header returns... FIXME. - */ - int xevoffset; - info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); g_assert(info); d(printf("Looking at message %s\n", info->info.uid)); + /* only need to seek past deleted messages, otherwise we should be at the right spot/state already */ + if (lastdel) { + d(printf("seeking to %d\n", (int)info->frompos)); + camel_mime_parser_seek(mp, info->frompos, SEEK_SET); + } + + if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) { + g_warning("Expected a From line here, didn't get it"); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + if (camel_mime_parser_tell_start_from(mp) != info->frompos) { + g_warning("Didn't get the next message where I expected (%d) got %d instead", + (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + lastdel = FALSE; if (expunge && info->info.flags & CAMEL_MESSAGE_DELETED) { d(printf("Deleting %s\n", info->info.uid)); - g_assert(!quick); - offset -= (info->info.content->endpos - info->frompos); - - /* FIXME: put this in folder_summary::remove()? */ if (cls->index) ibex_unindex(cls->index, info->info.uid); @@ -669,113 +534,54 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf count--; i--; info = NULL; - } else if (info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) { - int xevok = FALSE; + lastdel = TRUE; + } else { + /* otherwise, the message is staying, copy its From_ line across */ + if (i>0) { + write(fdout, "\n", 1); + } + info->frompos = lseek(fdout, 0, SEEK_CUR); + fromline = camel_mime_parser_from_line(mp); + write(fdout, fromline, strlen(fromline)); + } + if (info && info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) { d(printf("Updating header for %s flags = %08x\n", info->info.uid, info->info.flags)); - /* find the next message, header parts */ - camel_mime_parser_seek(mp, info->frompos, SEEK_SET); - if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) { - g_warning("camel_mime_parser_step failed (1)"); - goto error; - } - - if (camel_mime_parser_tell_start_from (mp) != info->frompos) { - g_warning("Summary/mbox mismatch, aborting sync"); - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary mismatch, aborting sync")); - goto error; - } - - if (camel_mime_parser_step (mp, &buffer, &len) == HSCAN_FROM_END) { + if (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_FROM_END) { g_warning("camel_mime_parser_step failed (2)"); goto error; } - /* Check if the X-Evolution header is valid. */ - - /* FIXME: Use camel_local_summary versions here */ - - xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset); - if (xev && header_evolution_decode (xev, &uid, &flags) != -1) - xevok = TRUE; - - xevnew = header_evolution_encode(strtoul (info->info.uid, NULL, 10), info->info.flags & 0xffff); - if (quick) { - if (!xevok) { - g_warning("The summary told me I had an X-Evolution header, but i dont!"); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Summary mismatch, X-Evolution header missing")); - goto error; - } - buffer = g_strdup_printf("X-Evolution: %s", xevnew); - lastpos = lseek(fd, 0, SEEK_CUR); - lseek(fd, xevoffset, SEEK_SET); - do { - len = write(fd, buffer, strlen (buffer)); - } while (len == -1 && errno == EINTR); - lseek(fd, lastpos, SEEK_SET); - g_free(buffer); - if (len == -1) { - goto error; - } - } else { - frompos = lseek(fdout, 0, SEEK_CUR); - fromline = camel_mbox_summary_build_from(camel_mime_parser_headers_raw (mp)); - write(fdout, fromline, strlen(fromline)); - g_free(fromline); - if (header_write(fdout, camel_mime_parser_headers_raw(mp), xevnew) == -1) { - d(printf("Error writing to tmp mailbox\n")); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Error writing to temp mailbox: %s"), - strerror(errno)); - goto error; - } - bodypos = lseek(fdout, 0, SEEK_CUR); - d(printf("pos = %d, endpos = %d, bodypos = %d\n", - (int) info->info.content->pos, - (int) info->info.content->endpos, - (int) info->info.content->bodypos)); - if (copy_block(fd, fdout, info->info.content->bodypos, - info->info.content->endpos - info->info.content->bodypos) == -1) { - g_warning("Cannot copy data to output fd"); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot copy data to output file: %s"), - strerror (errno)); - goto error; - } - info->frompos = frompos; - offset = bodypos - info->info.content->bodypos; + xevnew = camel_local_summary_encode_x_evolution(cls, (CamelMessageInfo *)info); + if (camel_local_summary_write_headers(fdout, camel_mime_parser_headers_raw(mp), xevnew) == -1) { + d(printf("Error writing to tmp mailbox\n")); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Error writing to temp mailbox: %s"), + strerror(errno)); + goto error; } info->info.flags &= 0xffff; g_free(xevnew); xevnew = NULL; camel_mime_parser_drop_step(mp); - camel_mime_parser_drop_step(mp); - } else { - if (!quick) { - if (copy_block(fd, fdout, info->frompos, - info->info.content->endpos - info->frompos) == -1) { - g_warning("Cannot copy data to output fd"); + } + + camel_mime_parser_drop_step(mp); + if (info) { + d(printf("looking for message content to copy across from %d\n", (int)camel_mime_parser_tell(mp))); + while (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_PRE_FROM) { + d(printf("copying mbox contents to tmp: '%.*s'\n", len, buffer)); + if (write(fdout, buffer, len) != len) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot copy data to output file: %s"), - strerror(errno)); + _("Writing to tmp mailbox failed: %s: %s"), + cls->folder_path, strerror(errno)); goto error; } - /* update from pos here? */ - info->frompos += offset; - } else { - d(printf("Nothing to do for this message\n")); } - } - if (!quick && info != NULL && offset != 0) { - d(printf("offsetting content: %d\n", (int)offset)); - camel_folder_summary_offset_content(info->info.content, offset); - d(printf("pos = %d, endpos = %d, bodypos = %d\n", - (int) info->info.content->pos, - (int) info->info.content->endpos, - (int) info->info.content->bodypos)); + d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp), + (int)camel_mime_parser_tell_start_from(mp))); + camel_mime_parser_unstep(mp); } } @@ -786,45 +592,31 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not close source folder %s: %s"), cls->folder_path, strerror(errno)); + fd = -1; goto error; } - if (!quick) { - if (close(fdout) == -1) { - g_warning("Cannot close tmp folder: %s", strerror(errno)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not close temp folder: %s"), - strerror(errno)); - goto error; - } - - if (rename(tmpname, cls->folder_path) == -1) { - g_warning("Cannot rename folder: %s", strerror(errno)); - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not rename folder: %s"), - strerror(errno)); - goto error; - } - tmpname = NULL; - - /* TODO: move up? */ - if (cls->index) - ibex_save(cls->index); + if (close(fdout) == -1) { + g_warning("Cannot close tmp folder: %s", strerror(errno)); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not close temp folder: %s"), + strerror(errno)); + fdout = -1; + goto error; } - if (stat(cls->folder_path, &st) == -1) { + /* this should probably either use unlink/link/unlink, or recopy over + the original mailbox, for various locking reasons/etc */ + if (rename(tmpname, cls->folder_path) == -1) { + g_warning("Cannot rename folder: %s", strerror(errno)); camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Unknown error: %s"), + _("Could not rename folder: %s"), strerror(errno)); goto error; } + tmpname = NULL; - camel_folder_summary_touch(s); - s->time = st.st_mtime; - mbs->folder_size = st.st_size; - camel_folder_summary_save(s); - - camel_object_unref(CAMEL_OBJECT(mp)); + camel_object_unref((CamelObject *)mp); return 0; error: @@ -839,12 +631,186 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf if (tmpname) unlink(tmpname); if (mp) - camel_object_unref(CAMEL_OBJECT(mp)); + camel_object_unref((CamelObject *)mp); return -1; } +/* perform a quick sync - only system flags have changed */ +static int +mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +{ + CamelMboxSummary *mbs = (CamelMboxSummary *)cls; + CamelFolderSummary *s = (CamelFolderSummary *)mbs; + CamelMimeParser *mp = NULL; + int i, count; + CamelMboxMessageInfo *info; + int fd = -1; + char *xevnew; + const char *xev; + int len; + off_t lastpos; + + d(printf("Performing quick summary sync\n")); + + fd = open(cls->folder_path, O_RDWR); + if (fd == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not open folder to summarise: %s: %s"), + cls->folder_path, strerror(errno)); + return -1; + } + + mp = camel_mime_parser_new(); + camel_mime_parser_scan_from(mp, TRUE); + camel_mime_parser_scan_pre_from(mp, TRUE); + camel_mime_parser_init_with_fd(mp, fd); + + count = camel_folder_summary_count(s); + for (i = 0; i < count; i++) { + int xevoffset; + + info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); + + g_assert(info); + + d(printf("Checking message %s %08x\n", info->info.uid, info->info.flags)); + + if ((info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) + continue; + + d(printf("Updating message %s\n", info->info.uid)); + + camel_mime_parser_seek(mp, info->frompos, SEEK_SET); + + if (camel_mime_parser_step(mp, 0, 0) != HSCAN_FROM) { + g_warning("Expected a From line here, didn't get it"); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + if (camel_mime_parser_tell_start_from(mp) != info->frompos) { + g_warning("Didn't get the next message where I expected (%d) got %d instead", + (int)info->frompos, (int)camel_mime_parser_tell_start_from(mp)); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Summary and folder mismatch, even after a sync")); + goto error; + } + + if (camel_mime_parser_step(mp, 0, 0) == HSCAN_FROM_END) { + g_warning("camel_mime_parser_step failed (2)"); + goto error; + } + + xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset); + if (xev == NULL || camel_local_summary_decode_x_evolution(cls, xev, NULL) == -1) { + g_warning("We're supposed to have a valid x-ev header, but we dont"); + goto error; + } + xevnew = camel_local_summary_encode_x_evolution(cls, (CamelMessageInfo *)info); + /* the raw header contains a leading ' ', so count that too */ + if (strlen(xev)-1 != strlen(xevnew)) { + g_free(xevnew); + g_warning("Hmm, the xev headers shouldn't have changed size, but they did"); + goto error; + } + + lastpos = lseek(fd, 0, SEEK_CUR); + lseek(fd, xevoffset+strlen("X-Evolution: "), SEEK_SET); + do { + len = write(fd, xevnew, strlen(xevnew)); + } while (len == -1 && errno == EINTR); + lseek(fd, lastpos, SEEK_SET); + + camel_mime_parser_drop_step(mp); + camel_mime_parser_drop_step(mp); + + info->info.flags &= 0xffff; + } + + d(printf("Closing folders\n")); + + if (close(fd) == -1) { + g_warning("Cannot close source folder: %s", strerror(errno)); + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not close source folder %s: %s"), + cls->folder_path, strerror(errno)); + fd = -1; + goto error; + } + + camel_object_unref((CamelObject *)mp); + return 0; + error: + if (fd != -1) + close(fd); - - + if (mp) + camel_object_unref((CamelObject *)mp); + + return -1; +} + +static int +mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex) +{ + struct stat st; + CamelMboxSummary *mbs = (CamelMboxSummary *)cls; + CamelFolderSummary *s = (CamelFolderSummary *)cls; + int i, count; + int quick = TRUE, work=FALSE; + int ret; + + /* first, sync ourselves up, just to make sure */ + summary_update(cls, mbs->folder_size, changeinfo, ex); + if (camel_exception_is_set(ex)) + return -1; + + count = camel_folder_summary_count(s); + if (count == 0) + return 0; + + /* check what work we have to do, if any */ + for (i=0;quick && i<count; i++) { + CamelMessageInfo *info = camel_folder_summary_index(s, i); + g_assert(info); + if ((expunge && (info->flags & CAMEL_MESSAGE_DELETED)) || + (info->flags & (CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_XEVCHANGE))) + quick = FALSE; + else + work |= (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0; + } + + /* yuck i hate this logic, but its to simplify the 'all ok, update summary' and failover cases */ + ret = -1; + if (quick) { + if (work) { + ret = mbox_summary_sync_quick(cls, expunge, changeinfo, ex); + if (ret == -1) { + g_warning("failed a quick-sync, trying a full sync"); + camel_exception_clear(ex); + } + } else { + ret = 0; + } + } + + if (ret == -1) + ret = mbox_summary_sync_full(cls, expunge, changeinfo, ex); + if (ret == -1) + return -1; + + if (stat(cls->folder_path, &st) == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unknown error: %s"), strerror(errno)); + return -1; + } + + camel_folder_summary_touch(s); + s->time = st.st_mtime; + mbs->folder_size = st.st_size; + camel_folder_summary_save(s); + + return 0; +} diff --git a/camel/providers/local/camel-mh-folder.c b/camel/providers/local/camel-mh-folder.c index 8d6ce69c3c..c0a9f3f2af 100644 --- a/camel/providers/local/camel-mh-folder.c +++ b/camel/providers/local/camel-mh-folder.c @@ -40,7 +40,7 @@ #include "camel-mime-message.h" #include "camel-exception.h" -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ static CamelFolderClass *parent_class = NULL; diff --git a/camel/providers/local/camel-mh-summary.c b/camel/providers/local/camel-mh-summary.c index b6b31664b4..2f3c829e8d 100644 --- a/camel/providers/local/camel-mh-summary.c +++ b/camel/providers/local/camel-mh-summary.c @@ -33,7 +33,7 @@ #include <ctype.h> -#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x)) +#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ #define CAMEL_MH_SUMMARY_VERSION (0x2000) @@ -279,10 +279,71 @@ mh_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, Came return 0; } +static int +mh_summary_sync_message(CamelLocalSummary *cls, CamelMessageInfo *info, CamelException *ex) +{ + CamelMimeParser *mp; + const char *xev, *buffer; + int xevoffset; + int fd, outfd, len, outlen, ret=0; + char *name, *tmpname, *xevnew; + + name = g_strdup_printf("%s/%s", cls->folder_path, info->uid); + fd = open(name, O_RDWR); + if (fd == -1) + return -1; + mp = camel_mime_parser_new(); + camel_mime_parser_init_with_fd(mp, fd); + if (camel_mime_parser_step(mp, 0, 0) != HSCAN_EOF) { + xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset); + xevnew = camel_local_summary_encode_x_evolution(cls, info); + if (xev == NULL + || camel_local_summary_decode_x_evolution(cls, xev, NULL) == -1 + || strlen(xev)+1 != strlen(xevnew)) { + + /* need to write a new copy/unlink old */ + tmpname = g_strdup_printf("%s/.tmp.%d.%s", cls->folder_path, getpid(), info->uid); + outfd = open(tmpname, O_CREAT|O_WRONLY|O_TRUNC, 0600); + if (outfd != -1) { + outlen = 0; + if ( (len = camel_local_summary_write_headers(outfd, camel_mime_parser_headers_raw(mp), xevnew)) == 0) { + while (outlen != -1 && (len = camel_mime_parser_read(mp, &buffer, 10240)) > 0) { + do { + outlen = write(fd, buffer, len); + } while (outlen == -1 && errno == EINTR); + } + } + if (close(outfd) == -1 + || len == -1 + || outlen == -1 + || rename(tmpname, name) == -1) { + unlink(tmpname); + ret = -1; + } + } else { + g_warning("sync can't create tmp file: %s", strerror(errno)); + } + g_free(tmpname); + } else { + /* else, we can just update the flags field */ + lseek(fd, xevoffset+strlen("X-Evolution: "), SEEK_SET); + do { + len = write(fd, xevnew, strlen(xevnew)); + } while (len == -1 && errno == EINTR); + if (len == -1) + ret = -1; + } + + g_free(xevnew); + } -/* sync the summary with the ondisk files. - It doesnt store the state in the file, the summary only, == MUCH faster */ + camel_object_unref((CamelObject *)mp); + g_free(name); + return ret; +} + +/* sync the summary file with the ondisk files */ static int mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changes, CamelException *ex) { @@ -292,16 +353,16 @@ mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo d(printf("summary_sync(expunge=%s)\n", expunge?"true":"false")); - if (cls->index) { - ibex_save(cls->index); - } - if (!expunge) - return 0; + /* we could probably get away without this ... but why not use it, esp if we're going to + be doing any significant io already */ + if (camel_local_summary_check(cls, changes, ex) == -1) + return -1; count = camel_folder_summary_count((CamelFolderSummary *)cls); for (i=count-1;i>=0;i--) { info = camel_folder_summary_index((CamelFolderSummary *)cls, i); - if (info && info->flags & CAMEL_MESSAGE_DELETED) { + g_assert(info); + if (expunge && (info->flags & CAMEL_MESSAGE_DELETED)) { name = g_strdup_printf("%s/%s", cls->folder_path, info->uid); d(printf("deleting %s\n", name)); if (unlink(name) == 0 || errno==ENOENT) { @@ -309,12 +370,19 @@ mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo /* FIXME: put this in folder_summary::remove()? */ if (cls->index) ibex_unindex(cls->index, info->uid); - + camel_folder_change_info_remove_uid(changes, info->uid); camel_folder_summary_remove((CamelFolderSummary *)cls, info); } + g_free(name); + } else if (info->flags & (CAMEL_MESSAGE_FOLDER_NOXEV|CAMEL_MESSAGE_FOLDER_FLAGGED)) { + if (mh_summary_sync_message(cls, info, ex) != -1) { + info->flags &= 0xffff; + } else { + g_warning("Problem occured when trying to expunge, ignored"); + } } } + return 0; } - |