2009-07-21 04:39:18 -04:00
|
|
|
/*
|
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
|
|
|
* Copyright (C) 2009 Mickael Guessant
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
package davmail.imap;
|
|
|
|
|
2009-01-29 17:18:53 -05:00
|
|
|
import com.sun.mail.imap.protocol.BASE64MailboxDecoder;
|
|
|
|
import com.sun.mail.imap.protocol.BASE64MailboxEncoder;
|
2006-12-12 18:57:24 -05:00
|
|
|
import davmail.AbstractConnection;
|
2009-04-23 10:54:06 -04:00
|
|
|
import davmail.BundleMessage;
|
2010-04-23 10:43:04 -04:00
|
|
|
import davmail.Settings;
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.exception.DavMailException;
|
2009-09-25 05:13:02 -04:00
|
|
|
import davmail.exception.HttpForbiddenException;
|
|
|
|
import davmail.exception.HttpNotFoundException;
|
2010-08-24 05:28:40 -04:00
|
|
|
import davmail.exception.InsufficientStorageException;
|
2006-12-12 18:57:24 -05:00
|
|
|
import davmail.exchange.ExchangeSession;
|
2008-10-31 13:12:30 -04:00
|
|
|
import davmail.exchange.ExchangeSessionFactory;
|
2009-04-03 03:38:31 -04:00
|
|
|
import davmail.ui.tray.DavGatewayTray;
|
2010-08-20 05:12:29 -04:00
|
|
|
import davmail.util.IOUtil;
|
2010-03-22 07:08:12 -04:00
|
|
|
import davmail.util.StringUtil;
|
2009-01-26 18:51:08 -05:00
|
|
|
import org.apache.commons.httpclient.HttpException;
|
2010-08-24 04:16:32 -04:00
|
|
|
import org.apache.log4j.Logger;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2009-03-02 10:38:02 -05:00
|
|
|
import javax.mail.MessagingException;
|
2009-10-30 07:14:47 -04:00
|
|
|
import javax.mail.internet.*;
|
2010-08-20 05:12:29 -04:00
|
|
|
import javax.mail.util.SharedByteArrayInputStream;
|
2009-03-02 10:38:02 -05:00
|
|
|
import java.io.*;
|
2009-01-29 17:18:53 -05:00
|
|
|
import java.net.Socket;
|
2009-02-03 12:56:38 -05:00
|
|
|
import java.net.SocketException;
|
2009-10-30 07:14:47 -04:00
|
|
|
import java.net.SocketTimeoutException;
|
2009-02-17 17:59:32 -05:00
|
|
|
import java.text.ParseException;
|
2009-10-30 07:14:47 -04:00
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.*;
|
2009-01-29 17:18:53 -05:00
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* Dav Gateway smtp connection implementation.
|
|
|
|
* Still alpha code : need to find a way to handle message ids
|
|
|
|
*/
|
|
|
|
public class ImapConnection extends AbstractConnection {
|
2010-08-24 04:16:32 -04:00
|
|
|
private static final Logger LOGGER = Logger.getLogger(ImapConnection.class);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2010-08-31 20:15:36 -04:00
|
|
|
protected String baseMailboxPath;
|
2009-01-22 19:59:41 -05:00
|
|
|
ExchangeSession.Folder currentFolder;
|
|
|
|
|
2009-08-11 12:01:21 -04:00
|
|
|
/**
|
|
|
|
* Initialize the streams and start the thread.
|
|
|
|
*
|
|
|
|
* @param clientSocket IMAP client socket
|
|
|
|
*/
|
2006-12-14 10:14:18 -05:00
|
|
|
public ImapConnection(Socket clientSocket) {
|
2009-05-04 18:31:06 -04:00
|
|
|
super(ImapConnection.class.getSimpleName(), clientSocket, null);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-05-15 08:39:15 -04:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2010-04-23 10:43:04 -04:00
|
|
|
final String capabilities;
|
|
|
|
int imapIdleDelay = Settings.getIntProperty("davmail.imapIdleDelay") * 60;
|
|
|
|
if (imapIdleDelay > 0) {
|
|
|
|
capabilities = "CAPABILITY IMAP4REV1 AUTH=LOGIN IDLE";
|
|
|
|
} else {
|
|
|
|
capabilities = "CAPABILITY IMAP4REV1 AUTH=LOGIN";
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
String line;
|
2009-02-03 12:56:38 -05:00
|
|
|
String commandId = null;
|
2009-03-16 20:25:10 -04:00
|
|
|
IMAPTokenizer tokens;
|
2006-12-12 18:57:24 -05:00
|
|
|
try {
|
2009-02-04 15:38:07 -05:00
|
|
|
ExchangeSessionFactory.checkConfig();
|
2010-05-04 04:37:19 -04:00
|
|
|
sendClient("* OK [" + capabilities + "] IMAP4rev1 DavMail server ready");
|
2006-12-12 18:57:24 -05:00
|
|
|
for (; ;) {
|
|
|
|
line = readClient();
|
|
|
|
// unable to read line, connection closed ?
|
|
|
|
if (line == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-17 17:59:32 -05:00
|
|
|
tokens = new IMAPTokenizer(line);
|
2006-12-12 18:57:24 -05:00
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-02-03 12:56:38 -05:00
|
|
|
commandId = tokens.nextToken();
|
2006-12-12 18:57:24 -05:00
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
String command = tokens.nextToken();
|
|
|
|
|
|
|
|
if ("LOGOUT".equalsIgnoreCase(command)) {
|
|
|
|
sendClient("* BYE Closing connection");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ("capability".equalsIgnoreCase(command)) {
|
2010-05-04 04:37:19 -04:00
|
|
|
sendClient("* " + capabilities);
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient(commandId + " OK CAPABILITY completed");
|
|
|
|
} else if ("login".equalsIgnoreCase(command)) {
|
|
|
|
parseCredentials(tokens);
|
2010-08-31 20:15:36 -04:00
|
|
|
// detect shared mailbox access
|
|
|
|
splitUserName();
|
2006-12-12 18:57:24 -05:00
|
|
|
try {
|
2008-10-31 13:12:30 -04:00
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient(commandId + " OK Authenticated");
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2006-12-12 18:57:24 -05:00
|
|
|
} catch (Exception e) {
|
2008-11-24 07:35:58 -05:00
|
|
|
DavGatewayTray.error(e);
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient(commandId + " NO LOGIN failed");
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.INITIAL;
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2009-01-22 19:59:41 -05:00
|
|
|
} else if ("AUTHENTICATE".equalsIgnoreCase(command)) {
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
String authenticationMethod = tokens.nextToken();
|
|
|
|
if ("LOGIN".equalsIgnoreCase(authenticationMethod)) {
|
|
|
|
try {
|
2009-02-20 12:06:03 -05:00
|
|
|
sendClient("+ " + base64Encode("Username:"));
|
|
|
|
state = State.LOGIN;
|
|
|
|
userName = base64Decode(readClient());
|
2010-08-31 20:15:36 -04:00
|
|
|
// detect shared mailbox access
|
|
|
|
splitUserName();
|
2009-02-20 12:06:03 -05:00
|
|
|
sendClient("+ " + base64Encode("Password:"));
|
|
|
|
state = State.PASSWORD;
|
|
|
|
password = base64Decode(readClient());
|
2009-01-22 19:59:41 -05:00
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
|
|
|
sendClient(commandId + " OK Authenticated");
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2009-01-22 19:59:41 -05:00
|
|
|
} catch (Exception e) {
|
|
|
|
DavGatewayTray.error(e);
|
|
|
|
sendClient(commandId + " NO LOGIN failed");
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.INITIAL;
|
2009-01-22 19:59:41 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " NO unsupported authentication method");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD authentication method required");
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
2009-01-29 17:18:53 -05:00
|
|
|
if (state != State.AUTHENTICATED) {
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient(commandId + " BAD command authentication required");
|
|
|
|
} else {
|
2009-09-22 11:55:31 -04:00
|
|
|
// check for expired session
|
|
|
|
session = ExchangeSessionFactory.getInstance(session, userName, password);
|
2009-01-23 05:30:48 -05:00
|
|
|
if ("lsub".equalsIgnoreCase(command) || "list".equalsIgnoreCase(command)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
if (tokens.hasMoreTokens()) {
|
2010-09-27 05:37:59 -04:00
|
|
|
String folderContext;
|
|
|
|
if (baseMailboxPath == null) {
|
|
|
|
folderContext = BASE64MailboxDecoder.decode(tokens.nextToken());
|
|
|
|
} else {
|
|
|
|
folderContext = baseMailboxPath + BASE64MailboxDecoder.decode(tokens.nextToken());
|
2010-08-31 20:15:36 -04:00
|
|
|
}
|
2009-01-22 19:59:41 -05:00
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-01-29 17:18:53 -05:00
|
|
|
String folderQuery = folderContext + BASE64MailboxDecoder.decode(tokens.nextToken());
|
2009-09-25 05:13:02 -04:00
|
|
|
if (folderQuery.endsWith("%/%") && !"/%/%".equals(folderQuery)) {
|
|
|
|
List<ExchangeSession.Folder> folders = session.getSubFolders(folderQuery.substring(0, folderQuery.length() - 3), false);
|
|
|
|
for (ExchangeSession.Folder folder : folders) {
|
|
|
|
sendClient("* " + command + " (" + folder.getFlags() + ") \"/\" \"" + BASE64MailboxEncoder.encode(folder.folderPath) + '\"');
|
2009-09-25 07:50:46 -04:00
|
|
|
sendSubFolders(command, folder.folderPath, false);
|
2009-09-25 05:13:02 -04:00
|
|
|
}
|
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
|
|
|
} else if (folderQuery.endsWith("%") || folderQuery.endsWith("*")) {
|
|
|
|
if ("/*".equals(folderQuery) || "/%".equals(folderQuery) || "/%/%".equals(folderQuery)) {
|
|
|
|
folderQuery = folderQuery.substring(1);
|
|
|
|
if ("%/%".equals(folderQuery)) {
|
|
|
|
folderQuery = folderQuery.substring(0, folderQuery.length() - 2);
|
|
|
|
}
|
|
|
|
sendClient("* " + command + " (\\HasChildren) \"/\" \"/public\"");
|
|
|
|
}
|
2010-01-20 17:43:38 -05:00
|
|
|
if ("*%".equals(folderQuery)) {
|
|
|
|
folderQuery = "*";
|
|
|
|
}
|
2009-09-25 05:13:02 -04:00
|
|
|
boolean recursive = folderQuery.endsWith("*") && !folderQuery.startsWith("/public");
|
2009-09-25 07:50:46 -04:00
|
|
|
sendSubFolders(command, folderQuery.substring(0, folderQuery.length() - 1), recursive);
|
2009-01-23 05:30:48 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
2009-01-22 19:59:41 -05:00
|
|
|
} else {
|
2009-09-25 05:13:02 -04:00
|
|
|
ExchangeSession.Folder folder = null;
|
|
|
|
try {
|
2009-09-25 05:31:01 -04:00
|
|
|
folder = session.getFolder(folderQuery);
|
2009-09-25 05:13:02 -04:00
|
|
|
} catch (HttpForbiddenException e) {
|
|
|
|
// access forbidden, ignore
|
2009-09-25 07:50:46 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOLDER_ACCESS_FORBIDDEN", folderQuery));
|
2009-09-25 05:13:02 -04:00
|
|
|
} catch (HttpNotFoundException e) {
|
|
|
|
// not found, ignore
|
2009-09-25 07:50:46 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOLDER_NOT_FOUND", folderQuery));
|
|
|
|
} catch (HttpException e) {
|
|
|
|
// other errors, ignore
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOLDER_ACCESS_ERROR", folderQuery, e.getMessage()));
|
2009-09-25 05:13:02 -04:00
|
|
|
}
|
2009-01-22 19:59:41 -05:00
|
|
|
if (folder != null) {
|
2009-07-31 02:21:46 -04:00
|
|
|
sendClient("* " + command + " (" + folder.getFlags() + ") \"/\" \"" + BASE64MailboxEncoder.encode(folder.folderPath) + '\"');
|
2009-01-23 05:30:48 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
2009-01-22 19:59:41 -05:00
|
|
|
} else {
|
|
|
|
sendClient(commandId + " NO Folder not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD missing folder argument");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD missing folder argument");
|
|
|
|
}
|
|
|
|
} else if ("select".equalsIgnoreCase(command) || "examine".equalsIgnoreCase(command)) {
|
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-01-29 17:18:53 -05:00
|
|
|
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
2010-08-31 20:15:36 -04:00
|
|
|
if (baseMailboxPath != null && !folderName.startsWith("/")) {
|
|
|
|
folderName = baseMailboxPath + folderName;
|
|
|
|
}
|
2009-09-25 05:13:02 -04:00
|
|
|
try {
|
|
|
|
currentFolder = session.getFolder(folderName);
|
|
|
|
currentFolder.loadMessages();
|
|
|
|
sendClient("* " + currentFolder.count() + " EXISTS");
|
|
|
|
sendClient("* " + currentFolder.count() + " RECENT");
|
|
|
|
sendClient("* OK [UIDVALIDITY 1]");
|
|
|
|
if (currentFolder.count() == 0) {
|
2009-12-20 05:51:02 -05:00
|
|
|
sendClient("* OK [UIDNEXT 1]");
|
2009-09-25 05:13:02 -04:00
|
|
|
} else {
|
|
|
|
sendClient("* OK [UIDNEXT " + currentFolder.getUidNext() + ']');
|
|
|
|
}
|
|
|
|
sendClient("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded Junk)");
|
2010-04-01 18:21:48 -04:00
|
|
|
sendClient("* OK [PERMANENTFLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded Junk)]");
|
2009-09-25 05:13:02 -04:00
|
|
|
if ("select".equalsIgnoreCase(command)) {
|
|
|
|
sendClient(commandId + " OK [READ-WRITE] " + command + " completed");
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " OK [READ-ONLY] " + command + " completed");
|
|
|
|
}
|
2009-09-28 20:10:45 -04:00
|
|
|
} catch (HttpNotFoundException e) {
|
|
|
|
sendClient(commandId + " NO Not found");
|
2009-09-25 05:13:02 -04:00
|
|
|
} catch (HttpForbiddenException e) {
|
|
|
|
sendClient(commandId + " NO Forbidden");
|
2009-03-26 17:54:50 -04:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD command unrecognized");
|
|
|
|
}
|
2010-01-21 05:49:23 -05:00
|
|
|
} else if ("expunge".equalsIgnoreCase(command)) {
|
2010-05-10 05:19:25 -04:00
|
|
|
if (expunge(false)) {
|
|
|
|
// need to refresh folder to avoid 404 errors
|
|
|
|
session.refreshFolder(currentFolder);
|
|
|
|
}
|
2010-01-21 05:49:23 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
|
|
|
} else if ("close".equalsIgnoreCase(command)) {
|
|
|
|
expunge(true);
|
|
|
|
// deselect folder
|
|
|
|
currentFolder = null;
|
2009-02-05 12:50:15 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
2009-01-22 19:59:41 -05:00
|
|
|
} else if ("create".equalsIgnoreCase(command)) {
|
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-01-29 17:18:53 -05:00
|
|
|
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
2009-10-27 19:34:13 -04:00
|
|
|
session.createMessageFolder(folderName);
|
2009-01-23 08:01:46 -05:00
|
|
|
sendClient(commandId + " OK folder created");
|
2009-01-22 19:59:41 -05:00
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD missing create argument");
|
|
|
|
}
|
2009-01-26 18:51:08 -05:00
|
|
|
} else if ("rename".equalsIgnoreCase(command)) {
|
2009-01-29 17:18:53 -05:00
|
|
|
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
|
|
|
String targetName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
2009-01-26 18:51:08 -05:00
|
|
|
try {
|
|
|
|
session.moveFolder(folderName, targetName);
|
|
|
|
sendClient(commandId + " OK rename completed");
|
|
|
|
} catch (HttpException e) {
|
2009-04-01 18:58:03 -04:00
|
|
|
sendClient(commandId + " NO " + e.getMessage());
|
2009-01-26 18:51:08 -05:00
|
|
|
}
|
2009-02-02 18:29:44 -05:00
|
|
|
} else if ("delete".equalsIgnoreCase(command)) {
|
|
|
|
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
|
|
|
try {
|
|
|
|
session.deleteFolder(folderName);
|
2010-07-02 02:37:18 -04:00
|
|
|
sendClient(commandId + " OK folder deleted");
|
2009-02-02 18:29:44 -05:00
|
|
|
} catch (HttpException e) {
|
2009-04-01 18:58:03 -04:00
|
|
|
sendClient(commandId + " NO " + e.getMessage());
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else if ("uid".equalsIgnoreCase(command)) {
|
2009-01-23 06:20:20 -05:00
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
String subcommand = tokens.nextToken();
|
|
|
|
if ("fetch".equalsIgnoreCase(subcommand)) {
|
2009-02-04 17:17:34 -05:00
|
|
|
if (currentFolder == null) {
|
|
|
|
sendClient(commandId + " NO no folder selected");
|
|
|
|
} else {
|
2009-05-15 08:39:15 -04:00
|
|
|
String ranges = tokens.nextToken();
|
|
|
|
if (ranges == null) {
|
|
|
|
sendClient(commandId + " BAD missing range parameter");
|
|
|
|
} else {
|
|
|
|
String parameters = null;
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
parameters = tokens.nextToken();
|
|
|
|
}
|
2010-01-21 05:49:23 -05:00
|
|
|
UIDRangeIterator uidRangeIterator = new UIDRangeIterator(currentFolder.messages, ranges);
|
2010-01-05 05:06:47 -05:00
|
|
|
while (uidRangeIterator.hasNext()) {
|
|
|
|
DavGatewayTray.switchIcon();
|
|
|
|
ExchangeSession.Message message = uidRangeIterator.next();
|
|
|
|
try {
|
|
|
|
handleFetch(message, uidRangeIterator.currentIndex, parameters);
|
2010-08-24 04:16:32 -04:00
|
|
|
} catch (SocketException e) {
|
|
|
|
// client closed connection
|
|
|
|
throw e;
|
2010-01-05 05:06:47 -05:00
|
|
|
} catch (IOException e) {
|
|
|
|
DavGatewayTray.log(e);
|
|
|
|
sendClient(commandId + " NO Unable to retrieve message: " + e.getMessage());
|
2009-05-15 08:39:15 -04:00
|
|
|
}
|
|
|
|
}
|
2010-01-05 05:06:47 -05:00
|
|
|
sendClient(commandId + " OK UID FETCH completed");
|
2009-01-23 05:30:48 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2009-02-04 17:17:34 -05:00
|
|
|
|
2009-01-23 06:20:20 -05:00
|
|
|
} else if ("search".equalsIgnoreCase(subcommand)) {
|
2009-10-27 19:34:13 -04:00
|
|
|
List<Long> uidList = handleSearch(tokens);
|
2010-06-02 18:18:21 -04:00
|
|
|
if (uidList.isEmpty()) {
|
|
|
|
sendClient("* SEARCH");
|
|
|
|
} else {
|
|
|
|
for (long uid : uidList) {
|
|
|
|
sendClient("* SEARCH " + uid);
|
|
|
|
}
|
2009-01-23 06:20:20 -05:00
|
|
|
}
|
|
|
|
sendClient(commandId + " OK SEARCH completed");
|
|
|
|
|
|
|
|
} else if ("store".equalsIgnoreCase(subcommand)) {
|
2010-01-21 05:49:23 -05:00
|
|
|
UIDRangeIterator uidRangeIterator = new UIDRangeIterator(currentFolder.messages, tokens.nextToken());
|
2009-01-26 18:51:08 -05:00
|
|
|
String action = tokens.nextToken();
|
|
|
|
String flags = tokens.nextToken();
|
2009-10-29 13:19:00 -04:00
|
|
|
handleStore(commandId, uidRangeIterator, action, flags);
|
2009-02-02 18:29:44 -05:00
|
|
|
} else if ("copy".equalsIgnoreCase(subcommand)) {
|
|
|
|
try {
|
2010-01-21 05:49:23 -05:00
|
|
|
UIDRangeIterator uidRangeIterator = new UIDRangeIterator(currentFolder.messages, tokens.nextToken());
|
2009-02-03 11:18:29 -05:00
|
|
|
String targetName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
2009-09-21 17:34:13 -04:00
|
|
|
while (uidRangeIterator.hasNext()) {
|
2009-09-24 05:18:57 -04:00
|
|
|
DavGatewayTray.switchIcon();
|
2009-09-21 17:34:13 -04:00
|
|
|
ExchangeSession.Message message = uidRangeIterator.next();
|
2009-07-31 02:21:46 -04:00
|
|
|
session.copyMessage(message, targetName);
|
2009-02-03 11:18:29 -05:00
|
|
|
}
|
2009-02-04 16:55:42 -05:00
|
|
|
sendClient(commandId + " OK copy completed");
|
|
|
|
} catch (HttpException e) {
|
2009-04-01 18:58:03 -04:00
|
|
|
sendClient(commandId + " NO " + e.getMessage());
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD command unrecognized");
|
|
|
|
}
|
2009-10-27 19:34:13 -04:00
|
|
|
} else if ("search".equalsIgnoreCase(command)) {
|
|
|
|
if (currentFolder == null) {
|
|
|
|
sendClient(commandId + " NO no folder selected");
|
|
|
|
} else {
|
|
|
|
List<Long> uidList = handleSearch(tokens);
|
2010-06-02 18:18:21 -04:00
|
|
|
if (uidList.isEmpty()) {
|
|
|
|
sendClient("* SEARCH");
|
|
|
|
} else {
|
|
|
|
int currentIndex = 0;
|
|
|
|
for (ExchangeSession.Message message : currentFolder.messages) {
|
|
|
|
currentIndex++;
|
|
|
|
if (uidList.contains(message.getImapUid())) {
|
|
|
|
sendClient("* SEARCH " + currentIndex);
|
|
|
|
}
|
2009-10-27 19:34:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sendClient(commandId + " OK SEARCH completed");
|
|
|
|
}
|
2009-02-20 12:06:03 -05:00
|
|
|
} else if ("fetch".equalsIgnoreCase(command)) {
|
|
|
|
if (currentFolder == null) {
|
|
|
|
sendClient(commandId + " NO no folder selected");
|
|
|
|
} else {
|
2010-01-05 05:06:47 -05:00
|
|
|
RangeIterator rangeIterator = new RangeIterator(currentFolder.messages, tokens.nextToken());
|
2009-02-20 12:06:03 -05:00
|
|
|
String parameters = null;
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
parameters = tokens.nextToken();
|
|
|
|
}
|
|
|
|
while (rangeIterator.hasNext()) {
|
2009-09-24 05:18:57 -04:00
|
|
|
DavGatewayTray.switchIcon();
|
2009-02-20 12:06:03 -05:00
|
|
|
ExchangeSession.Message message = rangeIterator.next();
|
2009-12-07 04:35:25 -05:00
|
|
|
try {
|
|
|
|
handleFetch(message, rangeIterator.currentIndex, parameters);
|
2010-01-24 15:41:02 -05:00
|
|
|
} catch (SocketException e) {
|
2010-01-27 17:08:05 -05:00
|
|
|
// client closed connection, rethrow exception
|
|
|
|
throw e;
|
2009-12-07 04:35:25 -05:00
|
|
|
} catch (IOException e) {
|
|
|
|
DavGatewayTray.log(e);
|
2010-01-05 05:06:47 -05:00
|
|
|
sendClient(commandId + " NO Unable to retrieve message: " + e.getMessage());
|
2009-12-07 04:35:25 -05:00
|
|
|
}
|
|
|
|
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
|
|
|
sendClient(commandId + " OK FETCH completed");
|
|
|
|
}
|
|
|
|
|
2009-10-29 13:19:00 -04:00
|
|
|
} else if ("store".equalsIgnoreCase(command)) {
|
2010-01-05 05:06:47 -05:00
|
|
|
RangeIterator rangeIterator = new RangeIterator(currentFolder.messages, tokens.nextToken());
|
2009-10-29 13:19:00 -04:00
|
|
|
String action = tokens.nextToken();
|
|
|
|
String flags = tokens.nextToken();
|
|
|
|
handleStore(commandId, rangeIterator, action, flags);
|
2009-02-20 12:06:03 -05:00
|
|
|
|
2009-01-23 05:50:46 -05:00
|
|
|
} else if ("append".equalsIgnoreCase(command)) {
|
2009-01-29 17:18:53 -05:00
|
|
|
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
2009-02-03 18:54:48 -05:00
|
|
|
HashMap<String, String> properties = new HashMap<String, String>();
|
2009-03-16 20:25:10 -04:00
|
|
|
String flags = null;
|
|
|
|
String date = null;
|
|
|
|
// handle optional flags
|
|
|
|
String nextToken = tokens.nextQuotedToken();
|
|
|
|
if (nextToken.startsWith("(")) {
|
|
|
|
flags = removeQuotes(nextToken);
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
nextToken = tokens.nextToken();
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
date = nextToken;
|
|
|
|
nextToken = tokens.nextToken();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (tokens.hasMoreTokens()) {
|
|
|
|
date = removeQuotes(nextToken);
|
|
|
|
nextToken = tokens.nextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags != null) {
|
2010-07-21 06:18:46 -04:00
|
|
|
// parse flags, on create read and draft flags are on the
|
|
|
|
// same messageFlags property, 8 means draft and 1 means read
|
2009-03-16 20:25:10 -04:00
|
|
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
|
|
|
while (flagtokenizer.hasMoreTokens()) {
|
|
|
|
String flag = flagtokenizer.nextToken();
|
|
|
|
if ("\\Seen".equals(flag)) {
|
2010-07-21 06:18:46 -04:00
|
|
|
if (properties.containsKey("draft")) {
|
|
|
|
// draft message, add read flag
|
|
|
|
properties.put("draft", "9");
|
|
|
|
} else {
|
|
|
|
// not (yet) draft, set read flag
|
|
|
|
properties.put("draft", "1");
|
|
|
|
}
|
2009-03-16 20:25:10 -04:00
|
|
|
} else if ("\\Flagged".equals(flag)) {
|
|
|
|
properties.put("flagged", "2");
|
|
|
|
} else if ("\\Answered".equals(flag)) {
|
|
|
|
properties.put("answered", "102");
|
|
|
|
} else if ("$Forwarded".equals(flag)) {
|
|
|
|
properties.put("forwarded", "104");
|
|
|
|
} else if ("\\Draft".equals(flag)) {
|
2010-07-21 06:18:46 -04:00
|
|
|
if (properties.containsKey("draft")) {
|
|
|
|
// read message, add draft flag
|
|
|
|
properties.put("draft", "9");
|
|
|
|
} else {
|
|
|
|
// not (yet) read, set draft flag
|
|
|
|
properties.put("draft", "8");
|
|
|
|
}
|
2009-03-16 20:25:10 -04:00
|
|
|
} else if ("Junk".equals(flag)) {
|
|
|
|
properties.put("junk", "1");
|
|
|
|
}
|
2009-02-03 18:54:48 -05:00
|
|
|
}
|
|
|
|
}
|
2009-02-05 12:15:30 -05:00
|
|
|
// handle optional date
|
2009-03-16 20:25:10 -04:00
|
|
|
if (date != null) {
|
2009-02-05 12:15:30 -05:00
|
|
|
SimpleDateFormat dateParser = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.ENGLISH);
|
2009-03-16 20:25:10 -04:00
|
|
|
Date dateReceived = dateParser.parse(date);
|
2009-02-05 12:15:30 -05:00
|
|
|
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
|
|
|
dateFormatter.setTimeZone(ExchangeSession.GMT_TIMEZONE);
|
|
|
|
|
|
|
|
properties.put("datereceived", dateFormatter.format(dateReceived));
|
2009-02-03 12:56:38 -05:00
|
|
|
}
|
2009-03-16 20:25:10 -04:00
|
|
|
int size = Integer.parseInt(nextToken);
|
2009-01-23 05:50:46 -05:00
|
|
|
sendClient("+ send literal data");
|
2010-07-12 12:35:46 -04:00
|
|
|
byte[] buffer = in.readContent(size);
|
2009-01-23 05:50:46 -05:00
|
|
|
// empty line
|
|
|
|
readClient();
|
2010-08-20 05:12:29 -04:00
|
|
|
MimeMessage mimeMessage = new MimeMessage(null, new SharedByteArrayInputStream(buffer));
|
2009-02-03 09:38:05 -05:00
|
|
|
|
2010-07-20 17:00:49 -04:00
|
|
|
String messageName = UUID.randomUUID().toString() + ".EML";
|
2010-08-24 05:28:40 -04:00
|
|
|
try {
|
|
|
|
session.createMessage(folderName, messageName, properties, mimeMessage);
|
|
|
|
sendClient(commandId + " OK APPEND completed");
|
|
|
|
} catch (InsufficientStorageException e) {
|
|
|
|
sendClient(commandId + " NO " + e.getMessage());
|
|
|
|
}
|
2010-04-23 10:43:04 -04:00
|
|
|
} else if ("idle".equalsIgnoreCase(command) && imapIdleDelay > 0) {
|
2010-04-22 05:41:41 -04:00
|
|
|
sendClient("+ idling ");
|
2010-04-22 17:50:16 -04:00
|
|
|
// clear cache before going to idle mode
|
|
|
|
currentFolder.clearCache();
|
|
|
|
DavGatewayTray.resetIcon();
|
2010-04-23 10:59:30 -04:00
|
|
|
try {
|
|
|
|
int count = 0;
|
2010-07-12 12:35:46 -04:00
|
|
|
while (in.available() == 0) {
|
2010-04-23 10:59:30 -04:00
|
|
|
if (++count >= imapIdleDelay) {
|
|
|
|
count = 0;
|
|
|
|
List<Long> previousImapUidList = currentFolder.getImapUidList();
|
|
|
|
if (session.refreshFolder(currentFolder)) {
|
|
|
|
handleRefresh(previousImapUidList, currentFolder.getImapUidList());
|
|
|
|
}
|
2010-04-22 17:50:16 -04:00
|
|
|
}
|
2010-04-23 10:59:30 -04:00
|
|
|
// sleep 1 second
|
|
|
|
Thread.sleep(1000);
|
2010-04-22 05:41:41 -04:00
|
|
|
}
|
2010-04-23 10:59:30 -04:00
|
|
|
// read DONE line
|
|
|
|
line = readClient();
|
|
|
|
if ("DONE".equals(line)) {
|
|
|
|
sendClient(commandId + " OK " + command + " terminated");
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD command unrecognized");
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
// client connection closed
|
|
|
|
throw new SocketException(e.getMessage());
|
2010-04-22 05:41:41 -04:00
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
} else if ("noop".equalsIgnoreCase(command) || "check".equalsIgnoreCase(command)) {
|
2009-01-23 06:20:20 -05:00
|
|
|
if (currentFolder != null) {
|
2010-06-10 16:47:55 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_IMAP_COMMAND", command, currentFolder.folderPath));
|
2010-04-22 17:50:16 -04:00
|
|
|
List<Long> previousImapUidList = currentFolder.getImapUidList();
|
2009-12-01 05:14:59 -05:00
|
|
|
if (session.refreshFolder(currentFolder)) {
|
2010-04-22 17:50:16 -04:00
|
|
|
handleRefresh(previousImapUidList, currentFolder.getImapUidList());
|
2009-12-01 05:14:59 -05:00
|
|
|
}
|
2009-01-23 06:20:20 -05:00
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
2009-02-05 11:06:34 -05:00
|
|
|
} else if ("subscribe".equalsIgnoreCase(command) || "unsubscribe".equalsIgnoreCase(command)) {
|
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
2009-02-05 12:50:15 -05:00
|
|
|
} else if ("status".equalsIgnoreCase(command)) {
|
|
|
|
try {
|
|
|
|
String encodedFolderName = tokens.nextToken();
|
|
|
|
String folderName = BASE64MailboxDecoder.decode(encodedFolderName);
|
|
|
|
ExchangeSession.Folder folder = session.getFolder(folderName);
|
2009-02-20 12:06:03 -05:00
|
|
|
// must retrieve messages
|
2009-03-27 06:31:52 -04:00
|
|
|
folder.loadMessages();
|
2009-02-05 12:50:15 -05:00
|
|
|
String parameters = tokens.nextToken();
|
|
|
|
StringBuilder answer = new StringBuilder();
|
|
|
|
StringTokenizer parametersTokens = new StringTokenizer(parameters);
|
|
|
|
while (parametersTokens.hasMoreTokens()) {
|
|
|
|
String token = parametersTokens.nextToken();
|
|
|
|
if ("MESSAGES".equalsIgnoreCase(token)) {
|
2009-07-31 02:21:46 -04:00
|
|
|
answer.append("MESSAGES ").append(folder.count()).append(' ');
|
2009-02-05 12:50:15 -05:00
|
|
|
}
|
|
|
|
if ("RECENT".equalsIgnoreCase(token)) {
|
2009-07-31 02:21:46 -04:00
|
|
|
answer.append("RECENT ").append(folder.count()).append(' ');
|
2009-02-05 12:50:15 -05:00
|
|
|
}
|
|
|
|
if ("UIDNEXT".equalsIgnoreCase(token)) {
|
2009-07-31 02:21:46 -04:00
|
|
|
if (folder.count() == 0) {
|
2009-02-05 12:50:15 -05:00
|
|
|
answer.append("UIDNEXT 1 ");
|
|
|
|
} else {
|
2009-07-31 02:21:46 -04:00
|
|
|
if (folder.count() == 0) {
|
2009-02-05 12:50:15 -05:00
|
|
|
answer.append("UIDNEXT 1 ");
|
|
|
|
} else {
|
2009-04-23 16:53:22 -04:00
|
|
|
answer.append("UIDNEXT ").append(folder.getUidNext()).append(' ');
|
2009-02-05 12:50:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if ("UIDVALIDITY".equalsIgnoreCase(token)) {
|
|
|
|
answer.append("UIDVALIDITY 1 ");
|
|
|
|
}
|
|
|
|
if ("UNSEEN".equalsIgnoreCase(token)) {
|
2009-04-23 16:53:22 -04:00
|
|
|
answer.append("UNSEEN ").append(folder.unreadCount).append(' ');
|
2009-02-05 12:50:15 -05:00
|
|
|
}
|
|
|
|
}
|
2010-03-22 09:29:11 -04:00
|
|
|
sendClient("* STATUS \"" + encodedFolderName + "\" (" + answer.toString().trim() + ')');
|
2009-02-05 12:50:15 -05:00
|
|
|
sendClient(commandId + " OK " + command + " completed");
|
|
|
|
} catch (HttpException e) {
|
|
|
|
sendClient(commandId + " NO folder not found");
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD command unrecognized");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
sendClient(commandId + " BAD missing command");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient("BAD Null command");
|
|
|
|
}
|
2009-04-13 05:36:27 -04:00
|
|
|
DavGatewayTray.resetIcon();
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
os.flush();
|
2009-01-22 19:59:41 -05:00
|
|
|
} catch (SocketTimeoutException e) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CLOSE_CONNECTION_ON_TIMEOUT"));
|
2009-01-22 19:59:41 -05:00
|
|
|
try {
|
|
|
|
sendClient("* BYE Closing connection");
|
|
|
|
} catch (IOException e1) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_CLOSING_CONNECTION_ON_TIMEOUT"));
|
2009-01-22 19:59:41 -05:00
|
|
|
}
|
2009-02-03 12:56:38 -05:00
|
|
|
} catch (SocketException e) {
|
2010-08-24 04:16:32 -04:00
|
|
|
LOGGER.warn(BundleMessage.formatLog("LOG_CLIENT_CLOSED_CONNECTION"));
|
2009-02-03 12:56:38 -05:00
|
|
|
} catch (Exception e) {
|
2009-07-15 18:47:31 -04:00
|
|
|
DavGatewayTray.log(e);
|
2009-02-03 12:56:38 -05:00
|
|
|
try {
|
2009-09-22 05:03:32 -04:00
|
|
|
String message = ((e.getMessage() == null) ? e.toString() : e.getMessage()).replaceAll("\\n", " ");
|
2009-02-03 12:56:38 -05:00
|
|
|
if (commandId != null) {
|
2009-03-26 17:54:50 -04:00
|
|
|
sendClient(commandId + " BAD unable to handle request: " + message);
|
2009-02-03 12:56:38 -05:00
|
|
|
} else {
|
2010-05-04 04:37:19 -04:00
|
|
|
sendClient("* BAD unable to handle request: " + message);
|
2009-02-03 12:56:38 -05:00
|
|
|
}
|
|
|
|
} catch (IOException e2) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.warn(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
2009-02-03 12:56:38 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} finally {
|
2007-05-15 05:37:55 -04:00
|
|
|
close();
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
DavGatewayTray.resetIcon();
|
|
|
|
}
|
|
|
|
|
2010-08-31 20:15:36 -04:00
|
|
|
/**
|
|
|
|
* Detect shared mailbox access.
|
|
|
|
* see http://msexchangeteam.com/archive/2004/03/31/105275.aspx
|
|
|
|
*/
|
|
|
|
protected void splitUserName() {
|
|
|
|
String[] tokens = null;
|
|
|
|
if (userName.indexOf('/') >= 0) {
|
|
|
|
tokens = userName.split("/");
|
|
|
|
} else if (userName.indexOf('\\') >= 0) {
|
|
|
|
tokens = userName.split("\\\\");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tokens != null && tokens.length == 3) {
|
|
|
|
userName = tokens[0] + '\\' + tokens[1];
|
|
|
|
baseMailboxPath = "/users/" + tokens[2] + '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-22 17:50:16 -04:00
|
|
|
/**
|
|
|
|
* Send expunge untagged response for removed IMAP message uids.
|
2010-04-23 10:43:04 -04:00
|
|
|
*
|
2010-04-22 17:50:16 -04:00
|
|
|
* @param previousImapUidList uid list before refresh
|
2010-04-23 10:43:04 -04:00
|
|
|
* @param imapUidList uid list after refresh
|
2010-04-22 17:50:16 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
private void handleRefresh(List<Long> previousImapUidList, List<Long> imapUidList) throws IOException {
|
|
|
|
//
|
2010-04-22 05:41:41 -04:00
|
|
|
int index = 1;
|
2010-04-22 17:50:16 -04:00
|
|
|
for (long previousImapUid : previousImapUidList) {
|
|
|
|
if (!imapUidList.contains(previousImapUid)) {
|
2010-04-22 05:41:41 -04:00
|
|
|
sendClient("* " + index + " EXPUNGE");
|
|
|
|
} else {
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sendClient("* " + currentFolder.count() + " EXISTS");
|
|
|
|
sendClient("* " + currentFolder.count() + " RECENT");
|
|
|
|
}
|
|
|
|
|
2010-03-22 07:08:12 -04:00
|
|
|
private void handleFetch(ExchangeSession.Message message, int currentIndex, String parameters) throws IOException, MessagingException {
|
2009-02-23 12:42:49 -05:00
|
|
|
StringBuilder buffer = new StringBuilder();
|
2009-03-19 08:41:11 -04:00
|
|
|
buffer.append("* ").append(currentIndex).append(" FETCH (UID ").append(message.getImapUid());
|
2009-03-27 06:31:52 -04:00
|
|
|
if (parameters != null) {
|
|
|
|
StringTokenizer paramTokens = new StringTokenizer(parameters);
|
|
|
|
while (paramTokens.hasMoreTokens()) {
|
2010-04-01 05:11:32 -04:00
|
|
|
@SuppressWarnings({"NonConstantStringShouldBeStringBuffer"})
|
2009-03-27 06:31:52 -04:00
|
|
|
String param = paramTokens.nextToken();
|
|
|
|
if ("FLAGS".equals(param)) {
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(" FLAGS (").append(message.getImapFlags()).append(')');
|
2010-03-31 17:47:45 -04:00
|
|
|
} else if ("RFC822.SIZE".equals(param)) {
|
2010-04-23 09:42:37 -04:00
|
|
|
buffer.append(" RFC822.SIZE ").append(message.getMimeMessageSize());
|
2009-10-29 13:19:00 -04:00
|
|
|
} else if ("ENVELOPE".equals(param)) {
|
|
|
|
appendEnvelope(buffer, message);
|
2009-03-27 06:31:52 -04:00
|
|
|
} else if ("BODYSTRUCTURE".equals(param)) {
|
2009-10-30 07:14:47 -04:00
|
|
|
appendBodyStructure(buffer, message);
|
2009-03-27 06:31:52 -04:00
|
|
|
} else if ("INTERNALDATE".equals(param) && message.date != null && message.date.length() > 0) {
|
|
|
|
try {
|
|
|
|
SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
|
|
|
dateParser.setTimeZone(ExchangeSession.GMT_TIMEZONE);
|
2010-07-09 17:25:18 -04:00
|
|
|
Date date = ExchangeSession.getZuluDateFormat().parse(message.date);
|
2009-03-27 06:31:52 -04:00
|
|
|
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.ENGLISH);
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(" INTERNALDATE \"").append(dateFormatter.format(date)).append('\"');
|
2009-03-27 06:31:52 -04:00
|
|
|
} catch (ParseException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_DATE", message.date);
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
2010-03-31 17:47:45 -04:00
|
|
|
} else if (param.startsWith("BODY[") || param.startsWith("BODY.PEEK[") || "RFC822.HEADER".equals(param)) {
|
|
|
|
// get full param
|
|
|
|
if (param.indexOf('[') >= 0) {
|
2010-09-09 15:51:25 -04:00
|
|
|
StringBuilder paramBuffer = new StringBuilder(param);
|
2010-03-31 17:47:45 -04:00
|
|
|
while (paramTokens.hasMoreTokens() && param.indexOf(']') < 0) {
|
2010-09-09 15:51:25 -04:00
|
|
|
paramBuffer.append(' ').append(paramTokens.nextToken());
|
2010-03-31 17:47:45 -04:00
|
|
|
}
|
2010-09-09 15:51:25 -04:00
|
|
|
param = paramBuffer.toString();
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
|
|
|
// parse buffer size
|
|
|
|
int startIndex = 0;
|
2010-03-27 06:18:53 -04:00
|
|
|
int maxSize = Integer.MAX_VALUE;
|
2009-03-27 06:31:52 -04:00
|
|
|
int ltIndex = param.indexOf('<');
|
|
|
|
if (ltIndex >= 0) {
|
|
|
|
int dotIndex = param.indexOf('.', ltIndex);
|
|
|
|
if (dotIndex >= 0) {
|
|
|
|
startIndex = Integer.parseInt(param.substring(ltIndex + 1, dotIndex));
|
2010-04-06 05:20:09 -04:00
|
|
|
maxSize = Integer.parseInt(param.substring(dotIndex + 1, param.indexOf('>')));
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
2009-03-09 20:12:52 -04:00
|
|
|
}
|
|
|
|
|
2009-03-27 06:31:52 -04:00
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
2010-09-13 18:19:25 -04:00
|
|
|
InputStream partInputStream = null;
|
|
|
|
OutputStream partOutputStream = null;
|
2009-04-23 10:54:06 -04:00
|
|
|
|
2010-03-31 17:47:45 -04:00
|
|
|
// load message
|
|
|
|
MimeMessage mimeMessage = message.getMimeMessage();
|
2010-03-22 07:08:12 -04:00
|
|
|
// try to parse message part index
|
|
|
|
String partIndexString = StringUtil.getToken(param, "[", "]");
|
2010-03-31 17:47:45 -04:00
|
|
|
if ("".equals(partIndexString)) {
|
|
|
|
// write message with headers
|
2010-04-23 09:42:37 -04:00
|
|
|
partOutputStream = new PartialOutputStream(baos, startIndex, maxSize);
|
|
|
|
partInputStream = message.getRawInputStream();
|
2010-03-31 17:47:45 -04:00
|
|
|
} else if ("TEXT".equals(partIndexString)) {
|
|
|
|
// write message without headers
|
2010-04-23 09:42:37 -04:00
|
|
|
partOutputStream = new PartialOutputStream(baos, startIndex, maxSize);
|
|
|
|
partInputStream = mimeMessage.getRawInputStream();
|
2010-03-31 17:47:45 -04:00
|
|
|
} else if ("RFC822.HEADER".equals(param) || partIndexString.startsWith("HEADER")) {
|
2010-09-13 18:19:25 -04:00
|
|
|
// Header requested fetch headers
|
|
|
|
String[] requestedHeaders = getRequestedHeaders(partIndexString);
|
|
|
|
if (requestedHeaders != null) {
|
|
|
|
Enumeration headerEnumeration = message.getMimeMessage().getMatchingHeaderLines(requestedHeaders);
|
|
|
|
while (headerEnumeration.hasMoreElements()) {
|
|
|
|
baos.write(((String) headerEnumeration.nextElement()).getBytes("UTF-8"));
|
|
|
|
baos.write(13);
|
|
|
|
baos.write(10);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// write headers only
|
|
|
|
partOutputStream = new PartOutputStream(baos, true, false, startIndex, maxSize);
|
|
|
|
partInputStream = message.getRawInputStream();
|
|
|
|
}
|
2010-03-31 17:47:45 -04:00
|
|
|
} else {
|
2010-04-06 04:31:53 -04:00
|
|
|
MimePart bodyPart = mimeMessage;
|
|
|
|
String[] partIndexStrings = partIndexString.split("\\.");
|
2010-04-23 10:43:04 -04:00
|
|
|
for (String subPartIndexString : partIndexStrings) {
|
2010-07-02 02:45:12 -04:00
|
|
|
// ignore MIME subpart index, will return full part
|
|
|
|
if ("MIME".equals(subPartIndexString)) {
|
|
|
|
break;
|
|
|
|
}
|
2010-04-06 04:31:53 -04:00
|
|
|
int subPartIndex;
|
|
|
|
// try to parse part index
|
|
|
|
try {
|
|
|
|
subPartIndex = Integer.parseInt(subPartIndexString);
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
throw new DavMailException("EXCEPTION_INVALID_PARAMETER", param);
|
|
|
|
}
|
2010-03-22 07:08:12 -04:00
|
|
|
|
2010-04-06 04:31:53 -04:00
|
|
|
Object mimeBody = bodyPart.getContent();
|
|
|
|
if (mimeBody instanceof MimeMultipart) {
|
|
|
|
MimeMultipart multiPart = (MimeMultipart) mimeBody;
|
2010-04-06 05:20:09 -04:00
|
|
|
if (subPartIndex - 1 < multiPart.getCount()) {
|
|
|
|
bodyPart = (MimePart) multiPart.getBodyPart(subPartIndex - 1);
|
|
|
|
} else {
|
|
|
|
throw new DavMailException("EXCEPTION_INVALID_PARAMETER", param);
|
|
|
|
}
|
2010-04-06 04:31:53 -04:00
|
|
|
} else if (subPartIndex != 1) {
|
|
|
|
throw new DavMailException("EXCEPTION_INVALID_PARAMETER", param);
|
|
|
|
}
|
2010-03-27 06:18:53 -04:00
|
|
|
}
|
2010-04-06 04:31:53 -04:00
|
|
|
|
|
|
|
// write selected part, without headers
|
2010-04-23 09:42:37 -04:00
|
|
|
partOutputStream = new PartialOutputStream(baos, startIndex, maxSize);
|
|
|
|
if (bodyPart instanceof MimeMessage) {
|
2010-04-23 10:43:04 -04:00
|
|
|
partInputStream = ((MimeMessage) bodyPart).getRawInputStream();
|
2010-04-23 09:42:37 -04:00
|
|
|
} else {
|
2010-04-23 10:43:04 -04:00
|
|
|
partInputStream = ((MimeBodyPart) bodyPart).getRawInputStream();
|
2010-04-23 09:42:37 -04:00
|
|
|
}
|
2010-03-27 06:18:53 -04:00
|
|
|
}
|
|
|
|
|
2010-04-23 09:42:37 -04:00
|
|
|
// copy selected content to baos
|
2010-09-13 18:19:25 -04:00
|
|
|
if (partInputStream != null && partOutputStream != null) {
|
|
|
|
IOUtil.write(partInputStream, partOutputStream);
|
|
|
|
partInputStream.close();
|
|
|
|
partOutputStream.close();
|
|
|
|
}
|
2010-03-22 07:08:12 -04:00
|
|
|
baos.close();
|
|
|
|
|
2010-03-31 17:47:45 -04:00
|
|
|
if ("RFC822.HEADER".equals(param)) {
|
|
|
|
buffer.append(" RFC822.HEADER ");
|
|
|
|
} else {
|
|
|
|
buffer.append(" BODY[").append(partIndexString).append(']');
|
|
|
|
}
|
2010-03-27 06:18:53 -04:00
|
|
|
// partial
|
|
|
|
if (startIndex > 0 || maxSize != Integer.MAX_VALUE) {
|
|
|
|
buffer.append('<').append(startIndex).append('>');
|
|
|
|
}
|
2010-03-22 07:08:12 -04:00
|
|
|
buffer.append(" {").append(baos.size()).append('}');
|
|
|
|
sendClient(buffer.toString());
|
|
|
|
os.write(baos.toByteArray());
|
|
|
|
os.flush();
|
|
|
|
buffer.setLength(0);
|
2009-03-09 20:12:52 -04:00
|
|
|
}
|
2009-02-23 12:42:49 -05:00
|
|
|
}
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(')');
|
2009-02-23 12:42:49 -05:00
|
|
|
sendClient(buffer.toString());
|
2009-10-30 07:14:47 -04:00
|
|
|
// do not keep message content in memory
|
|
|
|
message.dropMimeMessage();
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
|
|
|
|
2010-09-13 18:19:25 -04:00
|
|
|
protected String[] getRequestedHeaders(String partIndexString) {
|
|
|
|
int startIndex = partIndexString.indexOf('(');
|
|
|
|
int endIndex = partIndexString.indexOf(')');
|
|
|
|
if (startIndex >= 0 && endIndex >= 0) {
|
|
|
|
return partIndexString.substring(startIndex + 1, endIndex - 1).split(" ");
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-29 13:19:00 -04:00
|
|
|
protected void handleStore(String commandId, AbstractRangeIterator rangeIterator, String action, String flags) throws IOException {
|
|
|
|
while (rangeIterator.hasNext()) {
|
|
|
|
DavGatewayTray.switchIcon();
|
|
|
|
ExchangeSession.Message message = rangeIterator.next();
|
|
|
|
updateFlags(message, action, flags);
|
|
|
|
sendClient("* " + (rangeIterator.getCurrentIndex()) + " FETCH (UID " + message.getImapUid() + " FLAGS (" + (message.getImapFlags()) + "))");
|
|
|
|
}
|
2010-05-10 05:19:25 -04:00
|
|
|
// auto expunge
|
|
|
|
if (Settings.getBooleanProperty("davmail.imapAutoExpunge")) {
|
|
|
|
if (expunge(false)) {
|
|
|
|
session.refreshFolder(currentFolder);
|
|
|
|
}
|
|
|
|
}
|
2009-10-29 13:19:00 -04:00
|
|
|
sendClient(commandId + " OK STORE completed");
|
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
protected ExchangeSession.Condition buildConditions(SearchConditions conditions, IMAPTokenizer tokens) throws IOException {
|
|
|
|
ExchangeSession.MultiCondition condition = null;
|
2009-10-27 19:34:13 -04:00
|
|
|
while (tokens.hasMoreTokens()) {
|
2010-04-28 11:33:17 -04:00
|
|
|
String token = tokens.nextQuotedToken().toUpperCase();
|
2010-05-04 04:37:19 -04:00
|
|
|
if (token.startsWith("(") && token.endsWith(")")) {
|
2010-04-28 11:33:17 -04:00
|
|
|
// quoted search param
|
2010-06-08 18:21:53 -04:00
|
|
|
if (condition == null) {
|
|
|
|
condition = session.and();
|
|
|
|
}
|
2010-06-17 18:37:33 -04:00
|
|
|
condition.add(buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1))));
|
2010-04-28 11:33:17 -04:00
|
|
|
} else if ("OR".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
condition = session.or();
|
2009-10-27 19:34:13 -04:00
|
|
|
} else if (token.startsWith("OR ")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
condition = appendOrSearchParams(token, conditions);
|
2009-10-27 19:34:13 -04:00
|
|
|
} else {
|
2010-06-08 18:21:53 -04:00
|
|
|
if (condition == null) {
|
|
|
|
condition = session.and();
|
2009-10-27 19:34:13 -04:00
|
|
|
}
|
2010-06-17 18:37:33 -04:00
|
|
|
condition.add(appendSearchParam(tokens, token, conditions));
|
2009-10-27 19:34:13 -04:00
|
|
|
}
|
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
return condition;
|
2010-04-28 11:33:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected List<Long> handleSearch(IMAPTokenizer tokens) throws IOException {
|
|
|
|
List<Long> uidList = new ArrayList<Long>();
|
|
|
|
SearchConditions conditions = new SearchConditions();
|
2010-06-08 18:21:53 -04:00
|
|
|
ExchangeSession.Condition condition = buildConditions(conditions, tokens);
|
|
|
|
ExchangeSession.MessageList localMessages = currentFolder.searchMessages(condition);
|
2010-04-28 11:33:17 -04:00
|
|
|
Iterator<ExchangeSession.Message> iterator;
|
|
|
|
if (conditions.uidRange != null) {
|
|
|
|
iterator = new UIDRangeIterator(localMessages, conditions.uidRange);
|
|
|
|
} else if (conditions.indexRange != null) {
|
|
|
|
iterator = new RangeIterator(localMessages, conditions.indexRange);
|
|
|
|
} else {
|
|
|
|
iterator = localMessages.iterator();
|
|
|
|
}
|
|
|
|
while (iterator.hasNext()) {
|
|
|
|
ExchangeSession.Message message = iterator.next();
|
2010-07-02 02:37:18 -04:00
|
|
|
if ((conditions.flagged == null || message.flagged == conditions.flagged)
|
2010-04-28 11:33:17 -04:00
|
|
|
&& (conditions.answered == null || message.answered == conditions.answered)) {
|
2009-10-27 19:34:13 -04:00
|
|
|
uidList.add(message.getImapUid());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return uidList;
|
|
|
|
}
|
|
|
|
|
2009-10-29 13:19:00 -04:00
|
|
|
protected void appendEnvelope(StringBuilder buffer, ExchangeSession.Message message) throws IOException {
|
|
|
|
buffer.append(" ENVELOPE (");
|
|
|
|
|
|
|
|
try {
|
|
|
|
MimeMessage mimeMessage = message.getMimeMessage();
|
2010-03-22 06:17:50 -04:00
|
|
|
// Envelope for date, subject, from, sender, reply-to, to, cc, bcc,in-reply-to, message-id
|
2009-10-29 13:19:00 -04:00
|
|
|
appendEnvelopeHeader(buffer, mimeMessage.getHeader("Date"));
|
|
|
|
appendEnvelopeHeader(buffer, mimeMessage.getHeader("Subject"));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("From", ","));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("Sender", ","));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("Reply-To", ","));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("CC", ","));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("BCC", ","));
|
|
|
|
appendMailEnvelopeHeader(buffer, mimeMessage.getHeader("In-Reply-To", ","));
|
|
|
|
appendEnvelopeHeader(buffer, mimeMessage.getHeader("Messagee-Id"));
|
|
|
|
|
|
|
|
} catch (MessagingException me) {
|
|
|
|
DavGatewayTray.warn(me);
|
|
|
|
// send fake envelope
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append(" NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
|
|
|
buffer.append(')');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void appendEnvelopeHeader(StringBuilder buffer, String[] value) {
|
|
|
|
buffer.append(' ');
|
|
|
|
if (value != null && value.length > 0) {
|
2010-03-22 06:17:50 -04:00
|
|
|
String unfoldedValue = MimeUtility.unfold(value[0]);
|
|
|
|
if (unfoldedValue.indexOf('"') >= 0) {
|
|
|
|
buffer.append('{');
|
|
|
|
buffer.append(unfoldedValue.length());
|
|
|
|
buffer.append("}\r\n");
|
|
|
|
buffer.append(unfoldedValue);
|
|
|
|
} else {
|
|
|
|
buffer.append('"');
|
|
|
|
buffer.append(unfoldedValue);
|
|
|
|
buffer.append('"');
|
|
|
|
}
|
2009-10-29 13:19:00 -04:00
|
|
|
} else {
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void appendMailEnvelopeHeader(StringBuilder buffer, String value) {
|
|
|
|
buffer.append(' ');
|
|
|
|
if (value != null) {
|
|
|
|
try {
|
|
|
|
InternetAddress[] addresses = InternetAddress.parseHeader(value, false);
|
|
|
|
buffer.append('(');
|
|
|
|
for (InternetAddress address : addresses) {
|
|
|
|
buffer.append('(');
|
|
|
|
String personal = address.getPersonal();
|
|
|
|
if (personal != null) {
|
2009-10-29 18:45:17 -04:00
|
|
|
buffer.append('"').append(MimeUtility.encodeText(personal)).append('"');
|
2009-10-29 13:19:00 -04:00
|
|
|
} else {
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append(" NIL ");
|
2009-10-29 13:19:00 -04:00
|
|
|
String mail = address.getAddress();
|
|
|
|
int atIndex = mail.indexOf('@');
|
|
|
|
if (atIndex >= 0) {
|
|
|
|
buffer.append('"').append(mail.substring(0, atIndex)).append('"');
|
|
|
|
buffer.append(' ');
|
|
|
|
buffer.append('"').append(mail.substring(atIndex + 1)).append('"');
|
|
|
|
} else {
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
|
|
|
buffer.append(')');
|
|
|
|
}
|
|
|
|
buffer.append(')');
|
|
|
|
} catch (AddressException e) {
|
|
|
|
DavGatewayTray.warn(e);
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL");
|
2009-10-29 18:45:17 -04:00
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
|
DavGatewayTray.warn(e);
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
|
|
|
} else {
|
2009-11-02 16:37:57 -05:00
|
|
|
buffer.append("NIL");
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-08 18:25:39 -04:00
|
|
|
protected void appendBodyStructure(StringBuilder buffer, ExchangeSession.Message message) throws IOException {
|
2009-09-21 17:34:13 -04:00
|
|
|
|
2009-03-02 10:38:02 -05:00
|
|
|
buffer.append(" BODYSTRUCTURE ");
|
|
|
|
try {
|
2009-10-29 13:19:00 -04:00
|
|
|
MimeMessage mimeMessage = message.getMimeMessage();
|
2009-03-02 10:38:02 -05:00
|
|
|
Object mimeBody = mimeMessage.getContent();
|
|
|
|
if (mimeBody instanceof MimeMultipart) {
|
2010-09-14 09:42:48 -04:00
|
|
|
appendBodyStructure(buffer, (MimeMultipart) mimeBody);
|
2009-03-02 10:38:02 -05:00
|
|
|
} else {
|
|
|
|
// no multipart, single body
|
|
|
|
appendBodyStructure(buffer, mimeMessage);
|
|
|
|
}
|
2009-10-09 19:12:14 -04:00
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
|
DavGatewayTray.warn(e);
|
|
|
|
// failover: send default bodystructure
|
2009-10-29 13:19:00 -04:00
|
|
|
buffer.append("(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL NIL NIL NIL)");
|
2009-03-02 10:38:02 -05:00
|
|
|
} catch (MessagingException me) {
|
2009-09-04 12:01:24 -04:00
|
|
|
DavGatewayTray.warn(me);
|
2009-09-07 05:01:07 -04:00
|
|
|
// failover: send default bodystructure
|
2009-10-29 13:19:00 -04:00
|
|
|
buffer.append("(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL NIL NIL NIL)");
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-14 09:42:48 -04:00
|
|
|
protected void appendBodyStructure(StringBuilder buffer, MimeMultipart multiPart) throws IOException, MessagingException {
|
|
|
|
buffer.append('(');
|
|
|
|
|
|
|
|
for (int i = 0; i < multiPart.getCount(); i++) {
|
|
|
|
MimeBodyPart bodyPart = (MimeBodyPart) multiPart.getBodyPart(i);
|
|
|
|
Object mimeBody = bodyPart.getContent();
|
|
|
|
if (mimeBody instanceof MimeMultipart) {
|
|
|
|
appendBodyStructure(buffer, (MimeMultipart) mimeBody);
|
|
|
|
} else {
|
|
|
|
// no multipart, single body
|
|
|
|
appendBodyStructure(buffer, bodyPart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int slashIndex = multiPart.getContentType().indexOf('/');
|
|
|
|
if (slashIndex < 0) {
|
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CONTENT_TYPE", multiPart.getContentType());
|
|
|
|
}
|
|
|
|
int semiColonIndex = multiPart.getContentType().indexOf(';');
|
|
|
|
if (semiColonIndex < 0) {
|
|
|
|
buffer.append(" \"").append(multiPart.getContentType().substring(slashIndex + 1).toUpperCase()).append("\")");
|
|
|
|
} else {
|
|
|
|
buffer.append(" \"").append(multiPart.getContentType().substring(slashIndex + 1, semiColonIndex).trim().toUpperCase()).append("\")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-02 10:38:02 -05:00
|
|
|
protected void appendBodyStructure(StringBuilder buffer, MimePart bodyPart) throws IOException, MessagingException {
|
|
|
|
String contentType = bodyPart.getContentType();
|
|
|
|
int slashIndex = contentType.indexOf('/');
|
|
|
|
if (slashIndex < 0) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CONTENT_TYPE", contentType);
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|
|
|
|
buffer.append("(\"").append(contentType.substring(0, slashIndex).toUpperCase()).append("\" \"");
|
|
|
|
int semiColonIndex = contentType.indexOf(';');
|
|
|
|
if (semiColonIndex < 0) {
|
|
|
|
buffer.append(contentType.substring(slashIndex + 1).toUpperCase()).append("\" ()");
|
|
|
|
} else {
|
|
|
|
// extended content type
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(contentType.substring(slashIndex + 1, semiColonIndex).trim().toUpperCase()).append('\"');
|
2009-03-02 10:38:02 -05:00
|
|
|
int charsetindex = contentType.indexOf("charset=");
|
|
|
|
if (charsetindex >= 0) {
|
|
|
|
buffer.append(" (\"CHARSET\" ");
|
2009-10-27 19:34:13 -04:00
|
|
|
int charsetSemiColonIndex = contentType.indexOf(';', charsetindex);
|
2009-10-09 19:08:04 -04:00
|
|
|
int charsetEndIndex;
|
|
|
|
if (charsetSemiColonIndex > 0) {
|
|
|
|
charsetEndIndex = charsetSemiColonIndex;
|
|
|
|
} else {
|
2009-10-27 19:34:13 -04:00
|
|
|
charsetEndIndex = contentType.length();
|
2009-10-09 19:08:04 -04:00
|
|
|
}
|
2009-03-02 10:38:02 -05:00
|
|
|
String charSet = contentType.substring(charsetindex + "charset=".length(), charsetEndIndex);
|
|
|
|
if (!charSet.startsWith("\"")) {
|
|
|
|
buffer.append('"');
|
|
|
|
}
|
2009-10-09 19:08:04 -04:00
|
|
|
buffer.append(charSet.trim().toUpperCase());
|
2009-03-02 10:38:02 -05:00
|
|
|
if (!charSet.endsWith("\"")) {
|
|
|
|
buffer.append('"');
|
|
|
|
}
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(')');
|
2009-03-02 10:38:02 -05:00
|
|
|
} else {
|
2010-03-17 17:03:24 -04:00
|
|
|
buffer.append(" NIL");
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|
|
|
|
}
|
2009-03-27 05:07:03 -04:00
|
|
|
appendBodyStructureValue(buffer, bodyPart.getContentID());
|
|
|
|
appendBodyStructureValue(buffer, bodyPart.getDescription());
|
|
|
|
appendBodyStructureValue(buffer, bodyPart.getEncoding());
|
|
|
|
appendBodyStructureValue(buffer, bodyPart.getSize());
|
2010-03-22 07:08:12 -04:00
|
|
|
// line count not implemented in JavaMail, return 0
|
2010-03-17 17:03:24 -04:00
|
|
|
appendBodyStructureValue(buffer, 0);
|
2009-03-27 05:07:03 -04:00
|
|
|
buffer.append(')');
|
|
|
|
}
|
|
|
|
|
2009-04-03 03:38:31 -04:00
|
|
|
protected void appendBodyStructureValue(StringBuilder buffer, String value) {
|
2009-03-27 05:07:03 -04:00
|
|
|
if (value == null) {
|
2009-03-02 10:38:02 -05:00
|
|
|
buffer.append(" NIL");
|
|
|
|
} else {
|
2009-04-23 16:53:22 -04:00
|
|
|
buffer.append(" \"").append(value.toUpperCase()).append('\"');
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|
2009-03-27 05:07:03 -04:00
|
|
|
}
|
|
|
|
|
2009-04-03 03:38:31 -04:00
|
|
|
protected void appendBodyStructureValue(StringBuilder buffer, int value) {
|
2009-03-27 05:07:03 -04:00
|
|
|
if (value < 0) {
|
2009-03-02 10:38:02 -05:00
|
|
|
buffer.append(" NIL");
|
|
|
|
} else {
|
2009-03-27 05:07:03 -04:00
|
|
|
buffer.append(' ').append(value);
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-25 07:50:46 -04:00
|
|
|
protected void sendSubFolders(String command, String folderPath, boolean recursive) throws IOException {
|
|
|
|
try {
|
|
|
|
List<ExchangeSession.Folder> folders = session.getSubFolders(folderPath, recursive);
|
|
|
|
for (ExchangeSession.Folder folder : folders) {
|
|
|
|
sendClient("* " + command + " (" + folder.getFlags() + ") \"/\" \"" + BASE64MailboxEncoder.encode(folder.folderPath) + '\"');
|
|
|
|
}
|
|
|
|
} catch (HttpForbiddenException e) {
|
|
|
|
// access forbidden, ignore
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_SUBFOLDER_ACCESS_FORBIDDEN", folderPath));
|
|
|
|
} catch (HttpNotFoundException e) {
|
|
|
|
// not found, ignore
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOLDER_NOT_FOUND", folderPath));
|
|
|
|
} catch (HttpException e) {
|
|
|
|
// other errors, ignore
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOLDER_ACCESS_ERROR", folderPath, e.getMessage()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
/**
|
|
|
|
* client side search conditions
|
|
|
|
*/
|
2009-02-17 19:09:42 -05:00
|
|
|
static final class SearchConditions {
|
2009-04-16 18:20:30 -04:00
|
|
|
Boolean flagged;
|
|
|
|
Boolean answered;
|
2010-04-28 11:33:17 -04:00
|
|
|
String indexRange;
|
|
|
|
String uidRange;
|
2009-02-17 19:09:42 -05:00
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
protected ExchangeSession.MultiCondition appendOrSearchParams(String token, SearchConditions conditions) throws IOException {
|
|
|
|
ExchangeSession.MultiCondition orCondition = session.or();
|
2009-02-17 19:58:20 -05:00
|
|
|
IMAPTokenizer innerTokens = new IMAPTokenizer(token);
|
|
|
|
innerTokens.nextToken();
|
|
|
|
while (innerTokens.hasMoreTokens()) {
|
|
|
|
String innerToken = innerTokens.nextToken();
|
2010-06-17 18:37:33 -04:00
|
|
|
orCondition.add(appendSearchParam(innerTokens, innerToken, conditions));
|
2009-02-17 19:58:20 -05:00
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
return orCondition;
|
2009-02-17 19:58:20 -05:00
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
protected ExchangeSession.Condition appendSearchParam(StringTokenizer tokens, String token, SearchConditions conditions) throws IOException {
|
2009-02-17 17:59:32 -05:00
|
|
|
if ("NOT".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
String nextToken = tokens.nextToken();
|
2010-08-21 19:40:24 -04:00
|
|
|
if ("DELETED".equals(nextToken)) {
|
2010-07-01 11:07:16 -04:00
|
|
|
// conditions.deleted = Boolean.FALSE;
|
|
|
|
return session.isNull("deleted");
|
2010-06-08 18:21:53 -04:00
|
|
|
} else {
|
|
|
|
return session.not(appendSearchParam(tokens, nextToken, conditions));
|
|
|
|
}
|
2009-02-17 19:58:20 -05:00
|
|
|
} else if (token.startsWith("OR ")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return appendOrSearchParams(token, conditions);
|
2009-02-17 17:59:32 -05:00
|
|
|
} else if ("SUBJECT".equals(token)) {
|
2010-06-21 16:37:20 -04:00
|
|
|
return session.contains("subject", tokens.nextToken());
|
2009-02-17 17:59:32 -05:00
|
|
|
} else if ("BODY".equals(token)) {
|
2010-06-21 16:37:20 -04:00
|
|
|
return session.contains("body", tokens.nextToken());
|
2009-02-17 17:59:32 -05:00
|
|
|
} else if ("FROM".equals(token)) {
|
2010-06-21 16:37:20 -04:00
|
|
|
return session.contains("from", tokens.nextToken());
|
2009-02-17 19:58:20 -05:00
|
|
|
} else if ("TO".equals(token)) {
|
2010-06-21 16:37:20 -04:00
|
|
|
return session.contains("to", tokens.nextToken());
|
2009-02-17 19:58:20 -05:00
|
|
|
} else if ("CC".equals(token)) {
|
2010-06-21 16:37:20 -04:00
|
|
|
return session.contains("cc", tokens.nextToken());
|
2009-02-17 19:58:20 -05:00
|
|
|
} else if ("LARGER".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.gte("messageSize", tokens.nextToken());
|
2009-02-17 19:58:20 -05:00
|
|
|
} else if ("SMALLER".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.lt("messageSize", tokens.nextToken());
|
2009-08-11 12:01:21 -04:00
|
|
|
} else if (token.startsWith("SENT") || "SINCE".equals(token) || "BEFORE".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return appendDateSearchParam(tokens, token);
|
2009-02-17 19:09:42 -05:00
|
|
|
} else if ("SEEN".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.isTrue("read");
|
2009-02-17 19:09:42 -05:00
|
|
|
} else if ("UNSEEN".equals(token) || "NEW".equals(token)) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.isFalse("read");
|
2009-07-17 08:19:08 -04:00
|
|
|
} else if ("DELETED".equals(token)) {
|
2010-07-01 11:07:16 -04:00
|
|
|
// conditions.deleted = Boolean.TRUE;
|
2010-07-27 09:19:28 -04:00
|
|
|
return session.isEqualTo("deleted", "1");
|
2010-01-19 08:40:29 -05:00
|
|
|
} else if ("UNDELETED".equals(token) || "NOT DELETED".equals(token)) {
|
2010-07-01 11:07:16 -04:00
|
|
|
// conditions.deleted = Boolean.FALSE;
|
|
|
|
return session.isNull("deleted");
|
2009-02-17 19:09:42 -05:00
|
|
|
} else if ("FLAGGED".equals(token)) {
|
|
|
|
conditions.flagged = Boolean.TRUE;
|
|
|
|
} else if ("UNFLAGGED".equals(token) || "NEW".equals(token)) {
|
|
|
|
conditions.flagged = Boolean.FALSE;
|
|
|
|
} else if ("ANSWERED".equals(token)) {
|
|
|
|
conditions.answered = Boolean.TRUE;
|
|
|
|
} else if ("UNANSWERED".equals(token)) {
|
|
|
|
conditions.answered = Boolean.FALSE;
|
2009-02-17 17:59:32 -05:00
|
|
|
} else if ("HEADER".equals(token)) {
|
2009-02-17 19:58:20 -05:00
|
|
|
String headerName = tokens.nextToken().toLowerCase();
|
2009-03-19 08:41:11 -04:00
|
|
|
String value = tokens.nextToken();
|
2009-03-27 06:31:52 -04:00
|
|
|
if ("message-id".equals(headerName) && !value.startsWith("<")) {
|
2009-04-23 16:53:22 -04:00
|
|
|
value = '<' + value + '>';
|
2009-03-19 08:41:11 -04:00
|
|
|
}
|
2010-07-27 09:19:28 -04:00
|
|
|
return session.headerIsEqualTo(headerName, value);
|
2009-02-23 05:06:12 -05:00
|
|
|
} else if ("UID".equals(token)) {
|
|
|
|
String range = tokens.nextToken();
|
|
|
|
if ("1:*".equals(range)) {
|
|
|
|
// ignore: this is a noop filter
|
|
|
|
} else {
|
2010-04-28 11:33:17 -04:00
|
|
|
conditions.uidRange = range;
|
2009-02-23 05:06:12 -05:00
|
|
|
}
|
2009-08-11 12:01:21 -04:00
|
|
|
} else if ("OLD".equals(token) || "RECENT".equals(token) || "ALL".equals(token)) {
|
2009-02-17 19:09:42 -05:00
|
|
|
// ignore
|
2009-07-17 04:45:34 -04:00
|
|
|
} else if (token.indexOf(':') >= 0) {
|
|
|
|
// range search
|
2010-04-28 11:33:17 -04:00
|
|
|
conditions.indexRange = token;
|
2009-02-17 17:59:32 -05:00
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", token);
|
2009-02-17 17:59:32 -05:00
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
// client side search token
|
|
|
|
return null;
|
2009-02-17 17:59:32 -05:00
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
protected ExchangeSession.Condition appendDateSearchParam(StringTokenizer tokens, String token) throws IOException {
|
2009-02-17 19:09:42 -05:00
|
|
|
Date startDate;
|
|
|
|
Date endDate;
|
|
|
|
SimpleDateFormat parser = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);
|
|
|
|
parser.setTimeZone(ExchangeSession.GMT_TIMEZONE);
|
|
|
|
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
|
|
|
dateFormatter.setTimeZone(ExchangeSession.GMT_TIMEZONE);
|
2009-04-27 19:03:58 -04:00
|
|
|
String dateToken = tokens.nextToken();
|
2009-02-17 19:09:42 -05:00
|
|
|
try {
|
2009-04-27 19:03:58 -04:00
|
|
|
startDate = parser.parse(dateToken);
|
2009-02-17 19:09:42 -05:00
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
|
calendar.setTime(startDate);
|
|
|
|
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
endDate = calendar.getTime();
|
|
|
|
} catch (ParseException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", dateToken);
|
2009-02-17 19:09:42 -05:00
|
|
|
}
|
2009-08-11 12:01:21 -04:00
|
|
|
String searchAttribute;
|
|
|
|
if (token.startsWith("SENT")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
searchAttribute = "date";
|
2009-08-11 12:01:21 -04:00
|
|
|
} else {
|
2010-06-08 18:21:53 -04:00
|
|
|
searchAttribute = "lastmodified";
|
2009-08-11 12:01:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (token.endsWith("ON")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.and(session.gt(searchAttribute, dateFormatter.format(startDate)),
|
|
|
|
session.lt(searchAttribute, dateFormatter.format(endDate)));
|
2009-08-11 12:01:21 -04:00
|
|
|
} else if (token.endsWith("BEFORE")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.lt(searchAttribute, dateFormatter.format(startDate));
|
2009-08-11 12:01:21 -04:00
|
|
|
} else if (token.endsWith("SINCE")) {
|
2010-06-08 18:21:53 -04:00
|
|
|
return session.gte(searchAttribute, dateFormatter.format(startDate));
|
|
|
|
} else {
|
|
|
|
throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", dateToken);
|
2009-02-17 19:09:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-10 05:19:25 -04:00
|
|
|
protected boolean expunge(boolean silent) throws IOException {
|
|
|
|
boolean hasDeleted = false;
|
2009-03-27 06:31:52 -04:00
|
|
|
if (currentFolder.messages != null) {
|
2010-02-18 15:55:42 -05:00
|
|
|
int index = 1;
|
2009-03-27 06:31:52 -04:00
|
|
|
for (ExchangeSession.Message message : currentFolder.messages) {
|
2009-02-02 18:29:44 -05:00
|
|
|
if (message.deleted) {
|
2009-02-03 16:02:33 -05:00
|
|
|
message.delete();
|
2010-05-10 05:19:25 -04:00
|
|
|
hasDeleted = true;
|
2009-03-02 10:45:07 -05:00
|
|
|
if (!silent) {
|
|
|
|
sendClient("* " + index + " EXPUNGE");
|
|
|
|
}
|
2010-02-18 15:55:42 -05:00
|
|
|
} else {
|
|
|
|
index++;
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-05-10 05:19:25 -04:00
|
|
|
return hasDeleted;
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
protected void updateFlags(ExchangeSession.Message message, String action, String flags) throws IOException {
|
|
|
|
HashMap<String, String> properties = new HashMap<String, String>();
|
2009-02-23 05:06:12 -05:00
|
|
|
if ("-Flags".equalsIgnoreCase(action) || "-FLAGS.SILENT".equalsIgnoreCase(action)) {
|
2009-02-03 18:54:48 -05:00
|
|
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
|
|
|
while (flagtokenizer.hasMoreTokens()) {
|
|
|
|
String flag = flagtokenizer.nextToken();
|
2010-09-22 16:34:14 -04:00
|
|
|
if ("\\Seen".equalsIgnoreCase(flag) && message.read) {
|
2009-02-03 18:54:48 -05:00
|
|
|
properties.put("read", "0");
|
|
|
|
message.read = false;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Flagged".equalsIgnoreCase(flag) && message.flagged) {
|
2009-02-03 18:54:48 -05:00
|
|
|
properties.put("flagged", "0");
|
|
|
|
message.flagged = false;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Deleted".equalsIgnoreCase(flag) && message.deleted) {
|
2009-09-23 18:18:07 -04:00
|
|
|
properties.put("deleted", null);
|
|
|
|
message.deleted = false;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("Junk".equalsIgnoreCase(flag) && message.junk) {
|
2009-02-03 18:54:48 -05:00
|
|
|
properties.put("junk", "0");
|
|
|
|
message.junk = false;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("$Forwarded".equalsIgnoreCase(flag) && message.forwarded) {
|
2010-07-02 02:37:18 -04:00
|
|
|
properties.put("forwarded", null);
|
|
|
|
message.forwarded = false;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Answered".equalsIgnoreCase(flag) && message.answered) {
|
2010-07-02 02:37:18 -04:00
|
|
|
properties.put("answered", null);
|
|
|
|
message.answered = false;
|
2009-02-03 18:54:48 -05:00
|
|
|
}
|
|
|
|
}
|
2009-02-23 05:06:12 -05:00
|
|
|
} else if ("+Flags".equalsIgnoreCase(action) || "+FLAGS.SILENT".equalsIgnoreCase(action)) {
|
2009-02-03 18:54:48 -05:00
|
|
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
|
|
|
while (flagtokenizer.hasMoreTokens()) {
|
|
|
|
String flag = flagtokenizer.nextToken();
|
2010-09-22 16:34:14 -04:00
|
|
|
if ("\\Seen".equalsIgnoreCase(flag) && !message.read) {
|
2009-02-03 18:54:48 -05:00
|
|
|
properties.put("read", "1");
|
|
|
|
message.read = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Deleted".equalsIgnoreCase(flag) && !message.deleted) {
|
2009-02-24 09:26:48 -05:00
|
|
|
message.deleted = true;
|
2009-10-09 18:46:10 -04:00
|
|
|
properties.put("deleted", "1");
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Flagged".equalsIgnoreCase(flag) && !message.flagged) {
|
2009-02-24 09:26:48 -05:00
|
|
|
properties.put("flagged", "2");
|
|
|
|
message.flagged = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Answered".equalsIgnoreCase(flag) && !message.answered) {
|
2009-02-24 09:26:48 -05:00
|
|
|
properties.put("answered", "102");
|
|
|
|
message.answered = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("$Forwarded".equalsIgnoreCase(flag) && !message.forwarded) {
|
2009-02-24 09:26:48 -05:00
|
|
|
properties.put("forwarded", "104");
|
|
|
|
message.forwarded = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("Junk".equalsIgnoreCase(flag) && !message.junk) {
|
2009-02-24 09:26:48 -05:00
|
|
|
properties.put("junk", "1");
|
|
|
|
message.junk = true;
|
|
|
|
}
|
|
|
|
}
|
2009-09-16 11:08:00 -04:00
|
|
|
} else if ("FLAGS".equalsIgnoreCase(action) || "FLAGS.SILENT".equalsIgnoreCase(action)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
// flag list with default values
|
|
|
|
boolean read = false;
|
|
|
|
boolean deleted = false;
|
|
|
|
boolean junk = false;
|
|
|
|
boolean flagged = false;
|
|
|
|
boolean answered = false;
|
|
|
|
boolean forwarded = false;
|
|
|
|
// set flags from new flag list
|
2009-02-24 09:26:48 -05:00
|
|
|
StringTokenizer flagtokenizer = new StringTokenizer(flags);
|
|
|
|
while (flagtokenizer.hasMoreTokens()) {
|
|
|
|
String flag = flagtokenizer.nextToken();
|
2010-09-22 16:34:14 -04:00
|
|
|
if ("\\Seen".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
read = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Deleted".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
deleted = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Flagged".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
flagged = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("\\Answered".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
answered = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("$Forwarded".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
forwarded = true;
|
2010-09-22 16:34:14 -04:00
|
|
|
} else if ("Junk".equalsIgnoreCase(flag)) {
|
2009-10-29 08:56:37 -04:00
|
|
|
junk = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (read != message.read) {
|
|
|
|
message.read = read;
|
|
|
|
if (message.read) {
|
|
|
|
properties.put("read", "1");
|
|
|
|
} else {
|
|
|
|
properties.put("read", "0");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (deleted != message.deleted) {
|
|
|
|
message.deleted = deleted;
|
|
|
|
if (message.deleted) {
|
|
|
|
properties.put("deleted", "1");
|
|
|
|
} else {
|
|
|
|
properties.put("deleted", null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flagged != message.flagged) {
|
|
|
|
message.flagged = flagged;
|
|
|
|
if (message.flagged) {
|
|
|
|
properties.put("flagged", "2");
|
|
|
|
} else {
|
|
|
|
properties.put("flagged", "0");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (answered != message.answered) {
|
|
|
|
message.answered = answered;
|
|
|
|
if (message.answered) {
|
|
|
|
properties.put("answered", "102");
|
|
|
|
} else if (!forwarded) {
|
|
|
|
// remove property only if not forwarded
|
|
|
|
properties.put("answered", null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (forwarded != message.forwarded) {
|
|
|
|
message.forwarded = forwarded;
|
|
|
|
if (message.forwarded) {
|
|
|
|
properties.put("forwarded", "104");
|
|
|
|
} else if (!answered) {
|
|
|
|
// remove property only if not answered
|
|
|
|
properties.put("forwarded", null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (junk != message.junk) {
|
|
|
|
message.junk = junk;
|
|
|
|
if (message.junk) {
|
2009-02-03 18:54:48 -05:00
|
|
|
properties.put("junk", "1");
|
2009-10-29 08:56:37 -04:00
|
|
|
} else {
|
|
|
|
properties.put("junk", "0");
|
2009-02-03 18:54:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-04-16 17:16:40 -04:00
|
|
|
if (!properties.isEmpty()) {
|
2009-02-03 18:54:48 -05:00
|
|
|
session.updateMessage(message, properties);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
2009-01-09 17:41:10 -05:00
|
|
|
* Decode IMAP credentials
|
2009-01-22 19:59:41 -05:00
|
|
|
*
|
2009-01-09 17:41:10 -05:00
|
|
|
* @param tokens tokens
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException on error
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
|
|
|
protected void parseCredentials(StringTokenizer tokens) throws IOException {
|
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-01-29 17:18:53 -05:00
|
|
|
userName = tokens.nextToken();
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CREDENTIALS");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tokens.hasMoreTokens()) {
|
2009-01-29 17:18:53 -05:00
|
|
|
password = tokens.nextToken();
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CREDENTIALS");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2009-04-16 17:16:40 -04:00
|
|
|
int backslashindex = userName.indexOf('\\');
|
2006-12-12 18:57:24 -05:00
|
|
|
if (backslashindex > 0) {
|
|
|
|
userName = userName.substring(0, backslashindex) + userName.substring(backslashindex + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String removeQuotes(String value) {
|
|
|
|
String result = value;
|
2009-01-26 18:51:08 -05:00
|
|
|
if (result.startsWith("\"") || result.startsWith("{") || result.startsWith("(")) {
|
2006-12-12 18:57:24 -05:00
|
|
|
result = result.substring(1);
|
|
|
|
}
|
2009-01-26 18:51:08 -05:00
|
|
|
if (result.endsWith("\"") || result.endsWith("}") || result.endsWith(")")) {
|
2006-12-12 18:57:24 -05:00
|
|
|
result = result.substring(0, result.length() - 1);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-01-26 18:51:08 -05:00
|
|
|
/**
|
2009-02-25 12:12:02 -05:00
|
|
|
* Filter to output only headers, also count full size
|
2009-01-26 18:51:08 -05:00
|
|
|
*/
|
2009-09-21 17:34:13 -04:00
|
|
|
private static final class PartOutputStream extends FilterOutputStream {
|
|
|
|
private static final int START = 0;
|
|
|
|
private static final int CR = 1;
|
|
|
|
private static final int CRLF = 2;
|
|
|
|
private static final int CRLFCR = 3;
|
|
|
|
private static final int BODY = 4;
|
|
|
|
|
|
|
|
private int state = START;
|
|
|
|
private int size;
|
2010-03-27 06:18:53 -04:00
|
|
|
private int bufferSize;
|
2009-09-21 17:34:13 -04:00
|
|
|
private final boolean writeHeaders;
|
|
|
|
private final boolean writeBody;
|
|
|
|
private final int startIndex;
|
2010-03-27 06:18:53 -04:00
|
|
|
private final int maxSize;
|
2009-01-26 18:51:08 -05:00
|
|
|
|
2009-04-16 17:52:17 -04:00
|
|
|
private PartOutputStream(OutputStream os, boolean writeHeaders, boolean writeBody,
|
2010-03-27 06:18:53 -04:00
|
|
|
int startIndex, int maxSize) {
|
2009-01-26 18:51:08 -05:00
|
|
|
super(os);
|
2009-02-25 12:12:02 -05:00
|
|
|
this.writeHeaders = writeHeaders;
|
|
|
|
this.writeBody = writeBody;
|
2009-03-09 20:12:52 -04:00
|
|
|
this.startIndex = startIndex;
|
2010-03-27 06:18:53 -04:00
|
|
|
this.maxSize = maxSize;
|
2009-01-26 18:51:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void write(int b) throws IOException {
|
|
|
|
size++;
|
2009-03-09 20:12:52 -04:00
|
|
|
if (((state != BODY && writeHeaders) || (state == BODY && writeBody)) &&
|
2010-03-27 06:18:53 -04:00
|
|
|
(size > startIndex) && (bufferSize < maxSize)
|
2009-03-16 20:25:10 -04:00
|
|
|
) {
|
2009-01-26 18:51:08 -05:00
|
|
|
super.write(b);
|
2010-03-27 06:18:53 -04:00
|
|
|
bufferSize++;
|
2009-01-26 18:51:08 -05:00
|
|
|
}
|
|
|
|
if (state == START) {
|
|
|
|
if (b == '\r') {
|
|
|
|
state = CR;
|
|
|
|
}
|
|
|
|
} else if (state == CR) {
|
|
|
|
if (b == '\n') {
|
|
|
|
state = CRLF;
|
|
|
|
} else {
|
|
|
|
state = START;
|
|
|
|
}
|
|
|
|
} else if (state == CRLF) {
|
|
|
|
if (b == '\r') {
|
|
|
|
state = CRLFCR;
|
|
|
|
} else {
|
|
|
|
state = START;
|
|
|
|
}
|
|
|
|
} else if (state == CRLFCR) {
|
|
|
|
if (b == '\n') {
|
|
|
|
state = BODY;
|
|
|
|
} else {
|
|
|
|
state = START;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-27 06:18:53 -04:00
|
|
|
/**
|
|
|
|
* Partial output stream, start at startIndex and write maxSize bytes.
|
|
|
|
*/
|
|
|
|
private static final class PartialOutputStream extends FilterOutputStream {
|
|
|
|
private int size;
|
|
|
|
private int bufferSize;
|
|
|
|
private final int startIndex;
|
|
|
|
private final int maxSize;
|
|
|
|
|
|
|
|
private PartialOutputStream(OutputStream os, int startIndex, int maxSize) {
|
|
|
|
super(os);
|
|
|
|
this.startIndex = startIndex;
|
|
|
|
this.maxSize = maxSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void write(int b) throws IOException {
|
|
|
|
size++;
|
|
|
|
if ((size > startIndex) && (bufferSize < maxSize)) {
|
|
|
|
super.write(b);
|
|
|
|
bufferSize++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-05 05:06:47 -05:00
|
|
|
protected abstract static class AbstractRangeIterator implements Iterator<ExchangeSession.Message> {
|
|
|
|
ExchangeSession.MessageList messages;
|
2009-04-16 18:20:30 -04:00
|
|
|
int currentIndex;
|
2009-10-29 13:19:00 -04:00
|
|
|
|
|
|
|
protected int getCurrentIndex() {
|
|
|
|
return currentIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-05 05:06:47 -05:00
|
|
|
protected static class UIDRangeIterator extends AbstractRangeIterator {
|
2009-10-29 13:19:00 -04:00
|
|
|
final String[] ranges;
|
2009-04-16 18:20:30 -04:00
|
|
|
int currentRangeIndex;
|
2009-02-04 16:55:42 -05:00
|
|
|
long startUid;
|
|
|
|
long endUid;
|
|
|
|
|
2010-01-05 05:06:47 -05:00
|
|
|
protected UIDRangeIterator(ExchangeSession.MessageList messages, String value) {
|
|
|
|
this.messages = messages;
|
2009-02-04 16:55:42 -05:00
|
|
|
ranges = value.split(",");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected long convertToLong(String value) {
|
|
|
|
if ("*".equals(value)) {
|
|
|
|
return Long.MAX_VALUE;
|
|
|
|
} else {
|
|
|
|
return Long.parseLong(value);
|
|
|
|
}
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2009-09-07 07:42:07 -04:00
|
|
|
protected void skipToNextRangeStartUid() {
|
2009-02-04 16:55:42 -05:00
|
|
|
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));
|
2009-09-07 07:42:07 -04:00
|
|
|
if (endUid < startUid) {
|
|
|
|
long swap = endUid;
|
|
|
|
endUid = startUid;
|
|
|
|
startUid = swap;
|
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
} else if ("*".equals(currentRange)) {
|
|
|
|
startUid = endUid = messages.get(messages.size() - 1).getImapUid();
|
2009-02-04 16:55:42 -05:00
|
|
|
} else {
|
|
|
|
startUid = endUid = convertToLong(currentRange);
|
|
|
|
}
|
2010-01-05 05:06:47 -05:00
|
|
|
while (currentIndex < messages.size() && messages.get(currentIndex).getImapUid() < startUid) {
|
2009-02-04 16:55:42 -05:00
|
|
|
currentIndex++;
|
|
|
|
}
|
|
|
|
} else {
|
2010-01-05 05:06:47 -05:00
|
|
|
currentIndex = messages.size();
|
2009-02-04 16:55:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 07:42:07 -04:00
|
|
|
protected boolean hasNextInRange() {
|
2010-01-05 05:06:47 -05:00
|
|
|
return hasNextIndex() && messages.get(currentIndex).getImapUid() <= endUid;
|
2009-09-07 07:42:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean hasNextIndex() {
|
2010-01-05 05:06:47 -05:00
|
|
|
return currentIndex < messages.size();
|
2009-09-07 07:42:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean hasNextRange() {
|
|
|
|
return currentRangeIndex < ranges.length;
|
|
|
|
}
|
|
|
|
|
2009-02-04 16:55:42 -05:00
|
|
|
public boolean hasNext() {
|
2009-09-07 07:42:07 -04:00
|
|
|
boolean hasNextInRange = hasNextInRange();
|
|
|
|
// if has next range and current index after current range end, reset index
|
|
|
|
if (hasNextRange() && !hasNextInRange) {
|
|
|
|
currentIndex = 0;
|
2009-02-04 16:55:42 -05:00
|
|
|
}
|
2009-09-07 07:42:07 -04:00
|
|
|
while (hasNextIndex() && !hasNextInRange) {
|
|
|
|
skipToNextRangeStartUid();
|
|
|
|
hasNextInRange = hasNextInRange();
|
|
|
|
}
|
|
|
|
return hasNextIndex();
|
2009-02-04 16:55:42 -05:00
|
|
|
}
|
|
|
|
|
2009-02-20 12:06:03 -05:00
|
|
|
public ExchangeSession.Message next() {
|
2010-01-05 05:06:47 -05:00
|
|
|
ExchangeSession.Message message = messages.get(currentIndex++);
|
2009-03-19 08:41:11 -04:00
|
|
|
long uid = message.getImapUid();
|
2009-03-03 10:49:42 -05:00
|
|
|
if (uid < startUid || uid > endUid) {
|
2009-04-23 16:53:22 -04:00
|
|
|
throw new RuntimeException("Message uid " + uid + " not in range " + startUid + ':' + endUid);
|
2009-03-03 10:49:42 -05:00
|
|
|
}
|
|
|
|
return message;
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void remove() {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-05 05:06:47 -05:00
|
|
|
protected static class RangeIterator extends AbstractRangeIterator {
|
2009-02-24 05:22:55 -05:00
|
|
|
final String[] ranges;
|
2009-04-16 18:20:30 -04:00
|
|
|
int currentRangeIndex;
|
2009-02-20 12:06:03 -05:00
|
|
|
long startUid;
|
|
|
|
long endUid;
|
|
|
|
|
2010-01-05 05:06:47 -05:00
|
|
|
protected RangeIterator(ExchangeSession.MessageList messages, String value) {
|
|
|
|
this.messages = messages;
|
2009-02-20 12:06:03 -05:00
|
|
|
ranges = value.split(",");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected long convertToLong(String value) {
|
|
|
|
if ("*".equals(value)) {
|
|
|
|
return Long.MAX_VALUE;
|
|
|
|
} else {
|
|
|
|
return Long.parseLong(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 07:42:07 -04:00
|
|
|
protected void skipToNextRangeStart() {
|
2009-02-20 12:06:03 -05:00
|
|
|
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));
|
2009-09-07 07:42:07 -04:00
|
|
|
if (endUid < startUid) {
|
|
|
|
long swap = endUid;
|
|
|
|
endUid = startUid;
|
|
|
|
startUid = swap;
|
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
} else if ("*".equals(currentRange)) {
|
2010-06-02 18:32:28 -04:00
|
|
|
startUid = endUid = messages.size();
|
2009-02-20 12:06:03 -05:00
|
|
|
} else {
|
|
|
|
startUid = endUid = convertToLong(currentRange);
|
|
|
|
}
|
2010-01-05 05:06:47 -05:00
|
|
|
while (currentIndex < messages.size() && (currentIndex + 1) < startUid) {
|
2009-02-20 12:06:03 -05:00
|
|
|
currentIndex++;
|
|
|
|
}
|
|
|
|
} else {
|
2010-01-05 05:06:47 -05:00
|
|
|
currentIndex = messages.size();
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 07:42:07 -04:00
|
|
|
protected boolean hasNextInRange() {
|
|
|
|
return hasNextIndex() && currentIndex < endUid;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean hasNextIndex() {
|
2010-01-05 05:06:47 -05:00
|
|
|
return currentIndex < messages.size();
|
2009-09-07 07:42:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean hasNextRange() {
|
|
|
|
return currentRangeIndex < ranges.length;
|
|
|
|
}
|
|
|
|
|
2009-02-20 12:06:03 -05:00
|
|
|
public boolean hasNext() {
|
2009-09-07 07:42:07 -04:00
|
|
|
boolean hasNextInRange = hasNextInRange();
|
|
|
|
// if has next range and current index after current range end, reset index
|
|
|
|
if (hasNextRange() && !hasNextInRange) {
|
|
|
|
currentIndex = 0;
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
2009-09-07 07:42:07 -04:00
|
|
|
while (hasNextIndex() && !hasNextInRange) {
|
|
|
|
skipToNextRangeStart();
|
|
|
|
hasNextInRange = hasNextInRange();
|
|
|
|
}
|
|
|
|
return hasNextIndex();
|
2009-02-20 12:06:03 -05:00
|
|
|
}
|
|
|
|
|
2009-02-04 16:55:42 -05:00
|
|
|
public ExchangeSession.Message next() {
|
2010-01-05 05:06:47 -05:00
|
|
|
return messages.get(currentIndex++);
|
2009-02-04 16:55:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void remove() {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
}
|
2009-02-17 17:59:32 -05:00
|
|
|
|
|
|
|
class IMAPTokenizer extends StringTokenizer {
|
2009-04-16 17:52:17 -04:00
|
|
|
IMAPTokenizer(String value) {
|
2009-02-17 17:59:32 -05:00
|
|
|
super(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String nextToken() {
|
2009-03-16 20:25:10 -04:00
|
|
|
return removeQuotes(nextQuotedToken());
|
|
|
|
}
|
|
|
|
|
|
|
|
public String nextQuotedToken() {
|
2009-02-17 17:59:32 -05:00
|
|
|
StringBuilder nextToken = new StringBuilder();
|
|
|
|
nextToken.append(super.nextToken());
|
|
|
|
while (hasMoreTokens() && nextToken.length() > 0 && nextToken.charAt(0) == '"'
|
2009-12-07 18:58:38 -05:00
|
|
|
&& (nextToken.charAt(nextToken.length() - 1) != '"' || nextToken.length() == 1)) {
|
2009-02-17 17:59:32 -05:00
|
|
|
nextToken.append(' ').append(super.nextToken());
|
|
|
|
}
|
|
|
|
while (hasMoreTokens() && nextToken.length() > 0 && nextToken.charAt(0) == '('
|
|
|
|
&& nextToken.charAt(nextToken.length() - 1) != ')') {
|
|
|
|
nextToken.append(' ').append(super.nextToken());
|
|
|
|
}
|
2009-03-16 20:25:10 -04:00
|
|
|
return nextToken.toString();
|
2009-02-17 17:59:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-02 10:38:02 -05:00
|
|
|
}
|