From 8ab4d39cc621f0db323b7076f034d439c42eafe9 Mon Sep 17 00:00:00 2001 From: mguessan Date: Thu, 26 Apr 2007 10:29:11 +0000 Subject: [PATCH] Improve authentication failure handling, send explicit message to client git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@52 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- src/java/davmail/AbstractServer.java | 17 +- src/java/davmail/DavGatewayTray.java | 1 + .../davmail/exchange/BASE64EncoderStream.java | 152 +++++++----------- .../davmail/exchange/ExchangeSession.java | 57 ++++--- src/java/davmail/exchange/XmlDocument.java | 2 +- src/java/davmail/imap/ImapConnection.java | 4 +- src/java/davmail/pop/PopConnection.java | 9 +- src/java/davmail/smtp/SmtpConnection.java | 19 ++- 8 files changed, 131 insertions(+), 130 deletions(-) diff --git a/src/java/davmail/AbstractServer.java b/src/java/davmail/AbstractServer.java index 0a2d4310..9d93e0f1 100644 --- a/src/java/davmail/AbstractServer.java +++ b/src/java/davmail/AbstractServer.java @@ -18,17 +18,13 @@ public abstract class AbstractServer extends Thread { public AbstractServer(int port) { this.port = port; try { + //noinspection SocketOpenedButNotSafelyClosed serverSocket = new ServerSocket(port); } catch (IOException e) { DavGatewayTray.error("Exception creating server socket", e); } } - // Exit with an error message, when an exception occurs. - public static void fail(Exception e, String msg) { - System.err.println(msg + ": " + e); - System.exit(1); - } /** * The body of the server thread. Loop forever, listening for and @@ -37,10 +33,11 @@ public abstract class AbstractServer extends Thread { * new Socket. */ public void run() { + Socket clientSocket = null; try { //noinspection InfiniteLoopStatement while (true) { - Socket clientSocket = serverSocket.accept(); + clientSocket = serverSocket.accept(); DavGatewayTray.debug("Connection from " + clientSocket.getInetAddress() + " on port " + port); // only accept localhost connections for security reasons if (clientSocket.getInetAddress().toString().indexOf("127.0.0.1") > 0) { @@ -53,6 +50,14 @@ public abstract class AbstractServer extends Thread { } } catch (IOException e) { DavGatewayTray.warn("Exception while listening for connections", e); + } finally { + try { + if (clientSocket != null) { + clientSocket.close(); + } + } catch (IOException e) { + // ignore + } } } diff --git a/src/java/davmail/DavGatewayTray.java b/src/java/davmail/DavGatewayTray.java index 8c45b98e..19633022 100644 --- a/src/java/davmail/DavGatewayTray.java +++ b/src/java/davmail/DavGatewayTray.java @@ -160,6 +160,7 @@ public class DavGatewayTray { ActionListener exitListener = new ActionListener() { public void actionPerformed(ActionEvent e) { SystemTray.getSystemTray().remove(trayIcon); + //noinspection CallToSystemExit System.exit(0); } }; diff --git a/src/java/davmail/exchange/BASE64EncoderStream.java b/src/java/davmail/exchange/BASE64EncoderStream.java index 303315ab..fe901234 100644 --- a/src/java/davmail/exchange/BASE64EncoderStream.java +++ b/src/java/davmail/exchange/BASE64EncoderStream.java @@ -1,6 +1,7 @@ package davmail.exchange; // Imports + import java.io.OutputStream; import java.io.FilterOutputStream; import java.io.ByteArrayOutputStream; @@ -16,18 +17,18 @@ import java.io.UnsupportedEncodingException; * @author David A. Herman * @version 1.0 of September 2000 * @see java.io.FilterOutputStream - **/ + */ public class BASE64EncoderStream extends FilterOutputStream { /** * Useful constant representing the default maximum number of output * characters per line (76). - **/ + */ public static final int LINE_LENGTH = 76; /** * The BASE64 alphabet. - **/ + */ private static final byte[] alphabet; /** @@ -45,28 +46,28 @@ public class BASE64EncoderStream extends FilterOutputStream { /** * The internal buffer of encoded output bytes. - **/ + */ private byte[] output = new byte[4]; /** * The internal buffer of input bytes to be encoded. - **/ - private byte[] input = new byte[3]; + */ + private byte[] input = new byte[3]; /** * The index of the next position in the internal buffer of input bytes * at which to store input. - **/ + */ private int inputIndex = 0; /** * The number of characters that have been output on the current line. - **/ + */ private int chars = 0; /** * The maximum number of characters to output per line. - **/ + */ private int maxLineLength; /** @@ -74,7 +75,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * character of output data. This index is generated as input data comes * in, sometimes requiring more than one byte of input before it is * completely calculated, so it is shared in the object. - **/ + */ private int index; /** @@ -82,7 +83,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * stream, with the default maximum number of characters per line. * * @param out output stream - **/ + */ public BASE64EncoderStream(OutputStream out) { this(out, LINE_LENGTH); } @@ -96,7 +97,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * * @param out the underlying output stream. * @param max the maximum number of output bytes per line. - **/ + */ public BASE64EncoderStream(OutputStream out, int max) { super(out); maxLineLength = max; @@ -114,7 +115,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * underlying output stream, and closes the underlying output stream. * * @throws IOException if an I/O error occurs. - **/ + */ public void close() throws IOException { try { flush(); @@ -122,7 +123,6 @@ public class BASE64EncoderStream extends FilterOutputStream { // ignore } - // Add a terminating CRLF sequence. out.write('\r'); out.write('\n'); @@ -137,7 +137,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * * @param b the byte array to be encoded. * @throws IOException if an I/O error occurs. - **/ + */ public void write(byte b[]) throws IOException { write(b, 0, b.length); } @@ -147,11 +147,11 @@ public class BASE64EncoderStream extends FilterOutputStream { * at offset off, to be written to the underlying output * stream. * - * @param b the byte array to be encoded. + * @param b the byte array to be encoded. * @param off the offset at which to start reading from the byte array. * @param len the number of bytes to read. * @throws IOException if an I/O error occurs. - **/ + */ public void write(byte b[], int off, int len) throws IOException { for (int i = 0; i < len; i++) { write(b[off + i]); @@ -167,7 +167,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * * @param b the integer whose low-order byte is to be encoded. * @throws IOException if an I/O error occurs. - **/ + */ public void write(int b) throws IOException { switch (inputIndex) { case 0: @@ -179,7 +179,7 @@ public class BASE64EncoderStream extends FilterOutputStream { // ---------------------------- // Output: 00 XXXXXX - input[0] = (byte)(b & 0xFF); + input[0] = (byte) (b & 0xFF); index = ((input[0] & 0xFC) >> 2); output[0] = alphabet[index]; @@ -188,7 +188,7 @@ public class BASE64EncoderStream extends FilterOutputStream { // to be the last byte of input, then it will // already be padded with zeroes, and the rest // can be padded with '=' characters. - index = ((input[0] & 0x03) << 4); + index = ((input[0] & 0x03) << 4); break; @@ -202,7 +202,7 @@ public class BASE64EncoderStream extends FilterOutputStream { // ---------------------------- // Output: 00 XX YYYY - input[1] = (byte)(b & 0xFF); + input[1] = (byte) (b & 0xFF); // The first two bits of the second output character // have already been calculated and stored in the @@ -216,7 +216,7 @@ public class BASE64EncoderStream extends FilterOutputStream { // to be the last byte of input, then it will // already be padded with zeroes, and the rest // can be padded with '=' characters. - index = ((input[1] & 0x0F) << 2); + index = ((input[1] & 0x0F) << 2); break; @@ -230,7 +230,7 @@ public class BASE64EncoderStream extends FilterOutputStream { // ---------------------------- // Output: 00 XXXX YY - input[2] = (byte)(b & 0xFF); + input[2] = (byte) (b & 0xFF); // The first four bits of the third output character // have already been calculated and stored in the @@ -268,21 +268,19 @@ public class BASE64EncoderStream extends FilterOutputStream { * buffer is filled. * * @throws IOException if an I/O error occurs. - **/ + */ private void writeOutput() throws IOException { int newchars = (chars + 4) % maxLineLength; if (newchars == 0) { out.write(output); out.write('\r'); out.write('\n'); - } - else if (newchars < chars) { + } else if (newchars < chars) { out.write(output, 0, 4 - newchars); out.write('\r'); out.write('\n'); out.write(output, 4 - newchars, newchars); - } - else + } else out.write(output); chars = newchars; } @@ -297,7 +295,7 @@ public class BASE64EncoderStream extends FilterOutputStream { * 6.8, for more information. * * @throws IOException if an I/O error occurs. - **/ + */ private void pad() throws IOException { // If the input index is 0, then we ended on a multiple of 3 bytes // of input, so no padding is necessary. @@ -325,76 +323,44 @@ public class BASE64EncoderStream extends FilterOutputStream { } } - public static byte[] encode(byte[] bytes) { + public static byte[] encode(byte[] bytes) { - // Note: This is a public method on Sun's implementation - // and so it should be supported for compatibility. - // Also this method is used by the "B" encoding for now. - // This implementation usesthe encoding stream to - // process the bytes. Possibly, the BASE64 encoding - // stream should use this method for it's encoding. + // Note: This is a public method on Sun's implementation + // and so it should be supported for compatibility. + // Also this method is used by the "B" encoding for now. + // This implementation usesthe encoding stream to + // process the bytes. Possibly, the BASE64 encoding + // stream should use this method for it's encoding. - // Variables - ByteArrayOutputStream byteStream; - BASE64EncoderStream encoder; + // Variables + ByteArrayOutputStream byteStream; + BASE64EncoderStream encoder = null; - // Create Streams - byteStream = new ByteArrayOutputStream(); - encoder = new BASE64EncoderStream(byteStream); + // Create Streams + byteStream = new ByteArrayOutputStream(); - try { - - // Write Bytes - encoder.write(bytes); - encoder.flush(); - encoder.close(); - - } catch (IOException e) { - // ignore - } // try - - // Return Encoded Byte Array - return byteStream.toByteArray(); - - } // encode() - - /** - * For testing. Takes in a file name from the command line, or - * prompts the user for one if there are no command line options, and - * encodes the data in the file to BASE64, outputting the encoded - * data to System.out. - * - * @param args the command line arguments. - **/ - public static void main(String args[]) { - String fileName = ""; - if (args.length > 0) - fileName = args[0]; - else { - System.out.print("File name: "); - java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); - try { - fileName = in.readLine(); - } - catch (Throwable e) { - e.printStackTrace(); - System.exit(1); - } - } try { - java.io.FileInputStream in = new java.io.FileInputStream(fileName); - BASE64EncoderStream out = new BASE64EncoderStream(System.out); - int d = in.read(); - while (d != -1) { - out.write(d); - d = in.read(); + encoder = new BASE64EncoderStream(byteStream); + + // Write Bytes + encoder.write(bytes); + encoder.flush(); + + } catch (IOException e) { + // ignore + } finally { + try { + if (encoder != null) { + encoder.close(); + } + } catch (IOException e) { + // ignore } - in.close(); - out.close(); } - catch (Throwable e) { - e.printStackTrace(); - } - } + + // Return Encoded Byte Array + return byteStream.toByteArray(); + + } // encode() } \ No newline at end of file diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index f7948297..8de659bd 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -1,23 +1,23 @@ package davmail.exchange; +import davmail.Settings; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.util.URIUtil; +import org.apache.log4j.Logger; import org.apache.webdav.lib.Property; import org.apache.webdav.lib.ResponseEntity; import org.apache.webdav.lib.WebdavResource; -import org.apache.log4j.Logger; import org.jdom.Attribute; -import org.jdom.JDOMException; import org.jdom.input.DOMBuilder; -import org.w3c.tidy.Tidy; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.Element; +import org.w3c.tidy.Tidy; import javax.mail.MessagingException; import javax.mail.internet.MimeUtility; @@ -27,8 +27,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; -import davmail.Settings; - /** * Exchange session through Outlook Web Access (DAV) */ @@ -136,6 +134,9 @@ public class ExchangeSession { * @param httpClient current Http client */ protected static void configureClient(HttpClient httpClient) { + // do not send basic auth automatically + httpClient.getState().setAuthenticationPreemptive(false); + String enableProxy = Settings.getProperty("davmail.enableProxy"); String proxyHost = null; String proxyPort = null; @@ -177,19 +178,20 @@ public class ExchangeSession { HttpClient httpClient = new HttpClient(); configureClient(httpClient); - // get webmail root url (will follow redirects) + // get webmail root url (will not follow redirects) HttpMethod testMethod = new GetMethod(url); + testMethod.setFollowRedirects(false); int status = httpClient.executeMethod(testMethod); testMethod.releaseConnection(); logger.debug("Test configuration status: " + status); - if (status != HttpStatus.SC_OK) { + if (status != HttpStatus.SC_OK && status != HttpStatus.SC_UNAUTHORIZED) { throw new IOException("Unable to connect to OWA at " + url + ", status code " + status + ", check configuration"); } } catch (Exception exc) { - logger.error("DavMail configuration exception: \n"+exc.getMessage(), exc); - throw new IOException("DavMail configuration exception: \n"+exc.getMessage(), exc); + logger.error("DavMail configuration exception: \n" + exc.getMessage(), exc); + throw new IOException("DavMail configuration exception: \n" + exc.getMessage(), exc); } } @@ -224,8 +226,6 @@ public class ExchangeSession { authPrefs.add(AuthPolicy.BASIC); httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,authPrefs); */ - // do not send basic auth automatically - httpClient.getState().setAuthenticationPreemptive(false); configureClient(httpClient); @@ -272,6 +272,7 @@ public class ExchangeSession { && status != HttpStatus.SC_OK) { HttpException ex = new HttpException(); ex.setReasonCode(status); + ex.setReason(method.getStatusText()); throw ex; } @@ -338,14 +339,34 @@ public class ExchangeSession { wdr.setPath(URIUtil.getPath(inboxUrl)); } catch (Exception exc) { - logger.error("Exchange login exception ", exc); + StringBuffer message = new StringBuffer(); + message.append("DavMail login exception: "); + if (exc.getMessage() != null) { + message.append(exc.getMessage()); + } else if (exc instanceof HttpException) { + message.append(((HttpException) exc).getReasonCode()); + String httpReason = ((HttpException) exc).getReason(); + if (httpReason != null) { + message.append(" "); + message.append(httpReason); + } + } else { + message.append(exc); + } try { - System.err.println( - wdr.getStatusCode() + " " + wdr.getStatusMessage()); + message.append("\nWebdav status:"); + message.append(wdr.getStatusCode()); + + String webdavStatusMessage = wdr.getStatusMessage(); + if (webdavStatusMessage != null) { + message.append(webdavStatusMessage); + } } catch (Exception e) { logger.error("Exception getting status from " + wdr); } - throw exc; + + logger.error(message.toString()); + throw new IOException(message.toString()); } } @@ -1174,9 +1195,7 @@ public class ExchangeSession { } } xmlDocument.load(builder.build(w3cDocument)); - } catch (IOException ex1) { - logger.error("Exception parsing document", ex1); - } catch (JDOMException ex1) { + } catch (Exception ex1) { logger.error("Exception parsing document", ex1); } return xmlDocument; diff --git a/src/java/davmail/exchange/XmlDocument.java b/src/java/davmail/exchange/XmlDocument.java index e6b7a9d0..63ca1114 100644 --- a/src/java/davmail/exchange/XmlDocument.java +++ b/src/java/davmail/exchange/XmlDocument.java @@ -116,7 +116,7 @@ public class XmlDocument { document = new SAXBuilder().build(stream, dtd); } - public void load(Document value) throws JDOMException, IOException { + public void load(Document value) { document = value; } diff --git a/src/java/davmail/imap/ImapConnection.java b/src/java/davmail/imap/ImapConnection.java index c07f0ba7..c7359615 100644 --- a/src/java/davmail/imap/ImapConnection.java +++ b/src/java/davmail/imap/ImapConnection.java @@ -56,7 +56,7 @@ public class ImapConnection extends AbstractConnection { sendClient(commandId + " OK Authenticated"); state = AUTHENTICATED; } catch (Exception e) { - DavGatewayTray.error("Authentication failed",e); + DavGatewayTray.error(e.getMessage()); sendClient(commandId + " NO LOGIN failed"); state = INITIAL; } @@ -178,7 +178,7 @@ public class ImapConnection extends AbstractConnection { return result; } - public void sendMessage(StringBuffer buffer) throws Exception { + public void sendMessage(StringBuffer buffer) { // TODO implement } } diff --git a/src/java/davmail/pop/PopConnection.java b/src/java/davmail/pop/PopConnection.java index a57d3bb0..9242b898 100644 --- a/src/java/davmail/pop/PopConnection.java +++ b/src/java/davmail/pop/PopConnection.java @@ -101,8 +101,13 @@ public class PopConnection extends AbstractConnection { sendOK("PASS"); state = AUTHENTICATED; } catch (Exception e) { - DavGatewayTray.error("Authentication failed", e); - sendERR("authentication failed : " + e + " " + e.getMessage()); + String message = e.getMessage(); + if (message == null) { + message = e.toString(); + } + DavGatewayTray.error(message); + message = message.replaceAll("\\n", " "); + sendERR("authentication failed : " + message); } } } else if ("CAPA".equalsIgnoreCase(command)) { diff --git a/src/java/davmail/smtp/SmtpConnection.java b/src/java/davmail/smtp/SmtpConnection.java index 4bebf08b..8f61baed 100644 --- a/src/java/davmail/smtp/SmtpConnection.java +++ b/src/java/davmail/smtp/SmtpConnection.java @@ -65,8 +65,13 @@ public class SmtpConnection extends AbstractConnection { sendClient("235 OK Authenticated"); state = AUTHENTICATED; } catch (Exception e) { - DavGatewayTray.warn("Authentication failed",e); - sendClient("554 Authenticated failed"); + String message = e.getMessage(); + if (message == null) { + message = e.toString(); + } + DavGatewayTray.error(message); + message = message.replaceAll("\\n", " "); + sendClient("554 Authenticated failed " + message); state = INITIAL; } } else { @@ -101,9 +106,9 @@ public class SmtpConnection extends AbstractConnection { state = AUTHENTICATED; sendClient("250 Queued mail for delivery"); } catch (Exception e) { - DavGatewayTray.error("Authentication failed",e); + DavGatewayTray.error("Authentication failed", e); state = AUTHENTICATED; - sendClient("451 Error : " +e+" "+ e.getMessage()); + sendClient("451 Error : " + e + " " + e.getMessage()); } } else { @@ -121,7 +126,7 @@ public class SmtpConnection extends AbstractConnection { } catch (IOException e) { DavGatewayTray.error(e.getMessage()); try { - sendClient("500 "+e.getMessage()); + sendClient("500 " + e.getMessage()); } catch (IOException e2) { DavGatewayTray.debug("Exception sending error to client", e2); } @@ -129,14 +134,14 @@ public class SmtpConnection extends AbstractConnection { try { client.close(); } catch (IOException e2) { - DavGatewayTray.debug("Exception closing client",e2); + DavGatewayTray.debug("Exception closing client", e2); } try { if (session != null) { session.close(); } } catch (IOException e3) { - DavGatewayTray.debug("Exception closing gateway",e3); + DavGatewayTray.debug("Exception closing gateway", e3); } } DavGatewayTray.resetIcon();