]> git.notmuchmail.org Git - notmuch/blobdiff - lib/message.cc
notmuch_message_tags_to_maildir_flags: Do nothing outside of "new" and "cur"
[notmuch] / lib / message.cc
index 83e05191b2b2ad290c8f9bc4800785ab694644f5..6d8b6c7a18f488bc735f26ddb2b5b9edcf7ad3c7 100644 (file)
@@ -859,53 +859,48 @@ 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_flags_to_tags (notmuch_message_t *message)
 {
-    const char *flags, *p;
-    char f;
-    bool valid, unread;
-    unsigned i;
+    const char *flags;
     notmuch_status_t status;
+    notmuch_filenames_t *filenames;
     const char *filename;
+    char *combined_flags = talloc_strdup (message, "");
+    unsigned i;
+    int seen_maildir_info = 0;
 
-    filename = notmuch_message_get_filename (message);
+    for (filenames = notmuch_message_get_filenames (message);
+        notmuch_filenames_valid (filenames);
+        notmuch_filenames_move_to_next (filenames))
+    {
+       filename = notmuch_filenames_get (filenames);
 
-    flags = strstr (filename, ":2,");
-    if (!flags)
-       return NOTMUCH_STATUS_FILE_NOT_EMAIL;
-    flags += 3;
-
-    /*  Check that the letters are valid Maildir flags */
-    f = 0;
-    valid = true;
-    for (p=flags; valid && *p; p++) {
-       switch (*p) {
-       case 'P':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'D':
-       case 'F':
-           if (*p > f) f=*p;
-           else valid = false;
-       break;
-       default:
-           valid = false;
-       }
-    }
-    if (!valid) {
-       fprintf (stderr, "Warning: Invalid maildir flags in filename %s\n", filename);
-       return NOTMUCH_STATUS_FILE_NOT_EMAIL;
+       flags = strstr (filename, ":2,");
+       if (! flags)
+           continue;
+
+       seen_maildir_info = 1;
+       flags += 3;
+
+       combined_flags = talloc_strdup_append (combined_flags, flags);
     }
 
+    /* If none of the filenames have any maildir info field (not even
+     * an empty info with no flags set) then there's no information to
+     * go on, so do nothing. */
+    if (! seen_maildir_info)
+       return NOTMUCH_STATUS_SUCCESS;
+
     status = notmuch_message_freeze (message);
     if (status)
        return status;
-    unread = true;
+
     for (i = 0; i < ARRAY_SIZE(flag2tag); i++) {
-       if ((strchr (flags, flag2tag[i].flag) != NULL) ^ flag2tag[i].inverse) {
+       if ((strchr (combined_flags, flag2tag[i].flag) != NULL)
+           ^ 
+           flag2tag[i].inverse)
+       {
            status = notmuch_message_add_tag (message, flag2tag[i].tag);
        } else {
            status = notmuch_message_remove_tag (message, flag2tag[i].tag);
@@ -915,6 +910,8 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
     }
     status = notmuch_message_thaw (message);
 
+    talloc_free (combined_flags);
+
     return status;
 }
 
@@ -948,76 +945,111 @@ 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 iterate over all filenames in the message
- *
- * XXX: Needs to ensure that existing, unsupported flags in the
+/* 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)
 {
+    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);
 
-    filename = notmuch_message_get_filename (message);
-    /* TODO: Iterate over all file names. */
-    p = strstr(filename, ":2,");
-    if ((p && strcmp (p+3, flags) == 0) ||
-       (!p && flags[0] == '\0')) {
-       // Return if flags are not to be changed - this suppresses
-       // moving the message from new/ to cur/ during initial
-       // tagging.
-       return NOTMUCH_STATUS_SUCCESS;
-    }
-    if (!p)
-       p = filename + strlen(filename);
+    for (filenames = notmuch_message_get_filenames (message);
+        notmuch_filenames_valid (filenames);
+        notmuch_filenames_move_to_next (filenames))
+    {
+       filename = notmuch_filenames_get (filenames);
 
-    filename_new = (char*)talloc_size(message, (p-filename) + 3 + sizeof(flags));
-    if (unlikely (filename_new == NULL))
-       return NOTMUCH_STATUS_OUT_OF_MEMORY;
+       if (! _filename_is_in_maildir (filename))
+           continue;
+
+       p = strstr(filename, ":2,");
+       if ((p && strcmp (p+3, flags) == 0) ||
+           (!p && flags[0] == '\0'))
+       {
+           continue;
+       }
+
+       if (!p)
+           p = filename + strlen(filename);
+
+       filename_new = (char*) talloc_size (message,
+                                           (p-filename) + 3 + sizeof (flags));
+       if (unlikely (filename_new == NULL))
+           return NOTMUCH_STATUS_OUT_OF_MEMORY;
 
-    memcpy(filename_new, filename, p-filename);
-    filename_new[p-filename] = '\0';
+       memcpy (filename_new, filename, p-filename);
+       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);
+       /* If message is in new/ move it under cur/. */
+       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);
+       strcpy (filename_new+(p-filename), ":2,");
+       strcpy (filename_new+(p-filename)+3, flags);
 
-    if (strcmp (filename, filename_new) != 0) {
-       ret = rename (filename, filename_new);
-       if (ret == -1) {
-           perror (talloc_asprintf (message, "rename of %s to %s failed",
-                                    filename, filename_new));
-           exit (1);
+       if (strcmp (filename, filename_new) != 0) {
+           notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+           ret = rename (filename, filename_new);
+           if (ret == 0)
+               status = _notmuch_message_rename (message, filename_new);
+
+           _notmuch_message_sync (message);
+
+           if (status)
+               return status;
        }
-       return _notmuch_message_rename (message, filename_new);
-       /* _notmuch_message_sync is our caller. Do not call it here. */
     }
+
     return NOTMUCH_STATUS_SUCCESS;
 }