From 95dd5fe5d7e9cd8169a6e3174ea27376acd92870 Mon Sep 17 00:00:00 2001
From: Carl Worth <cworth@cworth.org>
Date: Thu, 11 Nov 2010 14:32:17 -0800
Subject: [PATCH] notmuch_message_tags_to_maildir_flags: Do nothing outside of
 "new" and "cur"

Some people use notmuch with non-maildir files, (for example, email
messages in MH format, or else cool things like using sluk[*] to suck
down feeds into a format that notmuch can index).

To better support uses like that, don't do any renaming for files that
are not in a directory named either "new" or "cur".

[*] https://github.com/krl/sluk/
---
 lib/message.cc        | 60 +++++++++++++++++++++++++++++++------------
 lib/notmuch-private.h |  3 +++
 lib/notmuch.h         | 10 +++++---
 test/maildir-sync     |  2 +-
 4 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/lib/message.cc b/lib/message.cc
index b4bf0ce0..6d8b6c7a 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -945,20 +945,45 @@ maildir_get_new_flags(notmuch_message_t *message, char *flags)
     *p = '\0';
 }
 
-static char *
-maildir_get_subdir (char *filename)
+/* Is the given filename within a maildir directory?
+ *
+ * Specifically, is the final directory component of 'filename' either
+ * "cur" or "new". If so, return a pointer to that final directory
+ * component within 'filename'. If not, return NULL.
+ *
+ * A non-NULL return value is guaranteed to be a valid string pointer
+ * pointing to the characters "new/" or "cur/", (but not
+ * NUL-terminated).
+ */
+static const char *
+_filename_is_in_maildir (const char *filename)
 {
-    char *p, *subdir = NULL;
-
-    p = filename + strlen (filename) - 1;
-    while (p > filename + 3 && *p != '/')
-	p--;
-    if (*p == '/') {
-	subdir = p - 3;
-	if (subdir > filename && *(subdir - 1) != '/')
-	    subdir = NULL;
+    const char *slash, *dir = NULL;
+
+    /* Find the last '/' separating directory from filename. */
+    slash = strrchr (filename, '/');
+    if (slash == NULL)
+	return NULL;
+
+    /* Jump back 4 characters to where the previous '/' will be if the
+     * directory is named "cur" or "new". */
+    if (slash - filename < 4)
+	return NULL;
+
+    slash -= 4;
+
+    if (*slash != '/')
+	return NULL;
+
+    dir = slash + 1;
+
+    if (STRNCMP_LITERAL (dir, "cur/") == 0 ||
+	STRNCMP_LITERAL (dir, "new/") == 0)
+    {
+	return dir;
     }
-    return subdir;
+
+    return NULL;
 }
 
 /* XXX: Needs to ensure that existing, unsupported flags in the
@@ -971,7 +996,7 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
     notmuch_filenames_t *filenames;
     char flags[ARRAY_SIZE(flag2tag)+1];
     const char *filename, *p;
-    char *filename_new, *subdir = NULL;
+    char *filename_new, *dir;
     int ret;
 
     maildir_get_new_flags (message, flags);
@@ -982,6 +1007,9 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
     {
 	filename = notmuch_filenames_get (filenames);
 
+	if (! _filename_is_in_maildir (filename))
+	    continue;
+
 	p = strstr(filename, ":2,");
 	if ((p && strcmp (p+3, flags) == 0) ||
 	    (!p && flags[0] == '\0'))
@@ -1001,9 +1029,9 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
 	filename_new[p-filename] = '\0';
 
 	/* If message is in new/ move it under cur/. */
-	subdir = maildir_get_subdir (filename_new);
-	if (subdir && memcmp (subdir, "new/", 4) == 0)
-	    memcpy (subdir, "cur/", 4);
+	dir = (char *) _filename_is_in_maildir (filename_new);
+	if (dir && STRNCMP_LITERAL (dir, "new/") == 0)
+	    memcpy (dir, "cur/", 4);
 
 	strcpy (filename_new+(p-filename), ":2,");
 	strcpy (filename_new+(p-filename)+3, flags);
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 37ccbb31..592cfb2b 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -57,6 +57,9 @@ NOTMUCH_BEGIN_DECLS
 
 #define COMPILE_TIME_ASSERT(pred) ((void)sizeof(char[1 - 2*!(pred)]))
 
+#define STRNCMP_LITERAL(var, literal) \
+    strncmp ((var), (literal), sizeof (literal) - 1)
+
 /* There's no point in continuing when we've detected that we've done
  * something wrong internally (as opposed to the user passing in a
  * bogus value).
diff --git a/lib/notmuch.h b/lib/notmuch.h
index ea12fe76..d2deca1e 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -947,9 +947,13 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message);
  *
  * Specifically, for each filename corresponding to this message:
  *
- * 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):
+ * If the filename is not in a maildir directory, do nothing.
+ * (A maildir directory is determined as a directory named "new" or
+ * "cur".)
+ *
+ * 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' iff the message has the "draft" tag
  *   'F' iff the message has the "flagged" tag
diff --git a/test/maildir-sync b/test/maildir-sync
index d3931a1f..df9351de 100755
--- a/test/maildir-sync
+++ b/test/maildir-sync
@@ -118,7 +118,7 @@ output+=$(notmuch search subject:"Message to lose maildir info" | notmuch_search
 test_expect_equal "$output" "No new mail. Detected 1 file rename.
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message to lose maildir info (inbox)"
 
-add_message [subject]='"Non-maildir message"' [dir]=notmaildir/new [filename]='non-maildir-message'
+add_message [subject]='"Non-maildir message"' [dir]=notmaildir [filename]='non-maildir-message'
 expected=$(notmuch search --output=files subject:"Non-maildir message")
 test_expect_success "Can remove unread tag from message in non-maildir directory" 'notmuch tag -unread subject:"Non-maildir message"'
 
-- 
2.45.2