diff --git a/src/java/davmail/DavGateway.java b/src/java/davmail/DavGateway.java index fbd0a424..7709446a 100644 --- a/src/java/davmail/DavGateway.java +++ b/src/java/davmail/DavGateway.java @@ -1,5 +1,6 @@ package davmail; +import davmail.http.DavGatewaySSLProtocolSocketFactory; import davmail.pop.PopServer; import davmail.smtp.SmtpServer; import davmail.tray.DavGatewayTray; @@ -57,25 +58,27 @@ public class DavGateway { DavGatewayTray.error("Exception creating server socket", e); } + // register custom SSL Socket factory + DavGatewaySSLProtocolSocketFactory.register(); } - public static void stop() { - if (smtpServer != null) { - smtpServer.close(); - try { - smtpServer.join(); - } catch (InterruptedException e) { - DavGatewayTray.warn("Exception waiting for listener to die", e); - } - } - if (popServer != null) { - popServer.close(); - try { - popServer.join(); - } catch (InterruptedException e) { - DavGatewayTray.warn("Exception waiting for listener to die", e); - } - } - } + public static void stop() { + if (smtpServer != null) { + smtpServer.close(); + try { + smtpServer.join(); + } catch (InterruptedException e) { + DavGatewayTray.warn("Exception waiting for listener to die", e); + } + } + if (popServer != null) { + popServer.close(); + try { + popServer.join(); + } catch (InterruptedException e) { + DavGatewayTray.warn("Exception waiting for listener to die", e); + } + } + } } diff --git a/src/java/davmail/Settings.java b/src/java/davmail/Settings.java index 5ba8b43f..47dfd334 100644 --- a/src/java/davmail/Settings.java +++ b/src/java/davmail/Settings.java @@ -55,6 +55,7 @@ public class Settings { SETTINGS.put("davmail.proxyUser", ""); SETTINGS.put("davmail.proxyPassword", ""); SETTINGS.put("davmail.server", "false"); + SETTINGS.put("davmail.server.certificate.hash", ""); save(); } } catch (IOException e) { @@ -114,4 +115,11 @@ public class Settings { String propertyValue = SETTINGS.getProperty(property); return "true".equals(propertyValue); } + + public static synchronized void saveProperty(String property, String value) { + Settings.load(); + Settings.setProperty(property, value); + Settings.save(); + } + } diff --git a/src/java/davmail/SettingsFrame.java b/src/java/davmail/SettingsFrame.java index 388d9cef..89b21f97 100644 --- a/src/java/davmail/SettingsFrame.java +++ b/src/java/davmail/SettingsFrame.java @@ -9,6 +9,21 @@ import java.awt.*; * DavMail settings frame */ public class SettingsFrame extends JFrame { + protected JTextField urlField; + protected JTextField popPortField; + protected JTextField smtpPortField; + protected JTextField keepDelayField; + + JCheckBox enableProxyField; + JTextField httpProxyField; + JTextField httpProxyPortField; + JTextField httpProxyUserField; + JTextField httpProxyPasswordField; + + JCheckBox allowRemoteField; + JTextField bindAddressField; + JTextField certHashField; + protected void addSettingComponent(JPanel panel, String label, Component component) { JLabel fieldLabel = new JLabel(label); fieldLabel.setHorizontalAlignment(SwingConstants.RIGHT); @@ -16,22 +31,15 @@ public class SettingsFrame extends JFrame { panel.add(component); } - public SettingsFrame() { - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setTitle("DavMail Settings"); - - JTabbedPane tabbedPane = new JTabbedPane(); - - JPanel mainPanel = new JPanel(new GridLayout(2, 1)); - + protected JPanel getSettingsPanel() { JPanel settingsPanel = new JPanel(new GridLayout(4, 2)); settingsPanel.setBorder(BorderFactory.createTitledBorder("Gateway settings")); - final JTextField urlField = new JTextField(Settings.getProperty("davmail.url"), 15); + urlField = new JTextField(Settings.getProperty("davmail.url"), 15); urlField.setToolTipText("Base outlook web access URL"); - final JTextField popPortField = new JTextField(Settings.getProperty("davmail.popPort"), 4); - final JTextField smtpPortField = new JTextField(Settings.getProperty("davmail.smtpPort"), 4); - final JTextField keepDelayField = new JTextField(Settings.getProperty("davmail.keepDelay"), 4); + popPortField = new JTextField(Settings.getProperty("davmail.popPort"), 4); + smtpPortField = new JTextField(Settings.getProperty("davmail.smtpPort"), 4); + keepDelayField = new JTextField(Settings.getProperty("davmail.keepDelay"), 4); keepDelayField.setToolTipText("Number of days to keep messages in trash"); @@ -39,19 +47,20 @@ public class SettingsFrame extends JFrame { addSettingComponent(settingsPanel, "Local POP port: ", popPortField); addSettingComponent(settingsPanel, "Local SMTP port: ", smtpPortField); addSettingComponent(settingsPanel, "Keep Delay: ", keepDelayField); + return settingsPanel; + } - mainPanel.add(settingsPanel); - + protected JPanel getProxyPanel() { JPanel proxyPanel = new JPanel(new GridLayout(5, 2)); proxyPanel.setBorder(BorderFactory.createTitledBorder("Proxy settings")); boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy"); - final JCheckBox enableProxyField = new JCheckBox(); + enableProxyField = new JCheckBox(); enableProxyField.setSelected(enableProxy); - final JTextField httpProxyField = new JTextField(Settings.getProperty("davmail.proxyHost"), 15); - final JTextField httpProxyPortField = new JTextField(Settings.getProperty("davmail.proxyPort"), 4); - final JTextField httpProxyUserField = new JTextField(Settings.getProperty("davmail.proxyUser"), 4); - final JTextField httpProxyPasswordField = new JPasswordField(Settings.getProperty("davmail.proxyPassword"), 4); + httpProxyField = new JTextField(Settings.getProperty("davmail.proxyHost"), 15); + httpProxyPortField = new JTextField(Settings.getProperty("davmail.proxyPort"), 4); + httpProxyUserField = new JTextField(Settings.getProperty("davmail.proxyUser"), 4); + httpProxyPasswordField = new JPasswordField(Settings.getProperty("davmail.proxyPassword"), 4); httpProxyField.setEnabled(enableProxy); httpProxyPortField.setEnabled(enableProxy); @@ -73,28 +82,66 @@ public class SettingsFrame extends JFrame { addSettingComponent(proxyPanel, "Proxy port: ", httpProxyPortField); addSettingComponent(proxyPanel, "Proxy user: ", httpProxyUserField); addSettingComponent(proxyPanel, "Proxy password: ", httpProxyPasswordField); + return proxyPanel; + } - mainPanel.add(proxyPanel); + public JPanel getNetworkSettingsPanel() { + JPanel networkSettingsPanel = new JPanel(new GridLayout(3, 2)); + networkSettingsPanel.setBorder(BorderFactory.createTitledBorder("Network settings")); + + allowRemoteField = new JCheckBox(); + allowRemoteField.setSelected(Settings.getBooleanProperty("davmail.allowRemote")); + allowRemoteField.setToolTipText("Allow remote connections to the gateway (server mode)"); + + bindAddressField = new JTextField(Settings.getProperty("davmail.bindAddress"), 15); + bindAddressField.setToolTipText("Bind only to the specified network address"); + + certHashField = new JTextField(Settings.getProperty("davmail.server.certificate.hash"), 15); + certHashField.setToolTipText("Manually accepted server certificate hash"); + + addSettingComponent(networkSettingsPanel, "Bind address: ", bindAddressField); + addSettingComponent(networkSettingsPanel, "Allow Remote Connections: ", allowRemoteField); + addSettingComponent(networkSettingsPanel, "Server certificate hash: ", certHashField); + return networkSettingsPanel; + } + + public void reload() { + // reload settings in form + urlField.setText(Settings.getProperty("davmail.url")); + popPortField.setText(Settings.getProperty("davmail.popPort")); + smtpPortField.setText(Settings.getProperty("davmail.smtpPort")); + keepDelayField.setText(Settings.getProperty("davmail.keepDelay")); + allowRemoteField.setSelected(Settings.getBooleanProperty(("davmail.allowRemote"))); + bindAddressField.setText(Settings.getProperty("davmail.bindAddress")); + boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy"); + enableProxyField.setSelected(enableProxy); + httpProxyField.setEnabled(enableProxy); + httpProxyPortField.setEnabled(enableProxy); + httpProxyUserField.setEnabled(enableProxy); + httpProxyPasswordField.setEnabled(enableProxy); + httpProxyField.setText(Settings.getProperty("davmail.proxyHost")); + httpProxyPortField.setText(Settings.getProperty("davmail.proxyPort")); + httpProxyUserField.setText(Settings.getProperty("davmail.proxyUser")); + httpProxyPasswordField.setText(Settings.getProperty("davmail.proxyPassword")); + certHashField.setText(Settings.getProperty("davmail.server.certificate.hash")); + } + + public SettingsFrame() { + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setTitle("DavMail Settings"); + + JTabbedPane tabbedPane = new JTabbedPane(); + + JPanel mainPanel = new JPanel(new GridLayout(2, 1)); + mainPanel.add(getSettingsPanel()); + mainPanel.add(getProxyPanel()); tabbedPane.add("Main", mainPanel); JPanel advancedPanel = new JPanel(); advancedPanel.setLayout(new BorderLayout()); - JPanel networkSettingsPanel = new JPanel(new GridLayout(2, 2)); - networkSettingsPanel.setBorder(BorderFactory.createTitledBorder("Network settings")); - - final JCheckBox allowRemoteField = new JCheckBox(); - allowRemoteField.setSelected(Settings.getBooleanProperty("davmail.allowRemote")); - allowRemoteField.setToolTipText("Allow remote connections to the gateway (server mode)"); - - final JTextField bindAddressField = new JTextField(Settings.getProperty("davmail.bindAddress"), 15); - bindAddressField.setToolTipText("Bind only to the specified network address"); - - addSettingComponent(networkSettingsPanel, "Bind address: ", bindAddressField); - addSettingComponent(networkSettingsPanel, "Allow Remote Connections: ", allowRemoteField); - - advancedPanel.add("North", networkSettingsPanel); + advancedPanel.add("North", getNetworkSettingsPanel()); tabbedPane.add("Advanced", advancedPanel); @@ -117,8 +164,9 @@ public class SettingsFrame extends JFrame { Settings.setProperty("davmail.proxyPort", httpProxyPortField.getText()); Settings.setProperty("davmail.proxyUser", httpProxyUserField.getText()); Settings.setProperty("davmail.proxyPassword", httpProxyPasswordField.getText()); - Settings.save(); + Settings.setProperty("davmail.server.certificate.hash", certHashField.getText()); dispose(); + Settings.save(); // restart listeners with new config DavGateway.start(); } @@ -127,23 +175,7 @@ public class SettingsFrame extends JFrame { cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - // reload settings in form - urlField.setText(Settings.getProperty("davmail.url")); - popPortField.setText(Settings.getProperty("davmail.popPort")); - smtpPortField.setText(Settings.getProperty("davmail.smtpPort")); - keepDelayField.setText(Settings.getProperty("davmail.keepDelay")); - allowRemoteField.setSelected(Settings.getBooleanProperty(("davmail.allowRemote"))); - bindAddressField.setText(Settings.getProperty("davmail.bindAddress")); - boolean enableProxy = Settings.getBooleanProperty("davmail.allowRemote"); - enableProxyField.setSelected(enableProxy); - httpProxyField.setEnabled(enableProxy); - httpProxyPortField.setEnabled(enableProxy); - httpProxyUserField.setEnabled(enableProxy); - httpProxyPasswordField.setEnabled(enableProxy); - httpProxyField.setText(Settings.getProperty("davmail.proxyHost")); - httpProxyPortField.setText(Settings.getProperty("davmail.proxyPort")); - httpProxyUserField.setText(Settings.getProperty("davmail.proxyUser")); - httpProxyPasswordField.setText(Settings.getProperty("davmail.proxyPassword")); + reload(); dispose(); } }); diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 06319df9..7db1425d 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -1,7 +1,15 @@ package davmail.exchange; import davmail.Settings; -import org.apache.commons.httpclient.*; +import davmail.http.DavGatewayHttpClientFactory; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.HttpURL; +import org.apache.commons.httpclient.HttpsURL; +import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; @@ -12,13 +20,22 @@ import org.apache.webdav.lib.ResponseEntity; import org.apache.webdav.lib.WebdavResource; import javax.mail.internet.MimeUtility; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; -import java.net.UnknownHostException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.SimpleTimeZone; +import java.util.Vector; /** * Exchange session through Outlook Web Access (DAV) @@ -65,91 +82,13 @@ public class ExchangeSession { * Create an exchange session for the given URL. * The session is not actually established until a call to login() */ - public ExchangeSession() { + ExchangeSession() { // SimpleDateFormat are not thread safe, need to create one instance for // each session dateParser = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); dateParser.setTimeZone(new SimpleTimeZone(0, "GMT")); } - /** - * Update http client configuration (proxy) - * - * @param httpClient current Http client - */ - protected static void configureClient(HttpClient httpClient) { - // do not send basic auth automatically - httpClient.getState().setAuthenticationPreemptive(false); - - boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy"); - String proxyHost = null; - int proxyPort = 0; - String proxyUser = null; - String proxyPassword = null; - - if (enableProxy) { - proxyHost = Settings.getProperty("davmail.proxyHost"); - proxyPort = Settings.getIntProperty("davmail.proxyPort"); - proxyUser = Settings.getProperty("davmail.proxyUser"); - proxyPassword = Settings.getProperty("davmail.proxyPassword"); - } - - // configure proxy - if (proxyHost != null && proxyHost.length() > 0) { - httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort); - if (proxyUser != null && proxyUser.length() > 0) { - -/* // Only available in newer HttpClient releases, not compatible with slide library - List authPrefs = new ArrayList(); - authPrefs.add(AuthPolicy.BASIC); - httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,authPrefs); -*/ - // instead detect ntlm authentication (windows domain name in user name) - int backslashindex = proxyUser.indexOf("\\"); - if (backslashindex > 0) { - httpClient.getState().setProxyCredentials(null, proxyHost, - new NTCredentials(proxyUser.substring(backslashindex + 1), - proxyPassword, null, - proxyUser.substring(0, backslashindex))); - } else { - httpClient.getState().setProxyCredentials(null, proxyHost, - new UsernamePasswordCredentials(proxyUser, proxyPassword)); - } - } - } - - } - - public static void checkConfig() throws IOException { - try { - String url = Settings.getProperty("davmail.url"); - - // create an HttpClient instance - HttpClient httpClient = new HttpClient(); - configureClient(httpClient); - - // 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 && status != HttpStatus.SC_UNAUTHORIZED - && status != HttpStatus.SC_MOVED_TEMPORARILY) { - throw new IOException("Unable to connect to OWA at " + url + ", status code " + - status + ", check configuration"); - } - - } catch (UnknownHostException exc) { - LOGGER.error("DavMail configuration exception: \n Unknown host " + exc.getMessage(), exc); - throw new IOException("DavMail configuration exception: \n Unknown host " + exc.getMessage(), exc); - } catch (Exception exc) { - LOGGER.error("DavMail configuration exception: \n" + exc.getMessage(), exc); - throw new IOException("DavMail configuration exception: \n" + exc.getMessage(), exc); - } - - } - /** * Test authentication mode : form based or basic. * @@ -159,15 +98,14 @@ public class ExchangeSession { */ protected boolean isBasicAuthentication(String url) throws IOException { // create an HttpClient instance - HttpClient httpClient = new HttpClient(); - configureClient(httpClient); + HttpClient httpClient = DavGatewayHttpClientFactory.getInstance(); HttpMethod testMethod = new GetMethod(url); int status = httpClient.executeMethod(testMethod); testMethod.releaseConnection(); return status == HttpStatus.SC_UNAUTHORIZED; } - public void login(String userName, String password) throws IOException { + void login(String userName, String password) throws IOException { try { String url = Settings.getProperty("davmail.url"); @@ -194,13 +132,24 @@ public class ExchangeSession { // get the internal HttpClient instance HttpClient httpClient = wdr.retrieveSessionInstance(); - configureClient(httpClient); + DavGatewayHttpClientFactory.configureClient(httpClient); - // get webmail root url (will follow redirects) + // get webmail root url // providing credentials + // manually follow redirect HttpMethod initmethod = new GetMethod(url); - wdr.executeHttpRequestMethod(httpClient, - initmethod); + initmethod.setFollowRedirects(false); + httpClient.executeMethod(initmethod); + int redirectCount = 0; + while (redirectCount++ < 10 && initmethod.getStatusCode() == 302) { + initmethod.releaseConnection(); + initmethod = new GetMethod(initmethod.getResponseHeader("Location").getValue()); + initmethod.setFollowRedirects(false); + httpClient.executeMethod(initmethod); + } + if (initmethod.getStatusCode() == 302) { + throw new IOException("Maximum redirections reached"); + } // default destination is provided url String destination = url; @@ -216,12 +165,12 @@ public class ExchangeSession { loginFormReader = new BufferedReader(new InputStreamReader(initmethod.getResponseBodyAsStream())); String line; // skip to form action - final String FORM_ACTION = "