From bb74e9dff80e64734308d5997c756fd96d041235 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 10 Nov 2010 17:36:09 -0800 Subject: [PATCH] lib: Rework interface for maildir_flags synchronization Instead of having an API for setting a library-wide flag for synchronization (notmuch_database_set_maildir_sync) we instead implement maildir synchronization with two new library functions: notmuch_message_maildir_flags_to_tags and notmuch_message_tags_to_maildir_flags These functions are nicely documented here, (though the implementation does not quite match the documentation yet---as plainly evidenced by the current results of the test suite). --- lib/database-private.h | 1 - lib/database.cc | 9 ----- lib/message.cc | 57 +++++++++++++++----------------- lib/notmuch-private.h | 2 +- lib/notmuch.h | 75 +++++++++++++++++++++++++++++++++++++----- notmuch-new.c | 4 +-- notmuch-restore.c | 8 +++-- notmuch-tag.c | 8 +++-- 8 files changed, 108 insertions(+), 56 deletions(-) diff --git a/lib/database-private.h b/lib/database-private.h index 81097509..140b54ed 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -51,7 +51,6 @@ struct _notmuch_database { Xapian::QueryParser *query_parser; Xapian::TermGenerator *term_gen; Xapian::ValueRangeProcessor *value_range_processor; - notmuch_bool_t maildir_sync; }; /* Convert tags from Xapian internal format to notmuch format. diff --git a/lib/database.cc b/lib/database.cc index 293d21aa..380bbe3d 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -688,8 +688,6 @@ notmuch_database_open (const char *path, notmuch = NULL; } - notmuch_database_set_maildir_sync (notmuch, FALSE); - DONE: if (notmuch_path) free (notmuch_path); @@ -719,13 +717,6 @@ notmuch_database_close (notmuch_database_t *notmuch) talloc_free (notmuch); } -void -notmuch_database_set_maildir_sync (notmuch_database_t *database, - notmuch_bool_t maildir_sync) -{ - database->maildir_sync = maildir_sync; -} - const char * notmuch_database_get_path (notmuch_database_t *notmuch) { diff --git a/lib/message.cc b/lib/message.cc index 80ff4ca9..88f7c362 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -610,29 +610,15 @@ _notmuch_message_set_date (notmuch_message_t *message, Xapian::sortable_serialise (time_value)); } -static notmuch_private_status_t -_notmuch_message_tags_to_maildir (notmuch_message_t *message); - /* Synchronize changes made to message->doc out into the database. */ void _notmuch_message_sync (notmuch_message_t *message) { Xapian::WritableDatabase *db; - notmuch_private_status_t status; if (message->notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) return; - if (message->notmuch->maildir_sync && - !notmuch_message_get_flag(message, NOTMUCH_MESSAGE_FLAG_TAGS_INVALID)) { - status = _notmuch_message_tags_to_maildir (message); - if (status != NOTMUCH_PRIVATE_STATUS_SUCCESS) { - fprintf (stderr, "Error: Cannot sync tags to maildir (%s)\n", - notmuch_status_to_string ((notmuch_status_t)status)); - /* Exit to avoid unsynchronized mailstore. */ - exit(1); - } - } db = static_cast (message->notmuch->xapian_db); db->replace_document (message->doc_id, message->doc); } @@ -749,7 +735,7 @@ _notmuch_message_remove_term (notmuch_message_t *message, * This change will not be reflected in the database until the next * call to _notmuch_message_sync. */ -notmuch_private_status_t +notmuch_status_t _notmuch_message_rename (notmuch_message_t *message, const char *new_filename) { @@ -757,29 +743,31 @@ _notmuch_message_rename (notmuch_message_t *message, char *direntry; Xapian::PostingIterator i, end; Xapian::Document document; - notmuch_private_status_t pstatus; + notmuch_private_status_t private_status; notmuch_status_t status; const char *old_filename; old_filename = notmuch_message_get_filename(message); old_filename = talloc_reference(local, old_filename); - if (unlikely(!old_filename)) - return NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY; + if (unlikely (! old_filename)) + return NOTMUCH_STATUS_OUT_OF_MEMORY; status = _notmuch_message_add_filename (message, new_filename); if (status) - return (notmuch_private_status_t)status; + return status; status = _notmuch_database_filename_to_direntry (local, message->notmuch, old_filename, &direntry); if (status) - return (notmuch_private_status_t)status; + return status; - pstatus = _notmuch_message_remove_term (message, "file-direntry", direntry); + private_status = _notmuch_message_remove_term (message, "file-direntry", direntry); + status = COERCE_STATUS (private_status, + "Unexpected error from _notmuch_message_remove_term"); talloc_free (local); - return pstatus; + return status; } notmuch_status_t @@ -838,14 +826,18 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag) return NOTMUCH_STATUS_SUCCESS; } +/* XXX: Needs to iterate over all message filenames. */ notmuch_status_t -notmuch_message_maildir_to_tags (notmuch_message_t *message, const char *filename) +notmuch_message_maildir_flags_to_tags (notmuch_message_t *message) { const char *flags, *p; char f; bool valid, unread; unsigned i; notmuch_status_t status; + const char *filename; + + filename = notmuch_message_get_filename (message); flags = strstr (filename, ":2,"); if (!flags) @@ -942,10 +934,14 @@ maildir_get_subdir (char *filename) return subdir; } -/* Rename the message file so that maildir flags corresponds to the - * tags and, if aplicable, move the message from new/ to cur/. */ -static notmuch_private_status_t -_notmuch_message_tags_to_maildir (notmuch_message_t *message) +/* XXX: Needs to iterate over all filenames in the message + * + * XXX: Needs to ensure that existing, unsupported flags in the + * filename are left unchanged (which also needs a test in the + * test suite). + */ +notmuch_status_t +notmuch_message_tags_to_maildir_flags (notmuch_message_t *message) { char flags[ARRAY_SIZE(flag2tag)+1]; const char *filename, *p; @@ -962,14 +958,15 @@ _notmuch_message_tags_to_maildir (notmuch_message_t *message) // Return if flags are not to be changed - this suppresses // moving the message from new/ to cur/ during initial // tagging. - return NOTMUCH_PRIVATE_STATUS_SUCCESS; + return NOTMUCH_STATUS_SUCCESS; } if (!p) p = filename + strlen(filename); filename_new = (char*)talloc_size(message, (p-filename) + 3 + sizeof(flags)); if (unlikely (filename_new == NULL)) - return NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY; + return NOTMUCH_STATUS_OUT_OF_MEMORY; + memcpy(filename_new, filename, p-filename); filename_new[p-filename] = '\0'; @@ -991,7 +988,7 @@ _notmuch_message_tags_to_maildir (notmuch_message_t *message) return _notmuch_message_rename (message, filename_new); /* _notmuch_message_sync is our caller. Do not call it here. */ } - return NOTMUCH_PRIVATE_STATUS_SUCCESS; + return NOTMUCH_STATUS_SUCCESS; } notmuch_status_t diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index d602e157..4c3d3eb8 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -261,7 +261,7 @@ notmuch_status_t _notmuch_message_add_filename (notmuch_message_t *message, const char *filename); -notmuch_private_status_t +notmuch_status_t _notmuch_message_rename (notmuch_message_t *message, const char *new_filename); diff --git a/lib/notmuch.h b/lib/notmuch.h index 41820b59..ca8707e8 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -176,12 +176,6 @@ notmuch_database_open (const char *path, void notmuch_database_close (notmuch_database_t *database); -/* Sets whether maildir flags should be synchronized with notmuch - * tags. */ -void -notmuch_database_set_maildir_sync (notmuch_database_t *database, - notmuch_bool_t maildir_sync); - /* Return the database path of the given database. * * The return value is a string owned by notmuch so should not be @@ -903,11 +897,74 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag); notmuch_status_t notmuch_message_remove_all_tags (notmuch_message_t *message); -/* Add or remove tags based on the maildir flags in the file name. +/* Add/remove tags according to maildir flags in the message filename(s) + * + * This function examines the filenames of 'message' for maildir + * flags, and adds or removes tags on 'message' as follows when these + * flags are present: + * + * Flag Action + * ---- ------ + * 'D' Adds the "draft" tag to the message + * 'F' Adds the "flagged" tag to the message + * 'P' Adds the "passed" tag to the message + * 'R' Adds the "replied" tag to the message + * 'S' Removes the "unread" tag from the message + * + * The only filenames examined for flags are filenames which appear to + * be within a maildir directory, (the file must be in a directory + * named "new" or "cur" and there must be a neighboring directory + * named respectively "cur" or "new"). The flags are identified as + * trailing components of the filename after a sequence of ":2,". + * + * If there are multiple filenames associated with this message, the + * flag is considered present if it appears in one or more + * filenames. (That is, the flags from the multiple filenames are + * combined with the logical OR operator.) + * + * A client can ensure that notmuch database tags remain synchronized + * with maildir flags by calling this function after each call to + * notmuch_database_add_message. See also + * notmuch_message_tags_to_maildir_flags for synchronizing tag changes + * back to maildir flags. */ notmuch_status_t -notmuch_message_maildir_to_tags (notmuch_message_t *message, - const char *filename); +notmuch_message_maildir_flags_to_tags (notmuch_message_t *message); + +/* Rename message filename(s) to encode tags as maildir flags + * + * Specifically, for each filename corresponding to this message: + * + * If the filename is not in a maildir directory, do nothing. + * (A maildir directory is determined as a directory named "new" or + * "cur" with a neighboring directory named respectively "cur" or + * "new".) + * + * If the filename is in a maildir directory, rename the file so that + * its filename ends with the sequence ":2," followed by zero or more + * of the following single-character flags (in ASCII order): + * + * 'D' if the message has the "draft" tag + * 'F' if the message has the "flagged" tag + * 'P' if the message has the "passed" tag + * 'R' if the message has the "replied" tag + * 'S' if the message does not have the "unread" tag + * + * Any existing flags unmentioned in the list above are left + * unaffected by the rename. + * + * Also, if this filename is in a directory named "new", rename it to + * be within the neighboring directory named "cur". + * + * A client can ensure that maildir filename flags remain synchronized + * with notmuch database tags by calling this function after changing + * tags, (after calls to notmuch_message_add_tag, + * notmuch_message_remove_tag, or notmuch_message_freeze/ + * notmuch_message_thaw). See also notmuch_message_maildir_flags_to_tags + * for synchronizing maildir flag changes back to tags. + */ +notmuch_status_t +notmuch_message_tags_to_maildir_flags (notmuch_message_t *message); /* Freeze the current state of 'message' within the database. * diff --git a/notmuch-new.c b/notmuch-new.c index 23e7afc5..3d628c23 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -412,12 +412,12 @@ add_files_recursive (notmuch_database_t *notmuch, for (tag=state->new_tags; *tag != NULL; tag++) notmuch_message_add_tag (message, *tag); if (state->synchronize_flags == TRUE) - notmuch_message_maildir_to_tags (message, next); + notmuch_message_maildir_flags_to_tags (message); break; /* Non-fatal issues (go on to next file) */ case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: if (state->synchronize_flags == TRUE) - notmuch_message_maildir_to_tags (message, next); + notmuch_message_maildir_flags_to_tags (message); break; case NOTMUCH_STATUS_FILE_NOT_EMAIL: fprintf (stderr, "Note: Ignoring non-mail file: %s\n", diff --git a/notmuch-restore.c b/notmuch-restore.c index 98660b32..f095f64a 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -25,6 +25,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; + notmuch_bool_t synchronize_flags; FILE *input; char *line = NULL; size_t line_size; @@ -41,8 +42,8 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) if (notmuch == NULL) return 1; - notmuch_database_set_maildir_sync (notmuch, - notmuch_config_get_maildir_synchronize_flags (config)); + synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); + if (argc) { input = fopen (argv[0], "r"); if (input == NULL) { @@ -133,6 +134,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) notmuch_message_thaw (message); + if (synchronize_flags) + notmuch_message_tags_to_maildir_flags (message); + NEXT_LINE: if (message) notmuch_message_destroy (message); diff --git a/notmuch-tag.c b/notmuch-tag.c index 53f08f43..60e21e0d 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -45,6 +45,7 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[])) notmuch_messages_t *messages; notmuch_message_t *message; struct sigaction action; + notmuch_bool_t synchronize_flags; int i; /* Setup our handler for SIGINT */ @@ -100,8 +101,8 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[])) NOTMUCH_DATABASE_MODE_READ_WRITE); if (notmuch == NULL) return 1; - notmuch_database_set_maildir_sync (notmuch, - notmuch_config_get_maildir_synchronize_flags (config)); + + synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); query = notmuch_query_create (notmuch, query_string); if (query == NULL) { @@ -129,6 +130,9 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[])) notmuch_message_thaw (message); + if (synchronize_flags) + notmuch_message_tags_to_maildir_flags (message); + notmuch_message_destroy (message); }