lib: Remove the synchronization of 'T' flag with "deleted" tag.
authorCarl Worth <cworth@cworth.org>
Tue, 9 Nov 2010 23:48:46 +0000 (15:48 -0800)
committerCarl Worth <cworth@cworth.org>
Thu, 11 Nov 2010 10:35:03 +0000 (02:35 -0800)
Tags in a notmuch database affect all messages with the identical
message-ID. But maildir tags affect individual files. And since
multiple files can contain the identical message-ID, there is not a
one-to-one correspondence between messages affected by tags and flags.

This is particularly dangerous with the 'T' (== "trashed") maildir
flag and the corresponding "deleted" tag in the notmuch
database. Since these flags/tags are often used to trigger
irreversible deletion operations, the lack of one-to-one
correspondence can be potentially dangerous.

For example, consider the following sequence:

  1. A third-party application is used to identify duplicate messages
     in the mail store, and mark all-but-one of each duplicate with
     the 'T' flag for subsequent deletion.

  2. A "notmuch new" operation reads that 'T' flag, adding the
     "deleted" flag to the corresponding messages within the notmuch
     database.

  3. A subsequent notmuch operation, (such as a "notmuch dump; notmuch
     restore" cycle) synchronized the "deleted" tag back to the mail
     store, applying the 'T' flag to all(!) filenames with duplicate
     message IDs.

  4. A third-party application reads the 'T' flags and irreversibly
     deletes all mail messages which had any duplicates(!).

In order to avoid this scenario, we simply refuse to synchronize the
'T' flag with the "deleted" tag. Instead, applications can set 'T' and
act on it to delete files, or can set "deleted" and act on it to
delete files. But in either case the semantics are clear and there is
never dangerous propagation through the one-to-many mapping of notmuch
message objects to files.

lib/message.cc

index c2a9ebdf0e0b4d683d5afc826639011e3d21ddbf..80ff4ca9f77d646bc87af04c43980d47df079d8d 100644 (file)
@@ -55,8 +55,7 @@ struct maildir_flag_tag flag2tag[] = {
     { 'F', "flagged", false},
     { 'P', "passed",  false},
     { 'R', "replied", false},
-    { 'S', "unread",  true },
-    { 'T', "deleted", false},
+    { 'S', "unread",  true }
 };
 
 /* We end up having to call the destructor explicitly because we had