From d424adcf6301d996942eef1332f3a61e16b709c2 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Thu, 16 Nov 2000 13:27:21 +0000 Subject: Check the uid string is all digits before trying to write a 'standard' 2000-11-16 Not Zed * providers/local/camel-local-summary.c (local_summary_encode_x_evolution): Check the uid string is all digits before trying to write a 'standard' x-ev header. * providers/local/camel-maildir-summary.c (camel_maildir_summary_info_to_name): Convert an info into a maildir name:info filename. (camel_maildir_summary_name_to_info): Convert a name:info filename into an info, and tell us if it didn't match it. (message_info_new): When creating a new filename, gets its info from the flags field. Likewise if creating from an existing file, extract the flags. (maildir_summary_sync): Remove a small memleak. Also, if our flags and that requires a filename change, perform that here. (message_info_new): Get the received date from the filename. Also, dont overwirte the uid if we have one. (maildir_summary_check): Sort the summary in received order before completion. (maildir_summary_next_uid_string): Test the name for collusions before we give it out. Retry, and if that fails, well, I guess we collide :( * providers/local/camel-mbox-folder.c (mbox_lock): Implement mbox locking. (mbox_unlock): And unlocking. (mbox_append_message): Lock the folder for write before doing anything. (mbox_get_message): Lock the folder for read before doing anything. * providers/local/camel-local-folder.c (camel_local_folder_lock): Implement something here. We handle the recursive ability but pass the locking to the folder itself. (camel_local_folder_unlock): Likewise for unlocking. (local_lock): Default - do nothing, return success. (local_unlock): Same. (local_sync): Changed slightly for locking api changes, and also, only lock around the sync process itself. * camel-lock.c: New file - utility functions for locking using different strategies and/or for locking folders safely. * Makefile.am (libcamel_la_SOURCES): Adde camel-lock.[ch] svn path=/trunk/; revision=6592 --- camel/ChangeLog | 39 +++++ camel/providers/local/camel-local-folder.c | 235 +++++--------------------- camel/providers/local/camel-local-folder.h | 10 +- camel/providers/local/camel-local-summary.c | 7 +- camel/providers/local/camel-maildir-folder.c | 5 +- camel/providers/local/camel-maildir-summary.c | 187 ++++++++++++++++++-- camel/providers/local/camel-maildir-summary.h | 4 + camel/providers/local/camel-mbox-folder.c | 86 ++++++++-- camel/providers/local/camel-mbox-folder.h | 1 + camel/providers/local/camel-mh-folder.c | 2 +- 10 files changed, 346 insertions(+), 230 deletions(-) diff --git a/camel/ChangeLog b/camel/ChangeLog index 42d17ed4e3..5b4350fca9 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,5 +1,44 @@ 2000-11-16 Not Zed + * providers/local/camel-local-summary.c + (local_summary_encode_x_evolution): Check the uid string is all + digits before trying to write a 'standard' x-ev header. + + * providers/local/camel-maildir-summary.c + (camel_maildir_summary_info_to_name): Convert an info into a + maildir name:info filename. + (camel_maildir_summary_name_to_info): Convert a name:info filename + into an info, and tell us if it didn't match it. + (message_info_new): When creating a new filename, gets its info + from the flags field. Likewise if creating from an existing file, + extract the flags. + (maildir_summary_sync): Remove a small memleak. Also, if our + flags and that requires a filename change, perform that here. + (message_info_new): Get the received date from the filename. + Also, dont overwirte the uid if we have one. + (maildir_summary_check): Sort the summary in received order before + completion. + (maildir_summary_next_uid_string): Test the name for collusions + before we give it out. Retry, and if that fails, well, I guess we + collide :( + + * providers/local/camel-mbox-folder.c (mbox_lock): Implement mbox + locking. + (mbox_unlock): And unlocking. + (mbox_append_message): Lock the folder for write before doing + anything. + (mbox_get_message): Lock the folder for read before doing + anything. + + * providers/local/camel-local-folder.c (camel_local_folder_lock): + Implement something here. We handle the recursive ability but + pass the locking to the folder itself. + (camel_local_folder_unlock): Likewise for unlocking. + (local_lock): Default - do nothing, return success. + (local_unlock): Same. + (local_sync): Changed slightly for locking api changes, and also, + only lock around the sync process itself. + * camel-lock.c: New file - utility functions for locking using different strategies and/or for locking folders safely. diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index 034f91d7ee..bec2d7fb56 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -1,7 +1,5 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */ -/* camel-local-folder.c : Abstract class for an email folder */ - -/* +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- + * * Authors: Michael Zucchi * * Copyright (C) 1999, 2000 Helix Code Inc. @@ -53,6 +51,8 @@ static CamelFolderClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CLOCALS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +static int local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); +static void local_unlock(CamelLocalFolder *lf); static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex); static gint local_get_message_count(CamelFolder *folder); @@ -111,6 +111,9 @@ camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class) camel_folder_class->set_message_user_flag = local_set_message_user_flag; camel_folder_class->get_message_user_tag = local_get_message_user_tag; camel_folder_class->set_message_user_tag = local_set_message_user_tag; + + camel_local_folder_class->lock = local_lock; + camel_local_folder_class->unlock = local_unlock; } static void @@ -221,17 +224,47 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con return lf; } -/* Have to work out how/when to lock */ -int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex) +/* lock the folder, may be called repeatedly (with matching unlock calls), + with type the same or less than the first call */ +int camel_local_folder_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) +{ + if (lf->locked > 0) { + /* lets be anal here - its important the code knows what its doing */ + g_assert(lf->locktype == type || lf->locktype == CAMEL_LOCK_WRITE); + } else { + if (CLOCALF_CLASS(lf)->lock(lf, type, ex) == -1) + return -1; + lf->locktype = type; + } + + lf->locked++; + + return 0; +} + +/* unlock folder */ +int camel_local_folder_unlock(CamelLocalFolder *lf) { + g_assert(lf->locked>0); + lf->locked--; + if (lf->locked == 0) + CLOCALF_CLASS(lf)->unlock(lf); + return 0; } -int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex) +static int +local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) { return 0; } +static void +local_unlock(CamelLocalFolder *lf) +{ + /* nothing */ +} + static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) { @@ -239,18 +272,18 @@ local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) d(printf("local sync, expunge=%s\n", expunge?"true":"false")); - if (camel_local_folder_lock(lf, ex) == -1) + if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) return; + /* if sync fails, we'll pass it up on exit through ex */ camel_local_summary_sync(lf->summary, expunge, lf->changes, ex); + camel_local_folder_unlock(lf); + if (camel_folder_change_info_changed(lf->changes)) { camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", lf->changes); camel_folder_change_info_clear(lf->changes); } - if (camel_local_folder_unlock(lf, ex) == -1) - return; - /* force save of metadata */ if (lf->index) ibex_save(lf->index); @@ -267,186 +300,6 @@ local_expunge(CamelFolder *folder, CamelException *ex) camel_folder_sync(folder, TRUE, ex); } -#if 0 -static void -local_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - CamelStream *output_stream = NULL, *filter_stream = NULL; - CamelMimeFilter *filter_from = NULL; - CamelMessageInfo *newinfo; - struct stat st; - off_t seek = -1; - char *xev; - guint32 uid; - char *fromline = NULL; - - if (stat(local_folder->folder_path, &st) != 0) - goto fail; - - output_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_WRONLY|O_APPEND, 0600); - if (output_stream == NULL) - goto fail; - - seek = st.st_size; - - /* assign a new x-evolution header/uid */ - camel_medium_remove_header(CAMEL_MEDIUM(message), "X-Evolution"); - uid = camel_folder_summary_next_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary)); - /* important that the header matches exactly 00000000-0000 */ - xev = g_strdup_printf("%08x-%04x", uid, info ? info->flags & 0xFFFF : 0); - camel_medium_add_header(CAMEL_MEDIUM(message), "X-Evolution", xev); - g_free(xev); - - /* we must write this to the non-filtered stream ... */ - fromline = camel_local_summary_build_from(CAMEL_MIME_PART(message)->headers); - if (camel_stream_printf(output_stream, seek==0?"%s":"\n%s", fromline) == -1) - goto fail; - - /* and write the content to the filtering stream, that translated '\nFrom' into '\n>From' */ - filter_stream = (CamelStream *) camel_stream_filter_new_with_stream(output_stream); - filter_from = (CamelMimeFilter *) camel_mime_filter_from_new(); - camel_stream_filter_add((CamelStreamFilter *) filter_stream, filter_from); - if (camel_data_wrapper_write_to_stream(CAMEL_DATA_WRAPPER(message), filter_stream) == -1) - goto fail; - - if (camel_stream_close(filter_stream) == -1) - goto fail; - - /* filter stream ref's the output stream itself, so we need to unref it too */ - camel_object_unref(CAMEL_OBJECT(filter_from)); - camel_object_unref(CAMEL_OBJECT(filter_stream)); - camel_object_unref(CAMEL_OBJECT(output_stream)); - g_free(fromline); - - /* force a summary update - will only update from the new position, if it can */ - if (camel_local_summary_update(local_folder->summary, seek==0?seek:seek+1, local_folder->changes) == 0) { - char uidstr[16]; - - sprintf(uidstr, "%u", uid); - newinfo = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uidstr); - - if (info && newinfo) { - CamelFlag *flag = info->user_flags; - CamelTag *tag = info->user_tags; - - while (flag) { - camel_flag_set(&(newinfo->user_flags), flag->name, TRUE); - flag = flag->next; - } - - while (tag) { - camel_tag_set(&(newinfo->user_tags), tag->name, tag->value); - tag = tag->next; - } - } - camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", local_folder->changes); - camel_folder_change_info_clear(local_folder->changes); - } - - return; - - fail: - if (camel_exception_is_set(ex)) { - camel_exception_setv(ex, camel_exception_get_id(ex), - _("Cannot append message to local file: %s"), camel_exception_get_description(ex)); - } else { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot append message to local file: %s"), g_strerror(errno)); - } - if (filter_stream) { - /*camel_stream_close (filter_stream); */ - camel_object_unref(CAMEL_OBJECT(filter_stream)); - } - if (output_stream) - camel_object_unref(CAMEL_OBJECT(output_stream)); - - if (filter_from) - camel_object_unref(CAMEL_OBJECT(filter_from)); - - g_free(fromline); - - /* make sure the file isn't munged by us */ - if (seek != -1) { - int fd = open(local_folder->folder_file_path, O_WRONLY, 0600); - - if (fd != -1) { - ftruncate(fd, st.st_size); - close(fd); - } - } -} - -static CamelMimeMessage * -local_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - CamelStream *message_stream = NULL; - CamelMimeMessage *message = NULL; - CamelLocalMessageInfo *info; - CamelMimeParser *parser = NULL; - char *buffer; - int len; - - /* get the message summary info */ - info = (CamelLocalMessageInfo *) camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uid); - - if (info == NULL) { - errno = ENOENT; - goto fail; - } - - /* if this has no content, its an error in the library */ - g_assert(info->info.content); - g_assert(info->frompos != -1); - - /* where we read from */ - message_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_RDONLY, 0); - if (message_stream == NULL) - goto fail; - - /* we use a parser to verify the message is correct, and in the correct position */ - parser = camel_mime_parser_new(); - camel_mime_parser_init_with_stream(parser, message_stream); - camel_object_unref(CAMEL_OBJECT(message_stream)); - camel_mime_parser_scan_from(parser, TRUE); - - camel_mime_parser_seek(parser, info->frompos, SEEK_SET); - if (camel_mime_parser_step(parser, &buffer, &len) != HSCAN_FROM) { - g_warning("File appears truncated"); - goto fail; - } - - if (camel_mime_parser_tell_start_from(parser) != info->frompos) { - /* TODO: This should probably perform a re-sync/etc, and try again? */ - 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)); - errno = EINVAL; - goto fail; - } - - message = camel_mime_message_new(); - if (camel_mime_part_construct_from_parser(CAMEL_MIME_PART(message), parser) == -1) { - g_warning("Construction failed"); - goto fail; - } - camel_object_unref(CAMEL_OBJECT(parser)); - - return message; - - fail: - camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s"), g_strerror(errno)); - - if (parser) - camel_object_unref(CAMEL_OBJECT(parser)); - if (message) - camel_object_unref(CAMEL_OBJECT(message)); - - return NULL; -} -#endif - /* The following functions all work off the summary, so the default operations provided in camel-local-folder will suffice for all subclasses. They may want to diff --git a/camel/providers/local/camel-local-folder.h b/camel/providers/local/camel-local-folder.h index fa0ad34e84..914f996f9a 100644 --- a/camel/providers/local/camel-local-folder.h +++ b/camel/providers/local/camel-local-folder.h @@ -32,6 +32,7 @@ extern "C" { #include #include #include "camel-local-summary.h" +#include "camel-lock.h" /* #include "camel-store.h" */ @@ -46,6 +47,7 @@ typedef struct { guint32 flags; /* open mode flags */ int locked; /* lock counter */ + CamelLockType locktype; /* what type of lock we have */ char *base_path; /* base path of the local folder */ char *folder_path; /* the path to the folder itself */ @@ -67,10 +69,10 @@ typedef struct { CamelLocalSummary *(*create_summary)(const char *path, const char *folder, ibex *index); /* Lock the folder for my operations */ - int (*lock)(CamelLocalFolder *); + int (*lock)(CamelLocalFolder *, CamelLockType type, CamelException *ex); /* Unlock the folder for my operations */ - int (*unlock)(CamelLocalFolder *); + void (*unlock)(CamelLocalFolder *); } CamelLocalFolderClass; @@ -84,8 +86,8 @@ CamelType camel_local_folder_get_type(void); /* Lock the folder for internal use. May be called repeatedly */ /* UNIMPLEMENTED */ -int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex); -int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex); +int camel_local_folder_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); +int camel_local_folder_unlock(CamelLocalFolder *lf); #ifdef __cplusplus } diff --git a/camel/providers/local/camel-local-summary.c b/camel/providers/local/camel-local-summary.c index ed33d9ffc2..7455e96cf8 100644 --- a/camel/providers/local/camel-local-summary.c +++ b/camel/providers/local/camel-local-summary.c @@ -290,12 +290,15 @@ local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo GString *val = g_string_new(""); CamelFlag *flag = mi->user_flags; CamelTag *tag = mi->user_tags; - char *ret; + char *ret, *p; guint32 uid; /* FIXME: work out what to do with uid's that aren't stored here? */ /* FIXME: perhaps make that a mbox folder only issue?? */ - if (sscanf(mi->uid, "%u", &uid) == 1) { + p = mi->uid; + while (*p && isdigit(*p)) + p++; + if (*p == 0 && sscanf(mi->uid, "%u", &uid) == 1) { g_string_sprintf(out, "%08x-%04x", uid, mi->flags & 0xffff); } else { g_string_sprintf(out, "%s-%04x", mi->uid, mi->flags & 0xffff); diff --git a/camel/providers/local/camel-maildir-folder.c b/camel/providers/local/camel-maildir-folder.c index 0cf3f23610..e3ff088238 100644 --- a/camel/providers/local/camel-maildir-folder.c +++ b/camel/providers/local/camel-maildir-folder.c @@ -96,7 +96,7 @@ CamelType camel_maildir_folder_get_type(void) (CamelObjectInitFunc) maildir_init, (CamelObjectFinalizeFunc) maildir_finalize); } - + return camel_maildir_folder_type; } @@ -128,8 +128,6 @@ static void maildir_append_message(CamelFolder * folder, CamelMimeMessage * mess CamelMaildirMessageInfo *mdi; char *name, *dest; - /* FIXME: probably needs additional locking */ - d(printf("Appending message\n")); /* add it to the summary/assign the uid, etc */ @@ -202,6 +200,7 @@ static CamelMimeMessage *maildir_get_message(CamelFolder * folder, const gchar * mdi = (CamelMaildirMessageInfo *)info; + /* what do we do if the message flags (and :info data) changes? filename mismatch - need to recheck I guess */ name = g_strdup_printf("%s/cur/%s", lf->folder_path, mdi->filename); if ((message_stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0)) == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), diff --git a/camel/providers/local/camel-maildir-summary.c b/camel/providers/local/camel-maildir-summary.c index cbe5a56f00..92b2be23f8 100644 --- a/camel/providers/local/camel-maildir-summary.c +++ b/camel/providers/local/camel-maildir-summary.c @@ -133,6 +133,71 @@ CamelMaildirSummary *camel_maildir_summary_new (const char *filename, const char return o; } +/* the 'standard' maildir flags. should be defined in sorted order. */ +static struct { + char flag; + guint32 flagbit; +} flagbits[] = { + { 'F', CAMEL_MESSAGE_FLAGGED }, + { 'R', CAMEL_MESSAGE_ANSWERED }, + { 'S', CAMEL_MESSAGE_SEEN }, + { 'T', CAMEL_MESSAGE_DELETED }, +}; + +/* convert the uid + flags into a unique:info maildir format */ +char *camel_maildir_summary_info_to_name(const CamelMessageInfo *info) +{ + char *p, *buf; + int i; + + buf = alloca(strlen(info->uid) + strlen(":2,") + (sizeof(flagbits)/sizeof(flagbits[0])) + 1); + p = buf + sprintf(buf, "%s:2,", info->uid); + for (i=0;iflags & flagbits[i].flagbit) + *p++ = flagbits[i].flag; + } + *p = 0; + + return g_strdup(buf); +} + +/* returns 0 if the info matches (or there was none), otherwise we changed it */ +int camel_maildir_summary_name_to_info(CamelMessageInfo *info, const char *name) +{ + char *p, c; + guint32 set = 0; /* what we set */ + /*guint32 all = 0;*/ /* all flags */ + int i; + + p = strstr(name, ":2,"); + if (p) { + p+=3; + while ((c = *p++)) { + /* we could assume that the flags are in order, but its just as easy not to require */ + for (i=0;iflags & flagbits[i].flagbit) == 0) { + set |= flagbits[i].flagbit; + } + /*all |= flagbits[i].flagbit;*/ + } + } + + /* changed? */ + /*if ((info->flags & all) != set) {*/ + if ((info->flags & set) != set) { + /* ok, they did change, only add the new flags ('merge flags'?) */ + /*info->flags &= all; if we wanted to set only the new flags, which we probably dont */ + info->flags |= set; + return 1; + } + } + + return 0; +} + +/* FIXME: We need to also provide an encode/decode X-Evolution function, as the default + is no good for us, and can screw up the uid info */ + static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header_raw *h) { CamelMessageInfo *mi; @@ -144,13 +209,41 @@ static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header if (mi) { mdi = (CamelMaildirMessageInfo *)mi; - mi->uid = camel_folder_summary_next_uid_string(s); + if (mi->uid == NULL) { + mi->uid = camel_folder_summary_next_uid_string(s); + } + + /* with maildir we know the real received date, from the filename */ + mi->date_received = strtoul(mi->uid, NULL, 10); - /* should store some status info in the filename, but we wont (yet) (fixme) */ if (mds->priv->current_file) { +#if 0 + char *p1, *p2, *p3; + unsigned long uid; +#endif + /* if setting from a file, grab the flags from it */ mdi->filename = g_strdup(mds->priv->current_file); + camel_maildir_summary_name_to_info(mi, mdi->filename); + +#if 0 + /* Actually, I dont think all this effort is worth it at all ... */ + + /* also, see if we can extract the next-id from tne name, and safe-if-fy ourselves against collisions */ + /* we check for something.something_number.something */ + p1 = strchr(mdi->filename, '.'); + if (p1) { + p2 = strchr(p1+1, '.'); + p3 = strchr(p1+1, '_'); + if (p2 && p3 && p30) + camel_folder_summary_set_uid(s, uid); + } + } +#endif } else { - mdi->filename = g_strdup_printf("%s:2,", mi->uid); + /* if creating a file, set its name from the flags we have */ + mdi->filename = camel_maildir_summary_info_to_name(mi); } } @@ -160,9 +253,10 @@ static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header static char *maildir_summary_next_uid_string(CamelFolderSummary *s) { CamelMaildirSummary *mds = (CamelMaildirSummary *)s; - /*CamelLocalSummary *cls = (CamelLocalSummary *)s;*/ - /* current_file is more a current_filename, so map the filename to a uid */ + d(printf("next uid string called?\n")); + + /* if we have a current file, then use that to get the uid */ if (mds->priv->current_file) { char *cln; @@ -172,8 +266,33 @@ static char *maildir_summary_next_uid_string(CamelFolderSummary *s) else return g_strdup(mds->priv->current_file); } else { - /* we use time.pid_count.hostname */ + /* the first would probably work, but just to be safe, check for collisions */ +#if 0 return g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), camel_folder_summary_next_uid(s), mds->priv->hostname); +#else + CamelLocalSummary *cls = (CamelLocalSummary *)s; + char *name = NULL, *uid = NULL; + struct stat st; + int retry = 0; + guint32 nextuid = camel_folder_summary_next_uid(s); + + /* we use time.pid_count.hostname */ + do { + if (retry > 0) { + g_free(name); + g_free(uid); + sleep(2); + } + uid = g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), nextuid, mds->priv->hostname); + name = g_strdup_printf("%s/tmp/%s", cls->folder_path, uid); + retry++; + } while (stat(name, &st) == 0 && retry<3); + + /* I dont know what we're supposed to do if it fails to find a unique name?? */ + + g_free(name); + return uid; +#endif } } @@ -219,6 +338,21 @@ remove_summary(char *key, CamelMessageInfo *info, CamelLocalSummary *cls) camel_folder_summary_remove((CamelFolderSummary *)cls, info); } +static int +sort_receive_cmp(const void *ap, const void *bp) +{ + const CamelMessageInfo + *a = *((CamelMessageInfo **)ap), + *b = *((CamelMessageInfo **)bp); + + if (a->date_received < b->date_received) + return -1; + else if (a->date_received > b->date_received) + return 1; + + return 0; +} + static int maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex) { @@ -343,6 +477,9 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca g_free(new); g_free(cur); + /* sort the summary based on receive time, since the directory order is not useful */ + qsort(s->messages->pdata, s->messages->len, sizeof(CamelMessageInfo *), sort_receive_cmp); + /* FIXME: move this up a class? */ /* force a save of the index, just to make sure */ @@ -355,8 +492,7 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca return 0; } -/* sync the summary with the ondisk files. - It doesnt store the state in the file, the summary only, == MUCH faster */ +/* sync the summary with the ondisk files. */ static int maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changes, CamelException *ex) { @@ -364,20 +500,19 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange CamelMessageInfo *info; CamelMaildirMessageInfo *mdi; char *name; + struct stat st; d(printf("summary_sync(expunge=%s)\n", expunge?"true":"false")); if (cls->index) { ibex_save(cls->index); } - if (!expunge) - return 0; 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) { - mdi = (CamelMaildirMessageInfo *)info; + mdi = (CamelMaildirMessageInfo *)info; + if (info && (info->flags & CAMEL_MESSAGE_DELETED) && expunge) { name = g_strdup_printf("%s/cur/%s", cls->folder_path, mdi->filename); d(printf("deleting %s\n", name)); if (unlink(name) == 0 || errno==ENOENT) { @@ -389,6 +524,34 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange camel_folder_change_info_remove_uid(changes, info->uid); camel_folder_summary_remove((CamelFolderSummary *)cls, info); } + g_free(name); + } else if (info && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) { + char *newname = camel_maildir_summary_info_to_name(info); + char *dest; + + /* do we care about additional metainfo stored inside the message? */ + /* probably should all go in the filename? */ + + /* have our flags/ i.e. name changed? */ + if (strcmp(newname, mdi->filename)) { + name = g_strdup_printf("%s/cur/%s", cls->folder_path, mdi->filename); + dest = g_strdup_printf("%s/cur/%s", cls->folder_path, newname); + rename(name, dest); + if (stat(dest, &st) == -1) { + /* we'll assume it didn't work, but dont change anything else */ + g_free(newname); + } else { + g_free(mdi->filename); + mdi->filename = newname; + } + g_free(name); + g_free(dest); + } else { + g_free(newname); + } + + /* strip FOLDER_MESSAGE_FLAGED, etc */ + info->flags &= 0xffff; } } return 0; diff --git a/camel/providers/local/camel-maildir-summary.h b/camel/providers/local/camel-maildir-summary.h index e1f00ac385..25ea845c21 100644 --- a/camel/providers/local/camel-maildir-summary.h +++ b/camel/providers/local/camel-maildir-summary.h @@ -59,5 +59,9 @@ struct _CamelMaildirSummaryClass { CamelType camel_maildir_summary_get_type (void); CamelMaildirSummary *camel_maildir_summary_new (const char *filename, const char *maildirdir, ibex *index); +/* convert some info->flags to/from the messageinfo */ +char *camel_maildir_summary_info_to_name(const CamelMessageInfo *info); +int camel_maildir_summary_name_to_info(CamelMessageInfo *info, const char *name); + #endif /* ! _CAMEL_MAILDIR_SUMMARY_H */ diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index d12b6431de..d65ad54c01 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -51,6 +51,8 @@ static CamelLocalFolderClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); +static void mbox_unlock(CamelLocalFolder *lf); static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex); static CamelMimeMessage *mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex); @@ -73,19 +75,25 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class) camel_folder_class->get_message = mbox_get_message; lclass->create_summary = mbox_create_summary; + lclass->lock = mbox_lock; + lclass->unlock = mbox_unlock; } static void mbox_init(gpointer object, gpointer klass) { - /*CamelFolder *folder = object; - CamelMboxFolder *mbox_folder = object;*/ + /*CamelFolder *folder = object;*/ + CamelMboxFolder *mbox_folder = object; + + mbox_folder->lockfd = -1; } static void mbox_finalise(CamelObject * object) { - /*CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(object);*/ + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)object; + + g_assert(mbox_folder->lockfd == -1); } CamelType camel_mbox_folder_get_type(void) @@ -124,6 +132,32 @@ static CamelLocalSummary *mbox_create_summary(const char *path, const char *fold return (CamelLocalSummary *)camel_mbox_summary_new(path, folder, index); } +static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) +{ + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + + /* make sure we have matching unlocks for locks, camel-local-folder class should enforce this */ + g_assert(mf->lockfd == -1); + + mf->lockfd = open(lf->folder_path, O_RDWR, 0); + if (mf->lockfd == -1) { + camel_exception_setv(ex, 1, "Cannot create folder lock on %s: %s", lf->folder_path, strerror(errno)); + return -1; + } + + return camel_lock_folder(lf->folder_path, mf->lockfd, type, ex); +} + +static void mbox_unlock(CamelLocalFolder *lf) +{ + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + + g_assert(mf->lockfd != -1); + camel_unlock_folder(lf->folder_path, mf->lockfd); + close(mf->lockfd); + mf->lockfd = -1; +} + static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex) { @@ -136,27 +170,28 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel int fd; struct stat st; - /* FIXME: Locking */ + /* If we can't lock, dont do anything */ + if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) + return; d(printf("Appending message\n")); /* first, check the summary is correct (updates folder_size too) */ camel_local_summary_check(lf->summary, lf->changes, ex); if (camel_exception_is_set(ex)) - return; + goto fail; /* add it to the summary/assign the uid, etc */ mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex); - if (camel_exception_is_set(ex)) { - return; - } + if (camel_exception_is_set(ex)) + goto fail; 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); if (output_stream == NULL) { camel_exception_setv(ex, 1, _("Cannot open mailbox: %s: %s\n"), lf->folder_path, strerror(errno)); - return; + goto fail; } /* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */ @@ -174,12 +209,8 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel if (camel_stream_close(filter_stream) == -1) goto fail_write; - /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */ - /* the stat really shouldn't fail, we just wrote to it */ - if (stat(lf->folder_path, &st) == 0) { - mbs->folder_size = st.st_size; - ((CamelFolderSummary *)mbs)->time = st.st_mtime; - } + /* unlock as soon as we can */ + camel_local_folder_unlock(lf); /* filter stream ref's the output stream itself, so we need to unref it too */ camel_object_unref((CamelObject *)filter_from); @@ -187,6 +218,13 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel camel_object_unref((CamelObject *)output_stream); g_free(fromline); + /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */ + /* the stat really shouldn't fail, we just wrote to it */ + if (stat(lf->folder_path, &st) == 0) { + mbs->folder_size = st.st_size; + ((CamelFolderSummary *)mbs)->time = st.st_mtime; + } + if (camel_folder_change_info_changed(lf->changes)) { camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); camel_folder_change_info_clear(lf->changes); @@ -223,6 +261,10 @@ fail_write: ((CamelFolderSummary *)mbs)->time = st.st_mtime; } +fail: + /* make sure we unlock the folder - before we start triggering events into appland */ + camel_local_folder_unlock(lf); + /* cascade the changes through, anyway, if there are any outstanding */ if (camel_folder_change_info_changed(lf->changes)) { camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); @@ -242,7 +284,9 @@ mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) d(printf("Getting message %s\n", uid)); - /* FIXME: mbox locking */ + /* lock the folder first, burn if we can't */ + if (camel_local_folder_lock(lf, CAMEL_LOCK_READ, ex) == -1) + return NULL; retry: /* get the message summary info */ @@ -251,6 +295,7 @@ retry: if (info == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message")); + camel_local_folder_unlock(lf); return NULL; } @@ -260,13 +305,15 @@ retry: /* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache the whole message in memory if the stream is non-seekable (which it is when built from a parser - with no stream). This means we dont have to lock the mbox for the life of the message. */ + with no stream). This means we dont have to lock the mbox for the life of the message, but only + while it is being created. */ fd = open(lf->folder_path, O_RDONLY); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, strerror(errno)); + camel_local_folder_unlock(lf); return NULL; } @@ -296,6 +343,7 @@ retry: _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, _("The folder appears to be irrecoverably corrupted.")); + camel_local_folder_unlock(lf); return NULL; } @@ -307,9 +355,13 @@ retry: _("Message construction failed: Corrupt mailbox?")); camel_object_unref((CamelObject *)parser); camel_object_unref((CamelObject *)message); + camel_local_folder_unlock(lf); return NULL; } + /* and unlock now we're finished with it */ + camel_local_folder_unlock(lf); + camel_object_unref((CamelObject *)parser); /* use the opportunity to notify of changes (particularly if we had a rebuild) */ diff --git a/camel/providers/local/camel-mbox-folder.h b/camel/providers/local/camel-mbox-folder.h index 573b5df177..3ef31a843c 100644 --- a/camel/providers/local/camel-mbox-folder.h +++ b/camel/providers/local/camel-mbox-folder.h @@ -39,6 +39,7 @@ extern "C" { typedef struct { CamelLocalFolder parent_object; + int lockfd; /* for when we have a lock on the folder */ } CamelMboxFolder; typedef struct { diff --git a/camel/providers/local/camel-mh-folder.c b/camel/providers/local/camel-mh-folder.c index c3b74dc3d6..8d6ce69c3c 100644 --- a/camel/providers/local/camel-mh-folder.c +++ b/camel/providers/local/camel-mh-folder.c @@ -127,7 +127,7 @@ static void mh_append_message(CamelFolder * folder, CamelMimeMessage * message, CamelMessageInfo *mi; char *name; - /* FIXME: probably needs additional locking */ + /* FIXME: probably needs additional locking (although mh doesn't appear do do it) */ d(printf("Appending message\n")); -- cgit v1.2.3