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.smtp;
|
|
|
|
|
|
|
|
import davmail.AbstractConnection;
|
2009-04-23 10:54:06 -04:00
|
|
|
import davmail.BundleMessage;
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.exception.DavMailException;
|
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;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-09 05:10:38 -05:00
|
|
|
import javax.mail.internet.InternetAddress;
|
|
|
|
import javax.mail.internet.AddressException;
|
2006-12-12 18:57:24 -05:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.Socket;
|
2008-12-05 03:48:54 -05:00
|
|
|
import java.net.SocketException;
|
2006-12-12 18:57:24 -05:00
|
|
|
import java.util.Date;
|
|
|
|
import java.util.StringTokenizer;
|
2008-12-09 05:10:38 -05:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.ArrayList;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Dav Gateway smtp connection implementation
|
|
|
|
*/
|
|
|
|
public class SmtpConnection extends AbstractConnection {
|
|
|
|
|
2009-08-21 05:58:19 -04:00
|
|
|
/**
|
|
|
|
* Initialize the streams and start the thread.
|
|
|
|
*
|
|
|
|
* @param clientSocket SMTP client socket
|
|
|
|
*/
|
2006-12-14 10:14:18 -05:00
|
|
|
public SmtpConnection(Socket clientSocket) {
|
2009-05-04 18:31:06 -04:00
|
|
|
super(SmtpConnection.class.getSimpleName(), clientSocket, null);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-04-16 17:52:17 -04:00
|
|
|
@Override
|
2006-12-12 18:57:24 -05:00
|
|
|
public void run() {
|
|
|
|
String line;
|
|
|
|
StringTokenizer tokens;
|
2008-12-09 05:10:38 -05:00
|
|
|
List<String> recipients = new ArrayList<String>();
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
try {
|
2008-10-31 13:12:30 -04:00
|
|
|
ExchangeSessionFactory.checkConfig();
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("220 DavMail SMTP ready at " + new Date());
|
|
|
|
for (; ;) {
|
|
|
|
line = readClient();
|
|
|
|
// unable to read line, connection closed ?
|
|
|
|
if (line == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tokens = new StringTokenizer(line);
|
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
String command = tokens.nextToken();
|
|
|
|
|
2009-01-29 17:18:53 -05:00
|
|
|
if (state == State.LOGIN) {
|
2008-09-18 17:34:03 -04:00
|
|
|
// AUTH LOGIN, read userName
|
2008-11-03 20:58:08 -05:00
|
|
|
userName = base64Decode(line);
|
2008-09-18 17:34:03 -04:00
|
|
|
sendClient("334 " + base64Encode("Password:"));
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.PASSWORD;
|
|
|
|
} else if (state == State.PASSWORD) {
|
2008-09-18 17:34:03 -04:00
|
|
|
// AUTH LOGIN, read password
|
2008-11-03 20:58:08 -05:00
|
|
|
password = base64Decode(line);
|
2008-09-18 17:34:03 -04:00
|
|
|
authenticate();
|
|
|
|
} else if ("QUIT".equalsIgnoreCase(command)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("221 Closing connection");
|
|
|
|
break;
|
2010-02-18 04:21:43 -05:00
|
|
|
} else if ("NOOP".equalsIgnoreCase(command)) {
|
|
|
|
sendClient("250 OK");
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("EHLO".equalsIgnoreCase(command)) {
|
2008-01-31 18:41:17 -05:00
|
|
|
sendClient("250-" + tokens.nextToken());
|
2006-12-12 18:57:24 -05:00
|
|
|
// inform server that AUTH is supported
|
|
|
|
// actually it is mandatory (only way to get credentials)
|
|
|
|
sendClient("250-AUTH LOGIN PLAIN");
|
|
|
|
sendClient("250 Hello");
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("HELO".equalsIgnoreCase(command)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("250 Hello");
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("AUTH".equalsIgnoreCase(command)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
if (tokens.hasMoreElements()) {
|
|
|
|
String authType = tokens.nextToken();
|
2009-10-28 11:16:57 -04:00
|
|
|
if ("PLAIN".equalsIgnoreCase(authType) && tokens.hasMoreElements()) {
|
2006-12-12 18:57:24 -05:00
|
|
|
decodeCredentials(tokens.nextToken());
|
2008-09-18 17:34:03 -04:00
|
|
|
authenticate();
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("LOGIN".equalsIgnoreCase(authType)) {
|
2010-04-07 18:02:58 -04:00
|
|
|
if (tokens.hasMoreTokens()) {
|
|
|
|
// user name sent on auth line
|
|
|
|
userName = base64Decode(tokens.nextToken());
|
|
|
|
sendClient("334 " + base64Encode("Password:"));
|
|
|
|
state = State.PASSWORD;
|
|
|
|
} else {
|
|
|
|
sendClient("334 " + base64Encode("Username:"));
|
|
|
|
state = State.LOGIN;
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
|
|
|
sendClient("451 Error : unknown authentication type");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendClient("451 Error : authentication type not specified");
|
|
|
|
}
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("MAIL".equalsIgnoreCase(command)) {
|
2009-01-29 17:18:53 -05:00
|
|
|
if (state == State.AUTHENTICATED) {
|
|
|
|
state = State.STARTMAIL;
|
2008-12-09 05:10:38 -05:00
|
|
|
recipients.clear();
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("250 Sender OK");
|
|
|
|
} else {
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.INITIAL;
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("503 Bad sequence of commands");
|
|
|
|
}
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("RCPT".equalsIgnoreCase(command)) {
|
2009-01-29 17:18:53 -05:00
|
|
|
if (state == State.STARTMAIL || state == State.RECIPIENT) {
|
2009-11-02 16:17:48 -05:00
|
|
|
if (line.toUpperCase().startsWith("RCPT TO:")) {
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.RECIPIENT;
|
2008-12-09 05:10:38 -05:00
|
|
|
try {
|
|
|
|
InternetAddress internetAddress = new InternetAddress(line.substring("RCPT TO:".length()));
|
|
|
|
recipients.add(internetAddress.getAddress());
|
|
|
|
} catch (AddressException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_RECIPIENT", line);
|
2008-12-09 05:10:38 -05:00
|
|
|
}
|
|
|
|
sendClient("250 Recipient OK");
|
|
|
|
} else {
|
|
|
|
sendClient("500 Unrecognized command");
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("503 Bad sequence of commands");
|
|
|
|
}
|
2009-10-28 11:16:57 -04:00
|
|
|
} else if ("DATA".equalsIgnoreCase(command)) {
|
2009-01-29 17:18:53 -05:00
|
|
|
if (state == State.RECIPIENT) {
|
|
|
|
state = State.MAILDATA;
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("354 Start mail input; end with <CRLF>.<CRLF>");
|
|
|
|
|
|
|
|
try {
|
2008-12-09 05:10:38 -05:00
|
|
|
session.sendMessage(recipients, in);
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("250 Queued mail for delivery");
|
|
|
|
} catch (Exception e) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.error(e);
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2009-04-23 16:53:22 -04:00
|
|
|
sendClient("451 Error : " + e + ' ' + e.getMessage());
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2006-12-12 18:57:24 -05:00
|
|
|
sendClient("503 Bad sequence of commands");
|
|
|
|
}
|
2010-01-14 08:54:39 -05:00
|
|
|
} else if ("RSET".equalsIgnoreCase(command)) {
|
|
|
|
recipients.clear();
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2010-01-14 08:54:39 -05:00
|
|
|
if (state == State.STARTMAIL ||
|
|
|
|
state == State.RECIPIENT ||
|
2010-02-18 16:01:17 -05:00
|
|
|
state == State.MAILDATA ||
|
|
|
|
state == State.AUTHENTICATED) {
|
2010-01-14 08:54:39 -05:00
|
|
|
state = State.AUTHENTICATED;
|
|
|
|
} else {
|
|
|
|
state = State.INITIAL;
|
|
|
|
}
|
|
|
|
sendClient("250 OK Reset");
|
|
|
|
} else {
|
|
|
|
sendClient("500 Unrecognized command");
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
|
|
|
sendClient("500 Unrecognized command");
|
|
|
|
}
|
|
|
|
|
|
|
|
os.flush();
|
|
|
|
}
|
2008-12-05 03:48:54 -05:00
|
|
|
|
|
|
|
} catch (SocketException e) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CONNECTION_CLOSED"));
|
2009-04-15 11:20:06 -04:00
|
|
|
} catch (Exception e) {
|
2009-07-15 18:47:31 -04:00
|
|
|
DavGatewayTray.log(e);
|
2007-04-25 19:29:46 -04:00
|
|
|
try {
|
2010-01-14 08:54:39 -05:00
|
|
|
sendClient("500 " + ((e.getMessage() == null) ? e : e.getMessage()));
|
2007-04-25 19:29:46 -04:00
|
|
|
} catch (IOException e2) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
2007-04-25 19:29:46 -04: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();
|
|
|
|
}
|
|
|
|
|
2008-09-18 17:34:03 -04:00
|
|
|
/**
|
|
|
|
* Create authenticated session with Exchange server
|
2008-12-05 03:48:54 -05:00
|
|
|
*
|
2008-09-18 17:34:03 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
protected void authenticate() throws IOException {
|
|
|
|
try {
|
2008-10-31 13:12:30 -04:00
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
2008-09-18 17:34:03 -04:00
|
|
|
sendClient("235 OK Authenticated");
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.AUTHENTICATED;
|
2008-09-18 17:34:03 -04:00
|
|
|
} catch (Exception e) {
|
2008-11-24 07:35:58 -05:00
|
|
|
DavGatewayTray.error(e);
|
2008-09-18 17:34:03 -04:00
|
|
|
String message = e.getMessage();
|
|
|
|
if (message == null) {
|
|
|
|
message = e.toString();
|
|
|
|
}
|
|
|
|
message = message.replaceAll("\\n", " ");
|
|
|
|
sendClient("554 Authenticated failed " + message);
|
2009-01-29 17:18:53 -05:00
|
|
|
state = State.INITIAL;
|
2008-09-18 17:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* Decode SMTP credentials
|
|
|
|
*
|
|
|
|
* @param encodedCredentials smtp encoded credentials
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException if invalid credentials
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
|
|
|
protected void decodeCredentials(String encodedCredentials) throws IOException {
|
2008-09-18 17:34:03 -04:00
|
|
|
String decodedCredentials = base64Decode(encodedCredentials);
|
2006-12-12 18:57:24 -05:00
|
|
|
int index = decodedCredentials.indexOf((char) 0, 1);
|
|
|
|
if (index > 0) {
|
|
|
|
userName = decodedCredentials.substring(1, index);
|
|
|
|
password = decodedCredentials.substring(index + 1);
|
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CREDENTIALS");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|