Robin Bankhead created MAILBOX-199:
--------------------------------------

             Summary: Maildir support for Windows
                 Key: MAILBOX-199
                 URL: https://issues.apache.org/jira/browse/MAILBOX-199
             Project: James Mailbox
          Issue Type: New Feature
          Components: maildir
         Environment: Windows 7 Professional
            Reporter: Robin Bankhead


Propose that Maildir be supported under the Windows platform. To my knowledge, 
the only technical bar to this is that maildir uses the colon (":") character, 
which is an illegal character on the NTFS (and I believe also FAT*) filesystem.

The Maildir standard document[1] that this project refers to, specifies a colon 
(although quite obliquely). The "standard" clearly was never aimed at use on 
Windows, but its benefits as a format strike me as sufficiently great that a 
small deviation is justifiable in order to include it with James and thus 
increase platform-portability.

The below patch alters message-naming so that, instead of a literal colon, the 
local system's path separator character (java.io.File.pathSeparator) is used in 
each case, giving a colon on *NIX and a semicolon (";") on Windows. (Critique 
of this particular methodology is welcome.)

It also includes a quick fix for an issue encountered with file locking on 
Windows, which calls System.gc() so should be made OS-specific. There may be 
other issues like this; I'm searching for them currently.

[1] http://cr.yp.to/proto/maildir.html

