From 1b7a12131842125d7c7b77c87dbbe5bb96eb3a73 Mon Sep 17 00:00:00 2001 From: mguessan Date: Fri, 20 Feb 2009 17:06:03 +0000 Subject: [PATCH] IMAP: Fetch by id for Apple Mail, hide authenticate password in logs, use actual message list size instead of object count git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@382 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- src/java/davmail/imap/ImapConnection.java | 181 +++++++++++++++------- 1 file changed, 128 insertions(+), 53 deletions(-) diff --git a/src/java/davmail/imap/ImapConnection.java b/src/java/davmail/imap/ImapConnection.java index dc00749d..5c57fe6b 100644 --- a/src/java/davmail/imap/ImapConnection.java +++ b/src/java/davmail/imap/ImapConnection.java @@ -77,11 +77,13 @@ public class ImapConnection extends AbstractConnection { if (tokens.hasMoreTokens()) { String authenticationMethod = tokens.nextToken(); if ("LOGIN".equalsIgnoreCase(authenticationMethod)) { - sendClient("+ " + base64Encode("Username:")); - userName = base64Decode(readClient()); - sendClient("+ " + base64Encode("Password:")); - password = base64Decode(readClient()); try { + sendClient("+ " + base64Encode("Username:")); + state = State.LOGIN; + userName = base64Decode(readClient()); + sendClient("+ " + base64Encode("Password:")); + state = State.PASSWORD; + password = base64Decode(readClient()); session = ExchangeSessionFactory.getInstance(userName, password); sendClient(commandId + " OK Authenticated"); state = State.AUTHENTICATED; @@ -135,8 +137,8 @@ public class ImapConnection extends AbstractConnection { String folderName = BASE64MailboxDecoder.decode(tokens.nextToken()); currentFolder = session.getFolder(folderName); messages = session.getAllMessages(currentFolder.folderUrl); - sendClient("* " + currentFolder.objectCount + " EXISTS"); - sendClient("* " + currentFolder.objectCount + " RECENT"); + sendClient("* " + messages.size() + " EXISTS"); + sendClient("* " + messages.size() + " RECENT"); sendClient("* OK [UIDVALIDITY 1]"); if (messages.size() == 0) { sendClient("* OK [UIDNEXT " + 1 + "]"); @@ -186,42 +188,14 @@ public class ImapConnection extends AbstractConnection { if (currentFolder == null) { sendClient(commandId + " NO no folder selected"); } else { - RangeIterator rangeIterator = new RangeIterator(tokens.nextToken()); + UIDRangeIterator uidRangeIterator = new UIDRangeIterator(tokens.nextToken()); String parameters = null; if (tokens.hasMoreTokens()) { parameters = tokens.nextToken(); } - while (rangeIterator.hasNext()) { - ExchangeSession.Message message = rangeIterator.next(); - if (parameters == null || "FLAGS".equals(parameters)) { - sendClient("* " + (rangeIterator.currentIndex) + " FETCH (UID " + message.getUidAsLong() + " FLAGS (" + (message.getImapFlags()) + "))"); - } else if ("BODYSTRUCTURE".equals(parameters)) { - sendClient("* " + (rangeIterator.currentIndex) + " FETCH (BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"windows-1252\") NIL NIL \"QUOTED-PRINTABLE\" " + message.size + " 50 NIL NIL NIL NIL))"); - // send full message - } else if (parameters.indexOf("BODY[]") >= 0) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - message.write(baos); - baos.close(); - - DavGatewayTray.debug("Message size: " + message.size + " actual size:" + baos.size() + " message+headers: " + (message.size + baos.size())); - sendClient("* " + (rangeIterator.currentIndex) + " FETCH (UID " + message.getUidAsLong() + " RFC822.SIZE " + baos.size() + " BODY[]<0>" + - " {" + baos.size() + "}"); - os.write(baos.toByteArray()); - os.flush(); - sendClient(")"); - } else { - // write headers to byte array - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - HeaderOutputStream headerOutputStream = new HeaderOutputStream(baos); - message.write(headerOutputStream); - baos.close(); - sendClient("* " + (rangeIterator.currentIndex) + " FETCH (UID " + message.getUidAsLong() + " RFC822.SIZE " + headerOutputStream.size() + " BODY[HEADER.FIELDS ()" + - "] {" + baos.size() + "}"); - os.write(baos.toByteArray()); - os.flush(); - sendClient(" FLAGS (" + (message.getImapFlags()) + "))"); - } - + while (uidRangeIterator.hasNext()) { + ExchangeSession.Message message = uidRangeIterator.next(); + handleFetch(message, uidRangeIterator.currentIndex, parameters); } sendClient(commandId + " OK UID FETCH completed"); } @@ -267,21 +241,21 @@ public class ImapConnection extends AbstractConnection { sendClient(commandId + " OK SEARCH completed"); } else if ("store".equalsIgnoreCase(subcommand)) { - RangeIterator rangeIterator = new RangeIterator(tokens.nextToken()); + UIDRangeIterator UIDRangeIterator = new UIDRangeIterator(tokens.nextToken()); String action = tokens.nextToken(); String flags = tokens.nextToken(); - while (rangeIterator.hasNext()) { - ExchangeSession.Message message = rangeIterator.next(); + while (UIDRangeIterator.hasNext()) { + ExchangeSession.Message message = UIDRangeIterator.next(); updateFlags(message, action, flags); - sendClient("* " + (rangeIterator.currentIndex) + " FETCH (UID " + message.getUidAsLong() + " FLAGS (" + (message.getImapFlags()) + "))"); + sendClient("* " + (UIDRangeIterator.currentIndex) + " FETCH (UID " + message.getUidAsLong() + " FLAGS (" + (message.getImapFlags()) + "))"); } sendClient(commandId + " OK STORE completed"); } else if ("copy".equalsIgnoreCase(subcommand)) { try { - RangeIterator rangeIterator = new RangeIterator(tokens.nextToken()); + UIDRangeIterator UIDRangeIterator = new UIDRangeIterator(tokens.nextToken()); String targetName = BASE64MailboxDecoder.decode(tokens.nextToken()); - while (rangeIterator.hasNext()) { - ExchangeSession.Message message = rangeIterator.next(); + while (UIDRangeIterator.hasNext()) { + ExchangeSession.Message message = UIDRangeIterator.next(); session.copyMessage(message.messageUrl, targetName); } sendClient(commandId + " OK copy completed"); @@ -292,6 +266,23 @@ public class ImapConnection extends AbstractConnection { } else { sendClient(commandId + " BAD command unrecognized"); } + } else if ("fetch".equalsIgnoreCase(command)) { + if (currentFolder == null) { + sendClient(commandId + " NO no folder selected"); + } else { + RangeIterator rangeIterator = new RangeIterator(tokens.nextToken()); + String parameters = null; + if (tokens.hasMoreTokens()) { + parameters = tokens.nextToken(); + } + while (rangeIterator.hasNext()) { + ExchangeSession.Message message = rangeIterator.next(); + handleFetch(message, rangeIterator.currentIndex, parameters); + } + sendClient(commandId + " OK FETCH completed"); + } + + } else if ("append".equalsIgnoreCase(command)) { String folderName = BASE64MailboxDecoder.decode(tokens.nextToken()); String flags = tokens.nextToken(); @@ -345,8 +336,8 @@ public class ImapConnection extends AbstractConnection { if (currentFolder != null) { currentFolder = session.getFolder(currentFolder.folderName); messages = session.getAllMessages(currentFolder.folderUrl); - sendClient("* " + currentFolder.objectCount + " EXISTS"); - sendClient("* " + currentFolder.objectCount + " RECENT"); + sendClient("* " + messages.size() + " EXISTS"); + sendClient("* " + messages.size() + " RECENT"); } sendClient(commandId + " OK " + command + " completed"); } else if ("subscribe".equalsIgnoreCase(command) || "unsubscribe".equalsIgnoreCase(command)) { @@ -356,23 +347,23 @@ public class ImapConnection extends AbstractConnection { String encodedFolderName = tokens.nextToken(); String folderName = BASE64MailboxDecoder.decode(encodedFolderName); ExchangeSession.Folder folder = session.getFolder(folderName); + // must retrieve messages + ExchangeSession.MessageList localMessages = session.getAllMessages(folder.folderUrl); String parameters = tokens.nextToken(); StringBuilder answer = new StringBuilder(); StringTokenizer parametersTokens = new StringTokenizer(parameters); while (parametersTokens.hasMoreTokens()) { String token = parametersTokens.nextToken(); if ("MESSAGES".equalsIgnoreCase(token)) { - answer.append("MESSAGES ").append(folder.objectCount).append(" "); + answer.append("MESSAGES ").append(localMessages.size()).append(" "); } if ("RECENT".equalsIgnoreCase(token)) { - answer.append("RECENT ").append(folder.objectCount).append(" "); + answer.append("RECENT ").append(localMessages.size()).append(" "); } if ("UIDNEXT".equalsIgnoreCase(token)) { if (folder.objectCount == 0) { answer.append("UIDNEXT 1 "); } else { - // must retrieve messages - ExchangeSession.MessageList localMessages = session.getAllMessages(folder.folderUrl); if (localMessages.size() == 0) { answer.append("UIDNEXT 1 "); } else { @@ -435,6 +426,37 @@ public class ImapConnection extends AbstractConnection { DavGatewayTray.resetIcon(); } + private void handleFetch(ExchangeSession.Message message, int currentIndex, String parameters) throws IOException { + if (parameters == null || "FLAGS".equals(parameters) || "FLAGS UID".equals(parameters)) { + sendClient("* " + (currentIndex) + " FETCH (UID " + message.getUidAsLong() + " FLAGS (" + (message.getImapFlags()) + "))"); + } else if ("BODYSTRUCTURE".equals(parameters)) { + sendClient("* " + (currentIndex) + " FETCH (UID " + message.getUidAsLong() + " BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"windows-1252\") NIL NIL \"QUOTED-PRINTABLE\" " + message.size + " 50 NIL NIL NIL NIL))"); + // send full message + } else if (parameters.indexOf("BODY[]") >= 0 || parameters.indexOf("BODY.PEEK[]") >= 0 || "BODY.PEEK[TEXT]".equals(parameters)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + message.write(baos); + baos.close(); + + DavGatewayTray.debug("Message size: " + message.size + " actual size:" + baos.size() + " message+headers: " + (message.size + baos.size())); + sendClient("* " + (currentIndex) + " FETCH (UID " + message.getUidAsLong() + " RFC822.SIZE " + baos.size() + " BODY[]<0>" + + " {" + baos.size() + "}"); + os.write(baos.toByteArray()); + os.flush(); + sendClient(")"); + } else { + // write headers to byte array + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + HeaderOutputStream headerOutputStream = new HeaderOutputStream(baos); + message.write(headerOutputStream); + baos.close(); + sendClient("* " + (currentIndex) + " FETCH (UID " + message.getUidAsLong() + " RFC822.SIZE " + headerOutputStream.size() + " BODY[HEADER.FIELDS ()" + + "] {" + baos.size() + "}"); + os.write(baos.toByteArray()); + os.flush(); + sendClient(" FLAGS (" + (message.getImapFlags()) + "))"); + } + } + static final class SearchConditions { Boolean flagged = null; Boolean answered = null; @@ -688,14 +710,14 @@ public class ImapConnection extends AbstractConnection { } } - protected class RangeIterator implements Iterator { + protected class UIDRangeIterator implements Iterator { String[] ranges; int currentIndex = 0; int currentRangeIndex = 0; long startUid; long endUid; - protected RangeIterator(String value) { + protected UIDRangeIterator(String value) { ranges = value.split(","); } @@ -741,6 +763,59 @@ public class ImapConnection extends AbstractConnection { } } + protected class RangeIterator implements Iterator { + String[] ranges; + int currentIndex = 0; + int currentRangeIndex = 0; + long startUid; + long endUid; + + protected RangeIterator(String value) { + ranges = value.split(","); + } + + protected long convertToLong(String value) { + if ("*".equals(value)) { + return Long.MAX_VALUE; + } else { + return Long.parseLong(value); + } + } + + protected void skipToStartUid() { + if (currentRangeIndex < ranges.length) { + String currentRange = ranges[currentRangeIndex++]; + int colonIndex = currentRange.indexOf(':'); + if (colonIndex > 0) { + startUid = convertToLong(currentRange.substring(0, colonIndex)); + endUid = convertToLong(currentRange.substring(colonIndex + 1)); + } else { + startUid = endUid = convertToLong(currentRange); + } + while (currentIndex < messages.size() && (currentIndex + 1) < startUid) { + currentIndex++; + } + } else { + currentIndex = messages.size(); + } + } + + public boolean hasNext() { + if (currentIndex < messages.size() && (currentIndex + 1) > endUid) { + skipToStartUid(); + } + return currentIndex < messages.size(); + } + + public ExchangeSession.Message next() { + return messages.get(currentIndex++); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + class IMAPTokenizer extends StringTokenizer { public IMAPTokenizer(String value) { super(value);