Hi everybody,

I've modified mairix to deal with changing maildir flags without the
need to update the database. This was primarily motivated by my work on
integrating mairix with the Gnus mail reader and propagating flags from
the search results to the original mails, but I guess it should be
useful for others, too.

The problem with maildir is that the flags are stored by appending
characters to the filename, e.g. "S" for seen and "R" for
replied. Therefore, you currently have to update the database every time
a maildir flag changes or you risk dangling symlinks. Additionally,
incoming maildir messages are first put into the 'new' directory and are
moved to 'cur' when the mail client has noticed the mail, which can also
lead to dangling symlinks if the database is not updated.

I've added a new option "-c" to mairix which will first check for the
existence of the target file before creating the symlink. If the target
does not exist anymore, it will change 'new' to 'cur' (if needed) and do
a file name completion without the flags using the glob-function from
the glibc.

I've also modified the process of updating the database to deal with
changing maildir flags: Currently, a maildir file with a changed flag
will be seen as a new message by mairix and hence is completely
rescanned (and the old one marked as dead). It will also get a new index
number in the database which made it very difficult for me to deal with
mairix search groups in Gnus since an IMAP server would assign a new UID
to this message although it is technically still the same mail
file. The patched mairix will see that the same mail still exists and
only rescan the flags and update the file name in the database.

You can apply the patch against the mairix v0.21 source with 
patch -p1 < mairix-maildir-patch.diff .

Regards,
David

diff -u -E -b -w -B mairix-0.21/db.c mairix-0.21-maildir-patch/db.c
--- mairix-0.21/db.c	2007-06-22 22:18:00.000000000 +0200
+++ mairix-0.21-maildir-patch/db.c	2008-02-18 22:41:29.000000000 +0100
@@ -684,7 +684,7 @@
 }
 /*}}}*/
 
-static void scan_maildir_flags(struct msgpath *m)/*{{{*/
+void scan_maildir_flags(struct msgpath *m)/*{{{*/
 {
   const char *p, *start;
   start = m->src.mpf.path;
@@ -854,6 +854,61 @@
  return m;
 }
 /*}}}*/
+int change_new_to_cur(char *p)/*{{{*/ 
+{
+  /* possibly change /new/ to /cur/ in maildir paths */
+  char *pos;
+  int numslash;
+  
+  pos = p + strlen(p);
+  numslash=0;
+  while ((pos > p) && (numslash<2))
+    if( *(--pos) == '/') numslash++;
+  if ((numslash==2) && (strncmp(pos, "/new/", 5) == 0)) {
+    strncpy(pos, "/cur/", 5);
+    return 1;
+  }
+  return 0;
+}
+/*}}}*/
+int looks_like_maildir(const char *p)/*{{{*/
+{
+  const char *s;
+  int numdots;
+  
+  /* assume it's maildir if there are two dots after the last slash */
+  s = p + strlen(p);
+  numdots=0;
+  while( (s > p) && (*s != '/'))
+    if( *(s--) == '.' )
+      numdots++;
+  if (numdots != 2) return 0;
+  return 1;
+}
+/*}}}*/
+int lookup_msgpath_without_flags(struct msgpath *sorted_paths, int n_msgs, char *key)/*{{{*/
+{
+  int h, l, length=0, m, r;
+  char *colpos; 
+  
+  if( !looks_like_maildir(key) ) return -1;
+  
+  colpos = strrchr(key, ':');
+  if(colpos) length = colpos - key;
+  else       length = strlen(key);
+  
+  h = n_msgs, l = 0;
+  while (h > l) {
+    m = (h + l) >> 1;
+    r = strncmp(sorted_paths[m].src.mpf.path, key, length);
+    if (r == 0) break;
+    if (l == m) return -1;
+    if (r > 0) h = m;
+    else       l = m;
+  }     
+  return m;
+}
+/*}}}*/
 void maybe_grow_message_arrays(struct database *db)/*{{{*/
 {
   if (db->n_msgs == db->max_msgs) {
@@ -907,7 +961,7 @@
   char *file_in_db, *file_in_new_list;
   int matched_index;
   int i, new_entries_start_at;
-  int any_new, n_newly_pruned, n_already_dead;
+  int any_new, n_newly_pruned, n_already_dead, n_changed_flags;
   int status;
 
   file_in_db = new_array(char, n_msgs);
@@ -917,6 +971,7 @@
 
   n_already_dead = 0;
   n_newly_pruned = 0;
+  n_changed_flags = 0;
 
   for (i=0; i<db->n_msgs; i++) {
     switch (db->type[i]) {
@@ -943,6 +998,19 @@
                * When that stat fails, the path won't get added to the db. */
             }
           }
+        } else {
+          /* if file was in /new/ we search now in /cur/ */
+          change_new_to_cur(db->msgs[i].src.mpf.path);
+          /* check if currently unknown file only has changed flags */
+          matched_index = lookup_msgpath_without_flags(sorted_paths, n_msgs, db->msgs[i].src.mpf.path);
+          if (matched_index >= 0) {
+            file_in_db[matched_index] = 1;
+            file_in_new_list[i] = 1;
+            n_changed_flags++;
+            free(db->msgs[i].src.mpf.path);
+            db->msgs[i].src.mpf.path=new_string(sorted_paths[matched_index].src.mpf.path);
+            scan_maildir_flags(&db->msgs[i]);
+          }
         }
         break;
       case MTY_MBOX:
@@ -953,6 +1021,8 @@
     }
   }
 