# This patch file was generated by NetBeans IDE
# Following Index: paths are relative to: 
/home/robin/backups/app-dev/Java/apache-james-mailbox-api/maildir
# This patch can be applied using context Tools: Patch action on respective 
folder.
# It uses platform neutral UTF-8 encoding and \n newlines.
# Above lines and this line are ignored by the patching process.
Index: src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
--- src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java Base 
(BASE)
+++ src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java Locally 
Modified (Based On LOCAL)
@@ -729,7 +729,7 @@
     public static String stripMetaFromName(String fileName) {
         int end = fileName.indexOf(",S="); // the size
         if (end == -1)
-            end = fileName.indexOf(":2,"); // the flags
+            end = fileName.indexOf(MaildirMessageName.FLAG_PREFIX + "2,"); // 
the flags
         if (end == -1)
             return fileName; // there is no meta data to strip
         return fileName.substring(0, end);
Index: src/main/java/org/apache/james/mailbox/maildir/MaildirMessageName.java
--- src/main/java/org/apache/james/mailbox/maildir/MaildirMessageName.java Base 
(BASE)
+++ src/main/java/org/apache/james/mailbox/maildir/MaildirMessageName.java 
Locally Modified (Based On LOCAL)
@@ -41,20 +41,25 @@
     public static final String FLAG_SEEN = "S";
     public static final String FLAG_DELETED = "T";
 
+       // Character used instead of ":" for Windows filesystems.
+       // Using java.io.File.pathSeparator which = ":" on *NIX
+       // and ";" on Windows.
+       public static final String FLAG_PREFIX = File.pathSeparator;//";";
+
     // patterns
     public static final String PATTERN_STRING_MESSAGE_NAME = 
"\\d+\\.\\w+\\..+?";
-    public static final String PATTERN_STRING_FLAGS = ":2,[DFRST]*";
+    public static final String PATTERN_STRING_FLAGS = FLAG_PREFIX + 
"2,[DFRST]*";
     public static final String PATTERN_STRING_SIZE = ",S=\\d+";
     public static final Pattern PATTERN_MESSAGE =
         Pattern.compile(PATTERN_STRING_MESSAGE_NAME + 
optional(PATTERN_STRING_SIZE) + optional(PATTERN_STRING_FLAGS));
     
     public static final Pattern PATTERN_UNSEEN_MESSAGES =
-        Pattern.compile(PATTERN_STRING_MESSAGE_NAME + PATTERN_STRING_SIZE + 
optional(":2,[^S]*"));
+        Pattern.compile(PATTERN_STRING_MESSAGE_NAME + PATTERN_STRING_SIZE + 
optional(FLAG_PREFIX + "2,[^S]*"));
     
     public static final FilenameFilter FILTER_UNSEEN_MESSAGES = 
createRegexFilter(PATTERN_UNSEEN_MESSAGES);
     
     public static final Pattern PATTERN_DELETED_MESSAGES =
-        Pattern.compile(PATTERN_STRING_MESSAGE_NAME + PATTERN_STRING_SIZE + 
":2,.*" + FLAG_DELETED);
+        Pattern.compile(PATTERN_STRING_MESSAGE_NAME + PATTERN_STRING_SIZE + 
FLAG_PREFIX + "2,.*" + FLAG_DELETED);
     
     public static final FilenameFilter FILTER_DELETED_MESSAGES = 
createRegexFilter(PATTERN_DELETED_MESSAGES);
     
@@ -250,7 +255,7 @@
      * into its components hostname, size and flags and fills the respective 
variables.
      */
     private void splitHostNameAndMeta() {
-        String[] hostnamemetaFlags = hostnameAndMeta.split(":", 2);
+        String[] hostnamemetaFlags = hostnameAndMeta.split(FLAG_PREFIX, 2);
         if (hostnamemetaFlags.length >= 1) {
           this.hostnameAndMeta = hostnamemetaFlags[0];
           int firstEnd = hostnameAndMeta.indexOf(',');
@@ -272,7 +277,7 @@
         }
 
         if (hostnamemetaFlags.length >= 2) {
-            this.flagsString = ":" + hostnamemetaFlags[1];
+            this.flagsString = FLAG_PREFIX + hostnamemetaFlags[1];
         }
         if (isMessageNameStrictParse()) {
             if (sizeString == null) {
@@ -364,7 +369,7 @@
      * @return A String valid for Maildir
      */
     public String encodeFlags(Flags flags) {
-        StringBuilder localFlagsString = new StringBuilder(":2,");
+        StringBuilder localFlagsString = new StringBuilder(FLAG_PREFIX + "2,");
         if (flags.contains(Flags.Flag.DRAFT))
             localFlagsString.append(FLAG_DRAFT);
         if (flags.contains(Flags.Flag.FLAGGED))
Index: 
src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
--- 
src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java 
Base (BASE)
+++ 
src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java 
Locally Modified (Based On LOCAL)
@@ -216,7 +216,18 @@
                     // if the flags don't have change we should not try to move
                     // the file
                     if (newMessageFile.equals(messageFile) == false) {
+                                               byte tries = 0;
+                                               while (true) {
+                                                       try {
                         FileUtils.moveFile(messageFile, newMessageFile);
+                                                               break;
+                                                       } catch(IOException e) {
+                                                               
if(File.pathSeparator.equals(":") || ++tries >= 5) {
+                                                                       throw e;
+                                                               }
+                                                               System.gc();
+                                                       }
+                                               }
                         modSeq = newMessageFile.lastModified();
 
                     } else {
Index: 
src/test/java/org/apache/james/mailbox/maildir/MailderMessageNameTest.java
--- src/test/java/org/apache/james/mailbox/maildir/MailderMessageNameTest.java 
Base (BASE)
+++ src/test/java/org/apache/james/mailbox/maildir/MailderMessageNameTest.java 
Locally Modified (Based On LOCAL)
@@ -48,34 +48,34 @@
     public static List<Object[]> testData() {
         List<Object[]> args = new ArrayList<Object[]>();
         // no size, two flags
-        Parts parts = 
Parts.fullName("1328026049.19146_0.km1111:2,RS").timeSeconds(1328026049)
+        Parts parts = 
Parts.fullName("1328026049.19146_0.km1111;2,RS").timeSeconds(1328026049)
                 
.baseName("1328026049.19146_0.km1111").flagAnswered().flagSeen();
         args.add(valid(parts));
 
         // size and flag
-        parts = 
Parts.fullName("1328613172.M569643P1862V0000000000000902I00EE42CE_0.km1111,S=13103:2,S")
+        parts = 
Parts.fullName("1328613172.M569643P1862V0000000000000902I00EE42CE_0.km1111,S=13103;2,S")
                 .timeSeconds(1328613172).size(13103L)
                 
.baseName("1328613172.M569643P1862V0000000000000902I00EE42CE_0.km1111").flagSeen();
         args.add(valid(parts));
 
         // size, no flags
-        parts = 
Parts.fullName("1340124194.M723289P3184V0000000000000902I006780E9_6.km1111,S=1344:2,")
+        parts = 
Parts.fullName("1340124194.M723289P3184V0000000000000902I006780E9_6.km1111,S=1344;2,")
                 
.baseName("1340124194.M723289P3184V0000000000000902I006780E9_6.km1111").timeSeconds(1340124194)
                 .size(1344L);
         args.add(valid(parts));
 
         // three flags, no size
-        parts = 
Parts.fullName("1106685752.12132_0.km1111:2,FRS").baseName("1106685752.12132_0.km1111")
+        parts = 
Parts.fullName("1106685752.12132_0.km1111;2,FRS").baseName("1106685752.12132_0.km1111")
                 
.timeSeconds(1106685752).flagFlagged().flagAnswered().flagSeen();
         args.add(valid(parts));
 
         // with dovecot attributes
-        parts = 
Parts.fullName("1035478339.27041_118.foo.org,S=1000,W=1030:2,S")
+        parts = 
Parts.fullName("1035478339.27041_118.foo.org,S=1000,W=1030;2,S")
                 
.baseName("1035478339.27041_118.foo.org").timeSeconds(1035478339).size(1000L).flagSeen();
         args.add(valid(parts));
 
         parts = parts.copy();
-        parts.fullName = "1035478339.27041_118.foo.org,W=1030,S=1000:2,S";
+        parts.fullName = "1035478339.27041_118.foo.org,W=1030,S=1000;2,S";
         args.add(valid(parts));
 
         // new mail, no info part at all. found in courier maildirs
@@ -84,15 +84,15 @@
         args.add(valid(parts));
 
         // new mail, generated by james
-        parts = Parts.fullName("1356001301.e563087e30181513.foohost,S=629:2,")
+        parts = Parts.fullName("1356001301.e563087e30181513.foohost,S=629;2,")
                 
.baseName("1356001301.e563087e30181513.foohost").timeSeconds(1356001301).size(629L);
         args.add(valid(parts));
 
-        parts = 
Parts.fullName("1355675588.5c7e107958851103.foohost,S=654:2,S").timeSeconds(1355675588)
+        parts = 
Parts.fullName("1355675588.5c7e107958851103.foohost,S=654;2,S").timeSeconds(1355675588)
                 
.baseName("1355675588.5c7e107958851103.foohost").size(654L).flagSeen();
         args.add(valid(parts));
 
-        parts = Parts.fullName("1355675651.f3dd564265174501.foohost,S=661:2,")
+        parts = Parts.fullName("1355675651.f3dd564265174501.foohost,S=661;2,")
                 
.baseName("1355675651.f3dd564265174501.foohost").timeSeconds(1355675651).size(661L);
         args.add(valid(parts));
 


--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to