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