+  if(verbose) fprintf(stderr,"%d maildir message files with changed flags\n", n_changed_flags);
+
   /* Add new entries to database */
   new_entries_start_at = db->n_msgs;
 
@@ -1026,7 +1096,7 @@
   free(file_in_db);
   free(file_in_new_list);
 
-  return any_new || (n_newly_pruned > 0);
+  return any_new || (n_newly_pruned > 0) || (n_changed_flags > 0);
 }
 /*}}}*/
 static void recode_encoding(struct matches *m, int *new_idx)/*{{{*/
diff -u -E -b -w -B mairix-0.21/mairix.c mairix-0.21-maildir-patch/mairix.c
--- mairix-0.21/mairix.c	2007-06-22 22:18:00.000000000 +0200
+++ mairix-0.21-maildir-patch/mairix.c	2008-02-18 22:38:06.000000000 +0100
@@ -410,6 +410,7 @@
          "-t           : include all messages in same threads as matching messages\n"
          "-o <mfolder> : override setting of mfolder from mairixrc file\n"
          "-r           : force raw output regardless of mformat setting in mairixrc file\n"
+         "-c           : try file name completion if maildir flags have changed\n"
          "expr_i       : search expression (all expr's AND'ed together):\n"
          "    word          : match word in message body and major headers\n"
          "    t:word        : match word in To: header\n"
@@ -483,6 +484,7 @@
   int do_integrity_checks = 1;
   int do_forced_unlock = 0;
   int do_fast_index = 0;
+  int do_md_completion = 0;
 
   struct globber_array *omit_globs;
 
@@ -515,6 +517,8 @@
       do_excerpt_output = 1;
     } else if (!strcmp(*argv, "-Q") || !strcmp(*argv, "--no-integrity-checks")) {
       do_integrity_checks = 0;
+    } else if (!strcmp(*argv, "-c") || !strcmp(*argv, "--maildir-completion")) {
+      do_md_completion = 1;
     } else if (!strcmp(*argv, "--unlock")) {
       do_forced_unlock = 1;
     } else if (!strcmp(*argv, "-F") ||
@@ -677,7 +681,7 @@
           database_path);
       unlock_and_exit(3);
     }
