mirror of
https://github.com/moparisthebest/davmail
synced 2025-02-28 09:21:49 -05:00
Replace int states with enum and hide IMAP password in logs, refactor IMAP command parsing
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@309 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
f92fa85d1a
commit
1389e48831
@ -2,17 +2,25 @@ package davmail;
|
||||
|
||||
import davmail.exchange.ExchangeSession;
|
||||
import davmail.exchange.ExchangeSessionFactory;
|
||||
import davmail.smtp.SmtpConnection;
|
||||
import davmail.tray.DavGatewayTray;
|
||||
import org.apache.commons.httpclient.util.Base64;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
|
||||
|
||||
/**
|
||||
* Generic connection common to pop3 and smtp implementations
|
||||
*/
|
||||
public class AbstractConnection extends Thread {
|
||||
|
||||
protected enum State {INITIAL, LOGIN, USER, PASSWORD, AUTHENTICATED, STARTMAIL, RECIPIENT, MAILDATA}
|
||||
|
||||
/**
|
||||
* read password state
|
||||
*/
|
||||
public static final int PASSWORD = 1;
|
||||
|
||||
protected final Socket client;
|
||||
|
||||
protected BufferedReader in;
|
||||
@ -21,7 +29,7 @@ public class AbstractConnection extends Thread {
|
||||
protected String userName = null;
|
||||
protected String password = null;
|
||||
// connection state
|
||||
protected int state = 0;
|
||||
protected State state = State.INITIAL;
|
||||
// Exchange session proxy
|
||||
protected ExchangeSession session;
|
||||
|
||||
@ -102,12 +110,15 @@ public class AbstractConnection extends Thread {
|
||||
*/
|
||||
public String readClient() throws IOException {
|
||||
String line = in.readLine();
|
||||
// TODO : add basic authorization check
|
||||
if (line != null) {
|
||||
if (line.startsWith("PASS")) {
|
||||
DavGatewayTray.debug("< PASS ********");
|
||||
} else if (state == SmtpConnection.PASSWORD) {
|
||||
// IMAP LOGIN
|
||||
} else if (line.startsWith("LOGIN")) {
|
||||
DavGatewayTray.debug("< LOGIN ********");
|
||||
} else if (state == State.PASSWORD) {
|
||||
DavGatewayTray.debug("< ********");
|
||||
// HTTP Basic Authentication
|
||||
} else if (line.startsWith("Authorization:")) {
|
||||
DavGatewayTray.debug("< Authorization: ********");
|
||||
} else {
|
||||
|
@ -1,31 +1,30 @@
|
||||
package davmail.imap;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.SocketException;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.sun.mail.imap.protocol.BASE64MailboxDecoder;
|
||||
import com.sun.mail.imap.protocol.BASE64MailboxEncoder;
|
||||
import davmail.AbstractConnection;
|
||||
import davmail.tray.DavGatewayTray;
|
||||
import davmail.exchange.ExchangeSession;
|
||||
import davmail.exchange.ExchangeSessionFactory;
|
||||
import com.sun.mail.imap.protocol.BASE64MailboxEncoder;
|
||||
import com.sun.mail.imap.protocol.BASE64MailboxDecoder;
|
||||
import davmail.tray.DavGatewayTray;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Dav Gateway smtp connection implementation.
|
||||
* Still alpha code : need to find a way to handle message ids
|
||||
*/
|
||||
public class ImapConnection extends AbstractConnection {
|
||||
protected static final int INITIAL = 0;
|
||||
protected static final int AUTHENTICATED = 1;
|
||||
protected static final int AUTHENTICATED = 2;
|
||||
|
||||
ExchangeSession.Folder currentFolder;
|
||||
List<ExchangeSession.Message> messages;
|
||||
@ -79,11 +78,11 @@ public class ImapConnection extends AbstractConnection {
|
||||
try {
|
||||
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||
sendClient(commandId + " OK Authenticated");
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
} catch (Exception e) {
|
||||
DavGatewayTray.error(e);
|
||||
sendClient(commandId + " NO LOGIN failed");
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
}
|
||||
} else if ("AUTHENTICATE".equalsIgnoreCase(command)) {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
@ -96,11 +95,11 @@ public class ImapConnection extends AbstractConnection {
|
||||
try {
|
||||
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||
sendClient(commandId + " OK Authenticated");
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
} catch (Exception e) {
|
||||
DavGatewayTray.error(e);
|
||||
sendClient(commandId + " NO LOGIN failed");
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
}
|
||||
} else {
|
||||
sendClient(commandId + " NO unsupported authentication method");
|
||||
@ -109,14 +108,14 @@ public class ImapConnection extends AbstractConnection {
|
||||
sendClient(commandId + " BAD authentication method required");
|
||||
}
|
||||
} else {
|
||||
if (state != AUTHENTICATED) {
|
||||
if (state != State.AUTHENTICATED) {
|
||||
sendClient(commandId + " BAD command authentication required");
|
||||
} else {
|
||||
if ("lsub".equalsIgnoreCase(command) || "list".equalsIgnoreCase(command)) {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String folderContext = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderContext = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String folderQuery = folderContext + BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderQuery = folderContext + BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
if (folderQuery.endsWith("%/%")) {
|
||||
folderQuery = folderQuery.substring(0, folderQuery.length() - 2);
|
||||
}
|
||||
@ -144,7 +143,7 @@ public class ImapConnection extends AbstractConnection {
|
||||
}
|
||||
} else if ("select".equalsIgnoreCase(command) || "examine".equalsIgnoreCase(command)) {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
currentFolder = session.getFolder(folderName);
|
||||
messages = session.getAllMessages(currentFolder.folderUrl);
|
||||
sendClient("* " + currentFolder.objectCount + " EXISTS");
|
||||
@ -164,15 +163,15 @@ public class ImapConnection extends AbstractConnection {
|
||||
sendClient(commandId + " OK CLOSE completed");
|
||||
} else if ("create".equalsIgnoreCase(command)) {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
session.createFolder(folderName);
|
||||
sendClient(commandId + " OK folder created");
|
||||
} else {
|
||||
sendClient(commandId + " BAD missing create argument");
|
||||
}
|
||||
} else if ("rename".equalsIgnoreCase(command)) {
|
||||
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String targetName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
String targetName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
try {
|
||||
session.moveFolder(folderName, targetName);
|
||||
sendClient(commandId + " OK rename completed");
|
||||
@ -214,7 +213,7 @@ public class ImapConnection extends AbstractConnection {
|
||||
for (int messageIndex = startIndex; messageIndex <= endIndex; messageIndex++) {
|
||||
ExchangeSession.Message message = messages.get(messageIndex - 1);
|
||||
|
||||
if ("(BODYSTRUCTURE)".equals(parameters)) {
|
||||
if ("BODYSTRUCTURE".equals(parameters)) {
|
||||
sendClient("* " + messageIndex + " FETCH (BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"windows-1252\") NIL NIL \"QUOTED-PRINTABLE\" " + message.size + " 50 NIL NIL NIL NIL))");
|
||||
} else if (parameters.indexOf("BODY[]") >= 0) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
@ -295,12 +294,13 @@ public class ImapConnection extends AbstractConnection {
|
||||
sendClient(commandId + " BAD command unrecognized");
|
||||
}
|
||||
} else if ("fetch".equalsIgnoreCase(command)) {
|
||||
// TODO : refactor with uid fetch
|
||||
if (tokens.hasMoreTokens()) {
|
||||
int messageIndex = Integer.parseInt(tokens.nextToken());
|
||||
ExchangeSession.Message message = messages.get(messageIndex - 1);
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String parameters = tokens.nextToken();
|
||||
if ("(BODYSTRUCTURE)".equals(parameters)) {
|
||||
if ("BODYSTRUCTURE".equals(parameters)) {
|
||||
sendClient("* " + messageIndex + " FETCH (BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"windows-1252\") NIL NIL \"QUOTED-PRINTABLE\" " + message.size + " 50 NIL NIL NIL NIL))");
|
||||
sendClient(commandId + " OK FETCH completed");
|
||||
} else {
|
||||
@ -311,9 +311,9 @@ public class ImapConnection extends AbstractConnection {
|
||||
}
|
||||
}
|
||||
} else if ("append".equalsIgnoreCase(command)) {
|
||||
String folderName = BASE64MailboxDecoder.decode(removeQuotes(tokens.nextToken()));
|
||||
String folderName = BASE64MailboxDecoder.decode(tokens.nextToken());
|
||||
String parameters = tokens.nextToken();
|
||||
int size = Integer.parseInt(removeQuotes(tokens.nextToken()));
|
||||
int size = Integer.parseInt(tokens.nextToken());
|
||||
sendClient("+ send literal data");
|
||||
char[] buffer = new char[size];
|
||||
int index = 0;
|
||||
@ -386,13 +386,13 @@ public class ImapConnection extends AbstractConnection {
|
||||
*/
|
||||
protected void parseCredentials(StringTokenizer tokens) throws IOException {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
userName = removeQuotes(tokens.nextToken());
|
||||
userName = tokens.nextToken();
|
||||
} else {
|
||||
throw new IOException("Invalid credentials");
|
||||
}
|
||||
|
||||
if (tokens.hasMoreTokens()) {
|
||||
password = removeQuotes(tokens.nextToken());
|
||||
password = tokens.nextToken();
|
||||
} else {
|
||||
throw new IOException("Invalid credentials");
|
||||
}
|
||||
|
@ -18,9 +18,6 @@ import java.util.StringTokenizer;
|
||||
* Dav Gateway pop connection implementation
|
||||
*/
|
||||
public class PopConnection extends AbstractConnection {
|
||||
protected static final int INITIAL = 0;
|
||||
protected static final int USER = 1;
|
||||
protected static final int AUTHENTICATED = 2;
|
||||
private List<ExchangeSession.Message> messages;
|
||||
|
||||
// Initialize the streams and start the thread
|
||||
@ -93,15 +90,15 @@ public class PopConnection extends AbstractConnection {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
userName = tokens.nextToken();
|
||||
sendOK("USER : " + userName);
|
||||
state = USER;
|
||||
state = State.USER;
|
||||
} else {
|
||||
sendERR("invalid syntax");
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
}
|
||||
} else if ("PASS".equalsIgnoreCase(command)) {
|
||||
if (state != USER) {
|
||||
if (state != State.USER) {
|
||||
sendERR("invalid state");
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
} else if (!tokens.hasMoreTokens()) {
|
||||
sendERR("invalid syntax");
|
||||
} else {
|
||||
@ -111,7 +108,7 @@ public class PopConnection extends AbstractConnection {
|
||||
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||
messages = session.getAllMessages("INBOX");
|
||||
sendOK("PASS");
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
} catch (SocketException e) {
|
||||
// can not send error to client after a socket exception
|
||||
DavGatewayTray.warn("Client closed connection ", e);
|
||||
@ -123,7 +120,7 @@ public class PopConnection extends AbstractConnection {
|
||||
} else if ("CAPA".equalsIgnoreCase(command)) {
|
||||
sendOK("Capability list follows");
|
||||
printCapabilities();
|
||||
} else if (state != AUTHENTICATED) {
|
||||
} else if (state != State.AUTHENTICATED) {
|
||||
sendERR("Invalid state not authenticated");
|
||||
} else {
|
||||
if ("STAT".equalsIgnoreCase(command)) {
|
||||
|
@ -18,13 +18,6 @@ import java.util.ArrayList;
|
||||
* Dav Gateway smtp connection implementation
|
||||
*/
|
||||
public class SmtpConnection extends AbstractConnection {
|
||||
protected static final int INITIAL = 0;
|
||||
protected static final int AUTHENTICATED = 1;
|
||||
protected static final int STARTMAIL = 2;
|
||||
protected static final int RECIPIENT = 3;
|
||||
protected static final int MAILDATA = 4;
|
||||
protected static final int LOGIN = 5;
|
||||
public static final int PASSWORD = 6;
|
||||
|
||||
// Initialize the streams and start the thread
|
||||
public SmtpConnection(Socket clientSocket) {
|
||||
@ -50,12 +43,12 @@ public class SmtpConnection extends AbstractConnection {
|
||||
if (tokens.hasMoreTokens()) {
|
||||
String command = tokens.nextToken();
|
||||
|
||||
if (state == LOGIN) {
|
||||
if (state == State.LOGIN) {
|
||||
// AUTH LOGIN, read userName
|
||||
userName = base64Decode(line);
|
||||
sendClient("334 " + base64Encode("Password:"));
|
||||
state = PASSWORD;
|
||||
} else if (state == PASSWORD) {
|
||||
state = State.PASSWORD;
|
||||
} else if (state == State.PASSWORD) {
|
||||
// AUTH LOGIN, read password
|
||||
password = base64Decode(line);
|
||||
authenticate();
|
||||
@ -78,7 +71,7 @@ public class SmtpConnection extends AbstractConnection {
|
||||
authenticate();
|
||||
} else if ("LOGIN".equals(authType)) {
|
||||
sendClient("334 " + base64Encode("Username:"));
|
||||
state = LOGIN;
|
||||
state = State.LOGIN;
|
||||
} else {
|
||||
sendClient("451 Error : unknown authentication type");
|
||||
}
|
||||
@ -86,23 +79,23 @@ public class SmtpConnection extends AbstractConnection {
|
||||
sendClient("451 Error : authentication type not specified");
|
||||
}
|
||||
} else if ("MAIL".equals(command)) {
|
||||
if (state == AUTHENTICATED) {
|
||||
state = STARTMAIL;
|
||||
if (state == State.AUTHENTICATED) {
|
||||
state = State.STARTMAIL;
|
||||
recipients.clear();
|
||||
sendClient("250 Sender OK");
|
||||
} else {
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
sendClient("503 Bad sequence of commands");
|
||||
}
|
||||
} else if ("RCPT".equals(command)) {
|
||||
if (state == STARTMAIL || state == RECIPIENT) {
|
||||
if (state == State.STARTMAIL || state == State.RECIPIENT) {
|
||||
if (line.startsWith("RCPT TO:")) {
|
||||
state = RECIPIENT;
|
||||
state = State.RECIPIENT;
|
||||
try {
|
||||
InternetAddress internetAddress = new InternetAddress(line.substring("RCPT TO:".length()));
|
||||
recipients.add(internetAddress.getAddress());
|
||||
} catch (AddressException e) {
|
||||
throw new IOException("Invalid recipient: "+line);
|
||||
throw new IOException("Invalid recipient: " + line);
|
||||
}
|
||||
sendClient("250 Recipient OK");
|
||||
} else {
|
||||
@ -110,26 +103,26 @@ public class SmtpConnection extends AbstractConnection {
|
||||
}
|
||||
|
||||
} else {
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
sendClient("503 Bad sequence of commands");
|
||||
}
|
||||
} else if ("DATA".equals(command)) {
|
||||
if (state == RECIPIENT) {
|
||||
state = MAILDATA;
|
||||
if (state == State.RECIPIENT) {
|
||||
state = State.MAILDATA;
|
||||
sendClient("354 Start mail input; end with <CRLF>.<CRLF>");
|
||||
|
||||
try {
|
||||
session.sendMessage(recipients, in);
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
sendClient("250 Queued mail for delivery");
|
||||
} catch (Exception e) {
|
||||
DavGatewayTray.error("Authentication failed", e);
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
sendClient("451 Error : " + e + " " + e.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
sendClient("503 Bad sequence of commands");
|
||||
}
|
||||
}
|
||||
@ -165,7 +158,7 @@ public class SmtpConnection extends AbstractConnection {
|
||||
try {
|
||||
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||
sendClient("235 OK Authenticated");
|
||||
state = AUTHENTICATED;
|
||||
state = State.AUTHENTICATED;
|
||||
} catch (Exception e) {
|
||||
DavGatewayTray.error(e);
|
||||
String message = e.getMessage();
|
||||
@ -174,7 +167,7 @@ public class SmtpConnection extends AbstractConnection {
|
||||
}
|
||||
message = message.replaceAll("\\n", " ");
|
||||
sendClient("554 Authenticated failed " + message);
|
||||
state = INITIAL;
|
||||
state = State.INITIAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user