-    result = search_top(do_threads, do_augment, database_path, complete_mfolder, argv, output_folder_type, verbose);
+    result = search_top(do_threads, do_augment, database_path, complete_mfolder, argv, output_folder_type, verbose, do_md_completion);
 
   } else {
     enum filetype ftype;
diff -u -E -b -w -B mairix-0.21/mairix.h mairix-0.21-maildir-patch/mairix.h
--- mairix-0.21/mairix.h	2007-06-22 22:18:00.000000000 +0200
+++ mairix-0.21-maildir-patch/mairix.h	2008-02-18 22:38:06.000000000 +0100
@@ -354,6 +354,9 @@
 int update_database(struct database *db, struct msgpath *sorted_paths, int n_paths, int do_fast_index);
 void check_database_integrity(struct database *db);
 int cull_dead_messages(struct database *db, int do_integrity_checks);
+int looks_like_maildir(const char *p);
+int change_new_to_cur(char *p);
+void scan_maildir_flags(struct msgpath *m);
 
 /* In mbox.c */
 void build_mbox_lists(struct database *db, const char *folder_base,
@@ -377,7 +380,7 @@
 void write_database(struct database *db, char *filename, int do_integrity_checks);
 
 /* In search.c */
-int search_top(int do_threads, int do_augment, char *database_path, char *complete_mfolder, char **argv, enum folder_type ft, int verbose);
+int search_top(int do_threads, int do_augment, char *database_path, char *complete_mfolder, char **argv, enum folder_type ft, int verbose, int do_md_completion);
 
 /* In stats.c */
 void get_db_stats(struct database *db);
diff -u -E -b -w -B mairix-0.21/search.c mairix-0.21-maildir-patch/search.c
--- mairix-0.21/search.c	2007-06-22 22:18:00.000000000 +0200
+++ mairix-0.21-maildir-patch/search.c	2008-02-18 22:49:27.000000000 +0100
@@ -32,6 +32,7 @@
 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
+#include <glob.h>
 
 /* Lame fix for systems where NAME_MAX isn't defined after including the above
  * set of .h files (Solaris, FreeBSD so far).  Probably grossly oversized but
@@ -654,6 +655,50 @@
   }
 }
 /*}}}*/
+char *find_new_link_target(char *link_target)/*{{{*/
+{
+  int ret, colon_pos;
+  char *target_glob,*pos,*new_target=NULL;
+  glob_t pglob;
+  
+  /* create search pattern */
+  pos = strrchr( link_target, ':');
+  if( pos ) colon_pos = pos - link_target;
+  else colon_pos = strlen(link_target);
+  target_glob = Malloc( colon_pos + 2);
+  strncpy( target_glob, link_target, colon_pos);
+  target_glob[colon_pos] = '*';
+  target_glob[colon_pos+1] = '\0';
+  
+  ret = glob( target_glob, GLOB_NOSORT, NULL, &pglob );
+  
+  /* if we found nothing yet, possibly replace /new/ with /cur/
+     and search again */
+  if( (ret == GLOB_NOMATCH) && change_new_to_cur(target_glob) )
+    ret = glob( target_glob, GLOB_NOSORT, NULL, &pglob );
+  
+  switch(ret) {
+  case 0: 
+    if( pglob.gl_pathc > 1) 
+      fprintf(stderr, "Matched more than one file for %s.\n", target_glob);
+    else {
+      if(verbose) fprintf(stderr, "Using completion %s.\n", target_glob);
+      new_target=new_string(pglob.gl_pathv[0]);
+    } 
+    break;
+  case GLOB_NOMATCH:
+    if (verbose) fprintf(stderr, "Found no completions for %s\n", target_glob);
+    break;
+  default:
+    fprintf(stderr, "Error while running glob on %s\n", target_glob);
+  }
+  
+  free(target_glob);     
+  globfree(&pglob);     
+  
+  return new_target;
+}
+/*}}}*/
 static void create_symlink(char *link_target, char *new_link)/*{{{*/
 {
   if (symlink(link_target, new_link) < 0) {
@@ -776,7 +821,8 @@
   *is_flagged = (db->msg_type_and_flags[idx] & FLAG_FLAGGED) ? 1 : 0;
 }
 
-static int do_search(struct read_db *db, char **args, char *output_path, int show_threads, enum folder_type ft, int verbose)/*{{{*/
+static int do_search(struct read_db *db, char **args, char *output_path, int show_threads, enum folder_type ft, 
+		     int verbose, int do_md_completion)/*{{{*/
 {
   char *colon, *start_words;
   int do_body, do_subject, do_from, do_to, do_cc, do_date, do_size;
@@ -1037,13 +1083,25 @@
           switch (rd_msg_type(db, i)) {
             case DB_MSG_FILE:
               {
-                char *target_path;
+              char *target_path, *new_target=NULL;
                 char *message_path;
                 int is_in_new;
+              struct msgpath mgp;
                 message_path = db->data + db->path_offsets[i];
+              if (do_md_completion && access(message_path, F_OK))
+                /* if target does not exist anymore, try file completion */
+                new_target = find_new_link_target(message_path);
+              if(new_target) {
+                mgp.src.mpf.path = new_target;
+                scan_maildir_flags(&mgp);
+                target_path = mk_maildir_path(i, output_path, 0, mgp.seen, mgp.replied, mgp.flagged);
+                create_symlink(new_target, target_path);
+                free(new_target);
+              } else {
                 is_in_new = looks_like_maildir_new_p(message_path);
                 target_path = mk_maildir_path(i, output_path, is_in_new, is_seen, is_replied, is_flagged);
                 create_symlink(message_path, target_path);
+              }
                 free(target_path);
                 ++n_hits;
               }
@@ -1069,8 +1127,17 @@
           switch (rd_msg_type(db, i)) {
             case DB_MSG_FILE:
               {
+               char *message_path, *new_target=NULL;
                 char *target_path = mk_mh_path(i, output_path);
-                create_symlink(db->data + db->path_offsets[i], target_path);
+               message_path = db->data + db->path_offsets[i];
+               if (do_md_completion && access(message_path, F_OK))
+                 /* if target does not exist anymore, try file completion */
+                 new_target = find_new_link_target(message_path);
+               if(new_target) {
+                 create_symlink(new_target, target_path);
+                 free(new_target);
+               } else
+                 create_symlink(message_path, target_path);
                 free(target_path);
                 ++n_hits;
               }
@@ -1382,7 +1449,8 @@
 }
 /*}}}*/
 
-int search_top(int do_threads, int do_augment, char *database_path, char *complete_mfolder, char **argv, enum folder_type ft, int verbose)/*{{{*/
+int search_top(int do_threads, int do_augment, char *database_path, char *complete_mfolder, 
+	       char **argv, enum folder_type ft, int verbose, int do_md_completion)/*{{{*/
 {
   struct read_db *db;
   int result;
@@ -1428,7 +1496,7 @@
     }
   }
 
-  result = do_search(db, argv, complete_mfolder, do_threads, ft, verbose);
+  result = do_search(db, argv, complete_mfolder, do_threads, ft, verbose, do_md_completion);
   free(complete_mfolder);
   close_db(db);
   return result;
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Mairix-users mailing list
Mairix-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mairix-users

Reply via email to