Codebase list davmail / 2fe8605
New upstream version 5.1.0.2891 Alexandre Rossi 5 years ago
36 changed file(s) with 1172 addition(s) and 533 deletion(s). Raw diff Collapse all Expand all
0 # DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange Gateway
0 # DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange and Office 365 Gateway
11
22 [![Build Status: Linux](https://travis-ci.org/mguessan/davmail.svg?branch=master)](https://travis-ci.org/mguessan/davmail)
33 [![Build status: Windows](https://ci.appveyor.com/api/projects/status/d7tx645gwqvprd4g?svg=true)](https://ci.appveyor.com/project/mguessan/davmail)
1313
1414 ## Download
1515 Download latest DavMail release on Sourceforge
16 [http://davmail.sourceforge.net/download.html](http://davmail.sourceforge.net/download.html)
16 [https://sourceforge.net/projects/davmail/files/davmail/5.1.0/](https://sourceforge.net/projects/davmail/files/davmail/5.1.0/)
1717
1818 ## Trunk builds
1919 Latest working builds are now available on Appveyor:
2020
21 * Windows setup [davmail-5.0.0-trunk-setup.exe](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.0.0-trunk-setup.exe?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
22 * Windows 64 bits setup [davmail-5.0.0-trunk-setup64.exe](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.0.0-trunk-setup64.exe?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
23 * Windows noinstall package [davmail-5.0.0-trunk-windows-noinstall.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.0.0-trunk-windows-noinstall.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
21 * Windows setup [davmail-5.1.0-trunk-setup.exe](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.1.0-trunk-setup.exe?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
22 * Windows 64 bits setup [davmail-5.1.0-trunk-setup64.exe](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.1.0-trunk-setup64.exe?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
23 * Windows noinstall package [davmail-5.1.0-trunk-windows-noinstall.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.1.0-trunk-windows-noinstall.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
2424
25 * Platform independent package [davmail-5.0.0-trunk.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.0.0-trunk.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
25 * Platform independent package [davmail-5.1.0-trunk.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail-5.1.0-trunk.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
2626
27 * Debian package [davmail_5.0.0-trunk-1_all.deb](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail_5.0.0-trunk-1_all.deb?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
27 * Debian package [davmail_5.1.0-trunk-1_all.deb](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2Fdavmail_5.1.0-trunk-1_all.deb?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
2828
29 * OSX application [DavMail-MacOSX-5.0.0-trunk.app.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2FDavMail-MacOSX-5.0.0-trunk.app.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
29 * OSX application [DavMail-MacOSX-5.1.0-trunk.app.zip](https://ci.appveyor.com/api/projects/mguessan/davmail/artifacts/dist%2FDavMail-MacOSX-5.1.0-trunk.app.zip?job=Environment%3A%20JAVA_HOME%3DC%3A%5CProgram%20Files%5CJava%5Cjdk1.8.0)
3030
3131 ## Contribute
3232 Contributions are welcome, you can either [submit a patch](https://sourceforge.net/p/davmail/patches/) or create a [Github pull request](https://github.com/mguessan/davmail/pulls).
0 ## DavMail 5.1.0 2018-12-18
1 Much improved interactive O365 authentication with OpenJFX bug workaround,
2 experimental stored Oauth refresh token support. More Linux distributions were tested
3 to make DavMail work with recent KDE and Gnome environments.
4
5 ### EWS:
6 - EWS: more progress on ADFS authentication
7 - EWS: cleanup warning message
8 - EWS: experimental, implement davmail.oauth.persistToken to store Oauth refresh token
9 - EWS: make progress on O365 ADFS authentication, fix method
10 - EWS: stored token authentication, load token by username
11 - EWS: make progress on O365 ADFS authentication, enable NTLM and pass credentials
12 - EWS: O365 authentication, set resource url on token refresh
13 - EWS: set default access token expiration
14 - EWS: implement stored access token in addition to refresh token (will only last one hour)
15 - EWS: experimental, load Oauth refresh token from setting davmail.oauth.refreshToken
16 - EWS: fix https://github.com/mguessan/davmail/issues/15 empty domain in NTLM authentication
17 - EWS: revert to 4.9.0 behavior for EWS mode
18 - EWS: fix regression in token handling
19 - EWS: allow urn protocol in O365 authenticator
20 - EWS: fix regression when main authentication relies on OWA and/or ADFS
21 - EWS: check for errors in returned json token
22 - EWS: Fix warning message
23 - EWS: workaround for JavaFX bug, add one more URL
24 - EWS: call setAlwaysOnTop(true); on page load success
25 - EWS: in addition to requestFocus, call toFront
26 - EWS: workaround for JavaFX bug, handle more methods in connection wrapper
27 - EWS: workaround for JavaFX bug, handle post requests
28 - EWS: workaround for JavaFX bug, add additional microsoft url
29 - EWS: workaround for JavaFX bug, fix java 8 regression
30 - EWS: Add export compiler arg java.base/sun.net.www.protocol.https for webview bug workaround
31 - EWS: improve interactive authenticator focus handling and remove reflection calls
32 - EWS: workaround for JavaFX bug, use reflection to avoid java 9 errors
33 - EWS: workaround for JavaFX bug, drop reference to internal sun class HttpsURLConnectionImpl
34 - EWS: workaround for JavaFX bug, disable integrity check on external resources in O365 authentication form
35 - EWS: javafx test can also trigger NoClassDefFoundError
36 - EWS: Rename JSLogger
37 - EWS: improve O365 interactive error handling
38 - EWS: override console.log to send error messages to Log4J
39 - EWS: More EWS test cases
40 - EWS: new authenticator test cases
41 - EWS: detect when user settings validation is required by Office 365
42 - EWS: detect manual window close event
43 - EWS: Make sure we close frame on timeout, improve error message
44 - EWS: refactor O365 authenticator to do all gui calls in Swing thread
45 - EWS: cleanup from audit
46 - EWS: encode slash inside folder names
47 - EWS: convert date without SimpleDateFormat during load messages to improve performance and reduce memory footprint
48 - EWS: Send authentication failed instead of generic error in case of username mismatch in O365Authenticator
49
50 ### Documentation:
51 - Doc: update project description in README.md
52 - Doc: update project description
53 - Doc: adjust IntelliJ link according to JetBrains recommendation
54 - Doc: revert openhub change, was a target side issue
55 - Doc: add YourKit Java Profiler logo to home page
56 - Doc: improve IntelliJ IDEA home page logo
57 - Doc: fix swt gtk version in documentation
58 - Doc: fix openhub link
59 - Doc: add link to https://apps.dev.microsoft.com/
60 - Doc: direct link to latest release package download list in README.md
61 - Doc: fix link in server setup documentation
62
63 ### OSX:
64 - OSX: upgrade universalJavaApplicationStub to 3.0.4
65
66 ### Linux:
67 - Linux: prepare systemd service
68 - Linux: missing openjfx dependency
69 - Linux: set cross platform look and feel on Linux, except is swing.defaultlaf is set
70 - Linux: enable anti aliasing in GUI
71 - Linux: improve launch scripts to handle more cases (OpenJDK 11 with or without SWT)
72 - Linux: remove swt4 suggests and revert gtk force, does not work under debian sid
73 - Linux: Force gtk version no longer required with cross platform look and feel
74 - Linux: use hi res icon images in frame mode
75 - Linux: Add JavaFX classpath to launch script
76 - Linux: add libopenjfx-java dependency to debian package
77 - Linux: switch swt dependency to suggests
78
79 ### Caldav:
80 - Caldav: another NullPointerException fix
81 - Caldav: fix #694 Null pointer exception writing days of week
82
83 ### Enhancements:
84 - Avoid nullpointerexception on missing credentials
85 - Move isLinux method to Settings
86 - Revert back to Java 6 build in all cases
87 - Restore Java 6 compatibility
88 - Add utility methods in Settings
89 - Do not try SWT when O365 interactive mode is selected.
90
91 ### Windows:
92 - Windows: update winrun4j 64 wrapper to support java > 8, see https://github.com/poidasmith/winrun4j/pull/81
93
94 ### GUI:
95 - GUI: dispose notification dialog on close
96 - GUI: increase default frame size
97 - GUI: add hi res icon images
98 - GUI: use setLocationRelativeTo to set frame location
99
100 ### SWT:
101 - SWT: O365Interactive is not compatible with SWT, do not try to create SWT tray
102 - SWT: call GDK.gdk_error_trap_push() to avoid crash
103 - SWT: Enable debug mode
104 - SWT: upgrade SWT to 4.9
105 - SWT: drop deprecated SWT 3 calls and adjust tray icon image to 22px
106
107 ### Appveyor:
108 - Appveyor: build with JDK11
109 - Merge patch #51 Check for javafx in compile classpath
110
111
0112 ## DavMail 5.0.0 2018-11-21
1113 Major release with Office 365 modern authentication (Oauth2) and MFA support.
2114 DavMail now supports IMAP SPECIAL-USE RFC6154.
66 - JAVA_HOME: C:\Program Files\Java\jdk9
77 - JAVA_HOME: C:\Program Files\Java\jdk1.8.0
88 - JAVA_HOME: C:\Program Files\Java\jdk10
9 - JAVA_HOME: C:\Program Files\Java\jdk11
910
1011 install:
1112 - ps: |
00 <project name="DavMail" default="dist" basedir=".">
11 <property file="user.properties"/>
2 <property name="version" value="5.0.0"/>
2 <property name="version" value="5.1.0"/>
33
44 <path id="classpath">
55 <pathelement location="classes"/>
2929 </condition>
3030
3131 <condition property="is.javafx">
32 <available classname="javafx.embed.swing.JFXPanel"/>
32 <available classname="javafx.embed.swing.JFXPanel" classpathref="classpath"/>
3333 </condition>
3434
3535 <condition property="is.javafx.message" value="available" else="missing">
36 <available classname="javafx.embed.swing.JFXPanel" property="is.javafx"/>
36 <available classname="javafx.embed.swing.JFXPanel" property="is.javafx" classpathref="classpath"/>
3737 </condition>
3838
3939 <condition property="is.java6">
8787 <mkdir dir="target/classes"/>
8888 </target>
8989
90 <target name="compile-java9" depends="init" if="is.java9">
90 <target name="compile-java9" depends="init">
9191 <mkdir dir="target/classes"/>
9292 <javac srcdir="src/java" destdir="target/classes" source="9" debug="on" encoding="UTF-8"
9393 includeantruntime="false">
9494 <exclude name="davmail/exchange/auth/*Interactive*" unless="is.javafx"/>
9595 <compilerarg value="--add-exports" />
9696 <compilerarg value="java.naming/com.sun.jndi.ldap=ALL-UNNAMED" />
97 <compilerarg value="--add-exports" />
98 <compilerarg value="java.base/sun.net.www.protocol.https=ALL-UNNAMED" />
9799 <classpath>
98100 <path refid="classpath"/>
99101 </classpath>
100102 </javac>
101103 </target>
102104
103 <target name="compile-java" depends="init" unless="is.java9">
105 <target name="compile-java" depends="init">
104106 <mkdir dir="target/classes"/>
105107 <javac srcdir="src/java" destdir="target/classes" source="1.6" target="1.6" debug="on" encoding="UTF-8"
106108 includeantruntime="false">
111113 </javac>
112114 </target>
113115
114 <target name="compile" depends="compile-java,compile-java9">
116 <target name="compile" depends="compile-java">
115117 <copy todir="target/classes">
116118 <fileset dir="src/java">
117119 <include name="**/*"/>
208210 <deb todir="dist"
209211 package="davmail"
210212 section="mail"
211 depends="openjdk-11-jre|openjdk-8-jre|openjdk-7-jre|openjdk-6-jre|oracle-java7-jre|sun-java6-jre|default-jre,libswt-gtk-3-java|libswt-gtk-3.6-java|libswt-gtk-3.5-java|libswt-gtk-3.4-java,libswt-cairo-gtk-3-jni|libswt-cairo-gtk-3.5-jni">
213 depends="openjdk-11-jre|openjdk-8-jre|openjdk-7-jre|openjdk-6-jre|oracle-java7-jre|sun-java6-jre|default-jre,libopenjfx-java,openjfx"
214 suggests="libswt-gtk-4-java,libswt-cairo-gtk-4-jni,libswt-gtk2-4-jni">
212215 <version upstream="${release-name}"/>
213216 <maintainer email="mguessan@free.fr" name="Mickaël Guessant"/>
214217 <description synopsis="DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange Gateway">
0 %{?!davrel: %define davrel 5.0.0}
1 %{?!davsvn: %define davsvn 2801}
0 %{?!davrel: %define davrel 5.1.0}
1 %{?!davsvn: %define davsvn 2890}
22 %define davver %{davrel}-%{davsvn}
33
44 Summary: DavMail is a POP/IMAP/SMTP/Caldav/Carddav/LDAP gateway for Microsoft Exchange
186186 %attr(0775,davmail,davmail) %{_localstatedir}/lib/davmail
187187
188188 %changelog
189 * Thu Dec 20 2018 Mickael Guessant <mguessan@free.fr>
190 - update to 5.1.0
191
189192 * Wed Nov 21 2018 Mickael Guessant <mguessan@free.fr>
190193 - update to 5.0.0
191194 - merge files in trunk
44 <groupId>davmail</groupId>
55 <artifactId>davmail</artifactId>
66 <packaging>jar</packaging>
7 <version>5.0.0</version>
7 <version>5.1.0</version>
88 <name>DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange Gateway</name>
99 <organization>
1010 <name>Mickaël Guessant</name>
231231 <version>4.6.3</version>
232232 <optional>true</optional>
233233 <scope>system</scope>
234 <systemPath>${project.basedir}/lib/swt-4.6.3-gtk-linux-x86_64.jar</systemPath>
234 <systemPath>${project.basedir}/lib/swt-4.9-gtk-linux-x86_64.jar</systemPath>
235235 </dependency>
236236 <dependency>
237237 <groupId>javax.servlet</groupId>
5858 <content_attribute id="money-gambling">none</content_attribute>
5959 </content_rating>
6060 <releases>
61 <release version="5.0.0" date="2018-11-21">
61 <release version="5.1.0" date="2018-12-18">
6262 <description>
6363 <p>
64 Major release with Office 365 modern authentication (Oauth2) and MFA support.
65 DavMail now supports IMAP SPECIAL-USE RFC6154.
66 On the packaging side, RPM files are now included in source package and more
67 distributions are supported by the spec file. An appveyor configuration is in place to
68 provide up to date trunk builds. Thanks to wberrier DavMail is now available as a
69 flatpack package, see https://flathub.org/apps/details/org.davmail.DavMail
70 This release also includes many bug fixes and enhancements, see below.
64 Much improved interactive O365 authentication with OpenJFX bug workaround,
65 experimental stored Oauth refresh token support. More Linux distributions were tested
66 to make DavMail work with recent KDE and Gnome environments.
7167
72 Known issues/limitations:
73 - Office 365 interactive authentication is based on OpenJFX (JavaFX),
74 which is available in Oracle JDK but not in OpenJDK. On windows use latest Oracle JDK (>=9),
75 on Linux OpenJDK 8 + JavaFX is the best option. This is obviously not available in server mode.
76 - Office 365 modern authentication does not have those constraints, however it will only work
77 with native Office 365 authentication, and not with ADFS.
68 ### EWS:
69 - EWS: more progress on ADFS authentication
70 - EWS: cleanup warning message
71 - EWS: experimental, implement davmail.oauth.persistToken to store Oauth refresh token
72 - EWS: make progress on O365 ADFS authentication, fix method
73 - EWS: stored token authentication, load token by username
74 - EWS: make progress on O365 ADFS authentication, enable NTLM and pass credentials
75 - EWS: O365 authentication, set resource url on token refresh
76 - EWS: set default access token expiration
77 - EWS: implement stored access token in addition to refresh token (will only last one hour)
78 - EWS: experimental, load Oauth refresh token from setting davmail.oauth.refreshToken
79 - EWS: fix https://github.com/mguessan/davmail/issues/15 empty domain in NTLM authentication
80 - EWS: revert to 4.9.0 behavior for EWS mode
81 - EWS: fix regression in token handling
82 - EWS: allow urn protocol in O365 authenticator
83 - EWS: fix regression when main authentication relies on OWA and/or ADFS
84 - EWS: check for errors in returned json token
85 - EWS: Fix warning message
86 - EWS: workaround for JavaFX bug, add one more URL
87 - EWS: call setAlwaysOnTop(true); on page load success
88 - EWS: in addition to requestFocus, call toFront
89 - EWS: workaround for JavaFX bug, handle more methods in connection wrapper
90 - EWS: workaround for JavaFX bug, handle post requests
91 - EWS: workaround for JavaFX bug, add additional microsoft url
92 - EWS: workaround for JavaFX bug, fix java 8 regression
93 - EWS: Add export compiler arg java.base/sun.net.www.protocol.https for webview bug workaround
94 - EWS: improve interactive authenticator focus handling and remove reflection calls
95 - EWS: workaround for JavaFX bug, use reflection to avoid java 9 errors
96 - EWS: workaround for JavaFX bug, drop reference to internal sun class HttpsURLConnectionImpl
97 - EWS: workaround for JavaFX bug, disable integrity check on external resources in O365 authentication form
98 - EWS: javafx test can also trigger NoClassDefFoundError
99 - EWS: Rename JSLogger
100 - EWS: improve O365 interactive error handling
101 - EWS: override console.log to send error messages to Log4J
102 - EWS: More EWS test cases
103 - EWS: new authenticator test cases
104 - EWS: detect when user settings validation is required by Office 365
105 - EWS: detect manual window close event
106 - EWS: Make sure we close frame on timeout, improve error message
107 - EWS: refactor O365 authenticator to do all gui calls in Swing thread
108 - EWS: cleanup from audit
109 - EWS: encode slash inside folder names
110 - EWS: convert date without SimpleDateFormat during load messages to improve performance and reduce memory footprint
111 - EWS: Send authentication failed instead of generic error in case of username mismatch in O365Authenticator
78112
79 ### EWS
80 - EWS: catch errors in setURLStreamHandlerFactory
81 - EWS: custom proxy selector, do not return proxies for direct socket connections
82 - EWS: create a custom proxy selector to manage O365 interactive authentication proxy
83 - EWS: improve error handling in O365 interactive authenticator, do not implicitly close JavaFX thread
84 - EWS: cleanup O365 interactive
85 - EWS: Set http.nonProxyHosts from davmail.noProxyFor in O365 interactive authentication
86 - EWS: improve error handling in O365 interactive authentication
87 - EWS: implement proxy support in O365 interactive authentication
88 - EWS: username with @ is email
89 - Do not try form authentication with direct EWS
90 - EWS: Force dispose of interactive frame
91 - EWS: improve interactive authentication error handling
92 - Fix main test case to support new authentication modes
93 - EWS: Enable DavMail custom SSLSocketFactory in O365 interactive authentication
94 - EWS: Add Oauth authentication section in DavMail settings interface
95 - EWS: Experimental ADFS authentication, not yet functional
96 - EWS: log page content on error in O365Authenticator
97 - EWS: register a stream handler for msauth protocol
98 - EWS: Allow clientId override in interactive authenticator
99 - EWS: Send authentication failed on phone MFA denied/no response
100 - EWS: enable progress bar on first page load only
101 - EWS: Office 365 unit test with loop
102 - EWS: make sure httpclient connections are closed, remove duplicated code
103 - EWS: use renewable token in EwsExchangeSession
104 - EWS: refactor O365 authentication to implement token refresh
105 - EWS: improve headless Office 365 authenticator error handling
106 - EWS: implement progress bar in interactive authentication frame
107 - EWS: check username in Office365 interactive authenticator
108 - EWS: Encode username in Office365 authenticator
109 - EWS: exclude JavaFX authenticator from Maven pom
110 - EWS: Remove reference to JavaFX authenticator in ExchangeSessionFactory
111 - EWS: Reorganise authenticators
112 - JavaFX dependencies are Java 11 only, revert and exclude JavaFX authenticator from Maven build
113 - Add JavaFX scene web dependency
114 - Add JavaFX swing dependency in POM
115 - EWS: add jettison dependency in pom
116 - Do not try interactive authentication in server mode
117 - EWS: Merge non interactive Oauth2 authentication
118 - EWS: Office365 modern authentication (Oauth2) with phone application MFA support
119 - EWS: Implement REST/Json method for Oauth authentication
120 - EWS: send username to interactive authentication frame
121 - EWS: implement interactive OAuth2 authentication (still experimental)
122 - EWS: Add jettison library for Oauth support
123 - EWS: First working prototype of interactive Oauth2 authentication
113 ### Documentation:
114 - Doc: update project description in README.md
115 - Doc: update project description
116 - Doc: adjust IntelliJ link according to JetBrains recommendation
117 - Doc: revert openhub change, was a target side issue
118 - Doc: add YourKit Java Profiler logo to home page
119 - Doc: improve IntelliJ IDEA home page logo
120 - Doc: fix swt gtk version in documentation
121 - Doc: fix openhub link
122 - Doc: add link to https://apps.dev.microsoft.com/
123 - Doc: direct link to latest release package download list in README.md
124 - Doc: fix link in server setup documentation
124125
125 ### Enhancements
126 - Improve SWT not available message
127 - Detect headless to force server mode, do not allow O365 interactive authentication in this case
128 - Javafx cleanup
129 - Fix empty setting handling: return default setting on empty value
130 - Implement headless choose client certificate and PKCS11 password prompt
131 - Package hi res image
132 - Merge #50 Assume notray in server mode
133 - Display connection mode help as a tooltip
134 - Merge DesktopBrowser: add support for xdg-open directly, see https://github.com/mguessan/davmail/pull/5
135 - Workaround for login.microsoftonline.com cookie domain
136 - i18n new davmail.mode setting
137 - Drop davmail.enableEws to create a new davmail.mode setting that can be EWS, WebDav, O365, O365Modern, O365Interactive or Auto
138 - Another JavaFX message fix
139 - Fix is.javafx default value
140 - Default is.javafx value
141 - Improve version check message
142 - Add a JavaFX check message
143 - Drop JavaFX runtime and use conditional build instead
144 - Add JavaFX runtime as a compile time dependency
145 - Remove last jsmooth dependency
146 - Adjust default davmail.properties for server mode usage
147 - Drop jarbundler
148 - Add jettison dependency to windows wrappers and installers
149 - Fix RFE #101: Add a new davmail.userAgent setting to let users force DavMail user agent
150 - Add oraclejdk11 and openjdk11 to Travis CI targets
151 - Try to add Javafx dependency for OpenJDK 11
152 - Make message info level in ant build
126 ### OSX:
127 - OSX: upgrade universalJavaApplicationStub to 3.0.4
153128
154 ### Linux
155 - Linux: Move spec file to root
156 - Fix relative path in launch script
157 - Copy davmail launch script to dist
158 - Linux: Drop old davmail.sh script
159 - Linux: merge external source files in main source tree
160 - Linux: Move init files from contribs to src/init
161 - Linux: compile with JavaFX on Fedora
162 - Linux: force Java 7 on RHEL 6 and do not deploy appstream on openSUSE_Leap_42.3
163 - Linux: drop reference to old architecture specific package
164 - Remove old hardcoded uids reference
165 - Linux: drop dependency to LSB functions in init script
166 - Linux: merge pull request https://github.com/mguessan/davmail/pull/4 include appdata file in rpm and deb packages
167 - Linux: merge davmail.sh to use a single script in all cases
168 - Linux: improve wrapper according to audit
169 - Linux: adjust desktop categories according to OpenSuse constraints, see https://en.opensuse.org/openSUSE:Packaging_desktop_menu_categories
170 - Linux: Simplify DavMail wrapper
171 - Linux: make spec file compatible with more distributions
172 - Linux: Additional notes on running DavMail with systray on Ubuntu 18
173 - Linux: merge RPM and Debian desktop files
174 - Linux: use simple name instead of path in desktop file
175 - Linux: drop desktopentry ant task
176 - Linux: move old desktop file to src/desktop
177 - Linux: Prepare desktop file merge
178 - Linux: merge pull request https://github.com/mguessan/davmail/pull/2 remove deprecations and duplicate main categories in desktop file, missing lf
179 - Linux: merge pull request https://github.com/mguessan/davmail/pull/2 remove deprecations and duplicate main categories in desktop file
180 - Linux: Add changelog entry for release in spec file
181 - Linux: fix spec file changelog date
182 - RPM: update init and logrotate from build.opensuse.org
129 ### Linux:
130 - Linux: prepare systemd service
131 - Linux: missing openjfx dependency
132 - Linux: set cross platform look and feel on Linux, except is swing.defaultlaf is set
133 - Linux: enable anti aliasing in GUI
134 - Linux: improve launch scripts to handle more cases (OpenJDK 11 with or without SWT)
135 - Linux: remove swt4 suggests and revert gtk force, does not work under debian sid
136 - Linux: Force gtk version no longer required with cross platform look and feel
137 - Linux: use hi res icon images in frame mode
138 - Linux: Add JavaFX classpath to launch script
139 - Linux: add libopenjfx-java dependency to debian package
140 - Linux: switch swt dependency to suggests
183141
184 ### Caldav
185 - Caldav: another fix for #344 Problem with Calendar and tasks, fix properties list
186 - Caldav: fix for #344 Problem with Calendar and tasks, calendar:MyResponseType is also calendar only on Exchange 2007
187 - Caldav: fix for #344 Problem with Calendar and tasks, Exchange 2007 does not accept ismeeting property request on non calendar items
142 ### Caldav:
143 - Caldav: another NullPointerException fix
144 - Caldav: fix #694 Null pointer exception writing days of week
188145
189 ### Documentation
190 - Doc: Convert release notes to markdown format
191 - Doc: add contribute section in README.md
192 - Doc: fix appveyor link
193 - Add download links to README.md
194 - Doc: fix typo
195 - Doc: update linux instructions, remove obsolete content
196 - Doc: reference official debian package and build.opensuse RPM packages in server setup documentation
197 - Doc: Drop piwik reference from site, no longer available on Sourceforge
198 - Doc: Add appveyor badge in README.md
199 - Doc: Add an FAQ entry on Office 365 modern authentication and MFA
200 - Doc: adjust indentation to match pull request
201 - Doc: appdata file from https://github.com/mguessan/davmail/pull/3
202 - Doc: make image link relative in README.md
203 - Doc: update release notes
204 - Add Sourceforge download badge to README.md
146 ### Enhancements:
147 - Avoid nullpointerexception on missing credentials
148 - Move isLinux method to Settings
149 - Revert back to Java 6 build in all cases
150 - Restore Java 6 compatibility
151 - Add utility methods in Settings
152 - Do not try SWT when O365 interactive mode is selected.
205153
206 ### IMAP
207 - IMAP: implement #341 imap SPECIAL-USE
154 ### Windows:
155 - Windows: update winrun4j 64 wrapper to support java > 8, see https://github.com/poidasmith/winrun4j/pull/81
208156
209 ### Appveyor
210 - appveyor: add Java 10 in matrix
211 - Build: use -trunk suffix for all artifacts
212 - appveyor: get artifacts
213 - Back to ant dist
214 - appveyor: fix for nsis 3
215 - appveyor: separate makensis from build file
216 - appveyor: copy processwork nsis plugin
217 - appveyor: switch from compile to dist target
218 - appveyor: disable test
219 - appveyor: fix ANT_HOME
220 - appveyor: debug
221 - appveyor: fix ant path
222 - Try to create an appveyor build descriptor
157 ### GUI:
158 - GUI: dispose notification dialog on close
159 - GUI: increase default frame size
160 - GUI: add hi res icon images
161 - GUI: use setLocationRelativeTo to set frame location
223162
224 ### Carddav
225 - Carddav: prefer urlcompname (client provided item name) for contacts over EWS
163 ### SWT:
164 - SWT: O365Interactive is not compatible with SWT, do not try to create SWT tray
165 - SWT: call GDK.gdk_error_trap_push() to avoid crash
166 - SWT: Enable debug mode
167 - SWT: upgrade SWT to 4.9
168 - SWT: drop deprecated SWT 3 calls and adjust tray icon image to 22px
226169
227 ### OSX
228 - OSX: restore OSX greyscale icons
170 ### Appveyor:
171 - Appveyor: build with JDK11
172 - Merge patch #51 Check for javafx in compile classpath
229173 </p>
230174 </description>
231175 </release>
11 #
22 # Usage: davmail [</path/to/davmail.properties>]
33 #
4 # In case of SWT crash under JDK 9, uninstall SWT or remove the second case below
4 # In case of SWT crash under JDK 9/11, uninstall SWT or remove SWT cases below
55 #
66 BASE=`dirname $0`
7 JAVA_OPTS="-Xmx512M -Dsun.net.inetaddr.ttl=60"
7 # force GTK2 to avoid crash with OpenJDK 11
8 JAVA_OPTS="-Xmx512M -Dsun.net.inetaddr.ttl=60 -Djdk.gtk.version=2.2"
9 JAVA=java
10 # uncomment this to force JDK 8
11 #JAVA=/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
12 # add JFX to classpath with OpenJDK 11
13 JFX_CLASSPATH=/usr/share/java/javafx-base.jar:/usr/share/java/javafx-controls.jar:/usr/share/java/javafx-fxml.jar:/usr/share/java/javafx-graphics.jar:/usr/share/java/javafx-media.jar:/usr/share/java/javafx-swing.jar:/usr/share/java/javafx-web.jar
814 if [ -f $BASE/davmail.jar ]; then
915 # this is the platform independent package
10 exec java $JAVA_OPTS -cp $BASE/davmail.jar:$BASE/lib/* davmail.DavGateway "$@"
16 exec ${JAVA} $JAVA_OPTS -cp $BASE/davmail.jar:$BASE/lib/*:${JFX_CLASSPATH} davmail.DavGateway "$@"
1117 elif [ -f /usr/share/java/swt.jar ]; then
12 # standard install with SWT
18 # SWT 3 is available
1319 export LD_LIBRARY_PATH=/usr/lib/jni
14 exec java $JAVA_OPTS -cp /usr/share/davmail/davmail.jar:/usr/share/java/swt.jar:/usr/share/davmail/lib/* davmail.DavGateway "$@"
20 exec ${JAVA} $JAVA_OPTS -cp /usr/share/davmail/davmail.jar:/usr/share/java/swt.jar:/usr/share/davmail/lib/* davmail.DavGateway "$@"
21 elif [ -f /usr/share/java/swt4.jar ]; then
22 # SWT 4 is available, force GTK 2 in SWT
23 export LD_LIBRARY_PATH=/usr/lib/jni
24 export SWT_GTK3=0
25 exec ${JAVA} $JAVA_OPTS -cp /usr/share/davmail/davmail.jar:/usr/share/java/swt4.jar:${JFX_CLASSPATH}:/usr/share/davmail/lib/* davmail.DavGateway "$@"
1526 else
16 exec java $JAVA_OPTS -cp /usr/share/davmail/davmail.jar:/usr/share/davmail/lib/* davmail.DavGateway "$@"
27 exec ${JAVA} $JAVA_OPTS -cp /usr/share/davmail/davmail.jar:/usr/share/davmail/lib/*:${JFX_CLASSPATH} davmail.DavGateway "$@"
1728 fi
0 [Unit]
1 Description=Davmail Exchange gateway
2 Documentation=http://davmail.sourceforge.net/serversetup.html
3 Documentation=http://davmail.sourceforge.net/advanced.html
4 Documentation=http://davmail.sourceforge.net/sslsetup.html
5 After=network.target
6
7 [Service]
8 Type=simple
9 User=davmail
10 PermissionsStartOnly=true
11 ExecStartPre=/usr/bin/touch /var/log/davmail.log
12 ExecStartPre=/bin/chown davmail:adm /var/log/davmail.log
13 ExecStart=/usr/bin/davmail /etc/davmail.properties
14 SuccessExitStatus=143
15
16 [Install]
17 WantedBy=multi-user.target
539539 Settings.save();
540540 }
541541
542 /**
543 * Test if running on Windows
544 *
545 * @return true on Windows
546 */
547 public static boolean isWindows() {
548 return System.getProperty("os.name").toLowerCase().startsWith("windows");
549 }
550
551 /**
552 * Test if running on Linux
553 *
554 * @return true on Linux
555 */
556 public static boolean isLinux() {
557 return System.getProperty("os.name").toLowerCase().startsWith("linux");
558 }
559
560 public static boolean isJava8() {
561 return "1.8".equals(System.getProperty("java.specification.version"));
562 }
542563 }
173173 // TODO: refactor buildSessionInfo
174174 session.buildSessionInfo(null);
175175
176 } else if (Settings.EWS.equals(mode) || poolKey.url.toLowerCase().endsWith("/ews/exchange.asmx")) {
177 HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(poolKey.url);
178 DavGatewayHttpClientFacade.setCredentials(httpClient, poolKey.userName, poolKey.password);
179 DavGatewayHttpClientFacade.addNTLM(httpClient);
180 session = new EwsExchangeSession(httpClient, poolKey.userName);
181 session.buildSessionInfo(null);
176 } else if (Settings.EWS.equals(mode)) {
177 if (poolKey.url.toLowerCase().endsWith("/ews/exchange.asmx")) {
178 ExchangeSession.LOGGER.debug("Direct EWS authentication");
179 HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(poolKey.url);
180 DavGatewayHttpClientFacade.setCredentials(httpClient, poolKey.userName, poolKey.password);
181 DavGatewayHttpClientFacade.addNTLM(httpClient);
182 session = new EwsExchangeSession(httpClient, poolKey.userName);
183 session.buildSessionInfo(null);
184 } else {
185 ExchangeSession.LOGGER.debug("OWA authentication in EWS mode");
186 session = new EwsExchangeSession(poolKey.url, poolKey.userName, poolKey.password);
187 }
182188 } else {
183189 try {
184190 session = new DavExchangeSession(poolKey.url, poolKey.userName, poolKey.password);
133133 String code;
134134 if (federationRedirectUrl != null && !federationRedirectUrl.isEmpty()) {
135135 LOGGER.debug("Detected ADFS, redirecting to " + federationRedirectUrl);
136 code = authenticateADFS(httpClient, federationRedirectUrl);
136 code = authenticateADFS(httpClient, federationRedirectUrl, url);
137137 } else {
138138 PostMethod logonMethod = new PostMethod("https://login.microsoftonline.com/common/login");
139139 logonMethod.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
161161 if (locationHeader == null || !locationHeader.getValue().startsWith(redirectUri)) {
162162 // extract response
163163 config = extractConfig(logonMethod.getResponseBodyAsString());
164 if (config.optJSONArray("arrScopes") != null) {
165 LOGGER.debug("Authentication successful but user consent needed, please open the following url in a browser");
164 if (config.optJSONArray("arrScopes") != null || config.optJSONArray("urlPostRedirect") != null) {
165 LOGGER.debug("Authentication successful but user consent or validation needed, please open the following url in a browser");
166166 LOGGER.debug(url);
167167 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
168168 } else if ("50126".equals(config.optString("sErrorCode"))) {
198198
199199 }
200200
201 private String authenticateADFS(HttpClient httpClient, String federationRedirectUrl) throws IOException {
201 private String authenticateADFS(HttpClient httpClient, String federationRedirectUrl, String authorizeUrl) throws IOException {
202 // set NTLM credentials
203 DavGatewayHttpClientFacade.setCredentials(httpClient, userid, password);
204 DavGatewayHttpClientFacade.addNTLM(httpClient);
202205 String responseBodyAsString;
203206
204207 // get ADFS login form
211214 logonFormMethod.releaseConnection();
212215 }
213216
214 // parse form to get target url, authenticate as userid
215 PostMethod logonMethod = new PostMethod(extract("method=\"post\" action=\"([^\"]+)\"", responseBodyAsString));
216 logonMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
217
218 logonMethod.setParameter("UserName", userid);
219 logonMethod.setParameter("Password", password);
220 logonMethod.setParameter("AuthMethod", "FormsAuthentication");
221
222 try {
223 httpClient.executeMethod(logonMethod);
224 } finally {
225 logonMethod.releaseConnection();
226 }
227
228 if (logonMethod.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY || logonMethod.getResponseHeader("Location") == null) {
229 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
230 }
231
232 GetMethod redirectMethod = new GetMethod(logonMethod.getResponseHeader("Location").getValue());
233 try {
234 httpClient.executeMethod(redirectMethod);
235 responseBodyAsString = redirectMethod.getResponseBodyAsString();
236 } finally {
237 redirectMethod.releaseConnection();
217 String location;
218
219 if (responseBodyAsString.contains("login.microsoftonline.com")) {
220 LOGGER.info("Already authenticated through Basic or NTLM");
221 } else {
222 // parse form to get target url, authenticate as userid
223 PostMethod logonMethod = new PostMethod(extract("method=\"post\" action=\"([^\"]+)\"", responseBodyAsString));
224 logonMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
225
226 logonMethod.setParameter("UserName", userid);
227 logonMethod.setParameter("Password", password);
228 logonMethod.setParameter("AuthMethod", "FormsAuthentication");
229
230 try {
231 httpClient.executeMethod(logonMethod);
232 } finally {
233 logonMethod.releaseConnection();
234 }
235
236 if (logonMethod.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY || logonMethod.getResponseHeader("Location") == null) {
237 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
238 }
239 location = logonMethod.getResponseHeader("Location").getValue();
240 GetMethod redirectMethod = new GetMethod(location);
241 try {
242 httpClient.executeMethod(redirectMethod);
243 responseBodyAsString = redirectMethod.getResponseBodyAsString();
244 } finally {
245 redirectMethod.releaseConnection();
246 }
238247 }
239248
240249 if (!responseBodyAsString.contains("login.microsoftonline.com")) {
257266 targetMethod.setParameter("wctx", wctx);
258267 try {
259268 httpClient.executeMethod(targetMethod);
260 responseBodyAsString = logonMethod.getResponseBodyAsString();
269 responseBodyAsString = targetMethod.getResponseBodyAsString();
261270 } finally {
262271 targetMethod.releaseConnection();
263272 }
266275 LOGGER.debug(targetMethod.getStatusLine());
267276 LOGGER.debug(responseBodyAsString);
268277
269 throw new IOException("ADFS authentication not yet implemented");
278 if (targetMethod.getStatusCode() == HttpStatus.SC_OK) {
279 JSONObject config = extractConfig(responseBodyAsString);
280 if (config.optJSONArray("arrScopes") != null || config.optJSONArray("urlPostRedirect") != null) {
281 LOGGER.debug("Authentication successful but user consent or validation needed, please open the following url in a browser");
282 LOGGER.debug(authorizeUrl);
283 throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
284 }
285 } else if (targetMethod.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY || targetMethod.getRequestHeader("Location") == null) {
286 throw new IOException("Unknown ADFS authentication failure");
287 }
288
289 location = targetMethod.getRequestHeader("Location").getValue();
290 if (location.contains("code=") && location.contains("&session_state=")) {
291 String code = location.substring(location.indexOf("code=") + 5, location.indexOf("&session_state="));
292 LOGGER.debug("Authentication Code: " + code);
293 return code;
294 }
295 throw new IOException("Unknown ADFS authentication failure");
270296 }
271297
272298 private PostMethod handleMfa(HttpClient httpClient, PostMethod logonMethod, String username, String clientRequestId) throws JSONException, IOException {
1717 */
1818 package davmail.exchange.auth;
1919
20 import davmail.BundleMessage;
2120 import davmail.Settings;
21 import davmail.exception.DavMailAuthenticationException;
22 import davmail.exception.DavMailException;
2223 import davmail.exchange.ews.BaseShape;
2324 import davmail.exchange.ews.DistinguishedFolderId;
2425 import davmail.exchange.ews.GetFolderMethod;
2526 import davmail.exchange.ews.GetUserConfigurationMethod;
2627 import davmail.http.DavGatewayHttpClientFacade;
27 import davmail.ui.tray.DavGatewayTray;
28 import javafx.application.Platform;
29 import javafx.beans.value.ChangeListener;
30 import javafx.beans.value.ObservableValue;
31 import javafx.concurrent.Worker.State;
32 import javafx.embed.swing.JFXPanel;
33 import javafx.scene.Scene;
34 import javafx.scene.control.ProgressBar;
35 import javafx.scene.layout.StackPane;
36 import javafx.scene.web.WebEngine;
37 import javafx.scene.web.WebView;
3828 import org.apache.commons.httpclient.HttpClient;
3929 import org.apache.commons.httpclient.util.URIUtil;
4030 import org.apache.log4j.Logger;
41 import org.w3c.dom.Document;
4231
4332 import javax.swing.*;
44 import javax.xml.transform.OutputKeys;
45 import javax.xml.transform.Transformer;
46 import javax.xml.transform.TransformerFactory;
47 import javax.xml.transform.dom.DOMSource;
48 import javax.xml.transform.stream.StreamResult;
49 import java.awt.*;
50 import java.io.ByteArrayOutputStream;
5133 import java.io.IOException;
52 import java.io.OutputStreamWriter;
53 import java.net.*;
54
55 public class O365InteractiveAuthenticator extends JFrame implements ExchangeAuthenticator {
56
34 import java.net.Authenticator;
35 import java.net.PasswordAuthentication;
36
37 public class O365InteractiveAuthenticator implements ExchangeAuthenticator {
38
39 private static final int MAX_COUNT = 300;
5740 private static final Logger LOGGER = Logger.getLogger(O365InteractiveAuthenticator.class);
5841
59 private static final int MAX_COUNT = 300;
60
61 static {
62 // register a stream handler for msauth protocol
63 try {
64 URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
65 @Override
66 public URLStreamHandler createURLStreamHandler(String protocol) {
67 if ("msauth".equals(protocol)) {
68 return new URLStreamHandler() {
69 @Override
70 protected URLConnection openConnection(URL u) {
71 return new URLConnection(u) {
72 @Override
73 public void connect() {
74 // ignore
75 }
76 };
77 }
78 };
79 }
80 return null;
81 }
82 });
83 } catch (Throwable t) {
84 LOGGER.warn("Unable to register msauth protocol handler");
85 }
86 }
87
88 String location;
8942 boolean isAuthenticated = false;
90 String errorCode = "";
91 final JFXPanel fxPanel = new JFXPanel();
92
93 public O365InteractiveAuthenticator() {
94 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
95 setTitle(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
96 try {
97 setIconImages(DavGatewayTray.getFrameIcons());
98 } catch (NoSuchMethodError error) {
99 DavGatewayTray.debug(new BundleMessage("LOG_UNABLE_TO_SET_ICON_IMAGE"));
100 }
101
102 JPanel mainPanel = new JPanel();
103
104 mainPanel.add(fxPanel);
105 add(BorderLayout.CENTER, mainPanel);
106
107 pack();
108 setResizable(true);
109 // center frame
110 setSize(600, 600);
111 setLocation(getToolkit().getScreenSize().width / 2 -
112 getSize().width / 2,
113 getToolkit().getScreenSize().height / 2 -
114 getSize().height / 2);
115 setVisible(true);
116 }
117
118 private void initFX(final JFXPanel fxPanel, final String url, final String redirectUri) {
119 WebView webView = new WebView();
120 final WebEngine webViewEngine = webView.getEngine();
121
122 final ProgressBar loadProgress = new ProgressBar();
123 loadProgress.progressProperty().bind(webViewEngine.getLoadWorker().progressProperty());
124
125 StackPane hBox = new StackPane();
126 hBox.getChildren().setAll(webView, loadProgress);
127 Scene scene = new Scene(hBox);
128 fxPanel.setScene(scene);
129
130 webViewEngine.setUserAgent(DavGatewayHttpClientFacade.getUserAgent());
131
132 webViewEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
133 @Override
134 public void changed(ObservableValue ov, State oldState, State newState) {
135 LOGGER.debug(webViewEngine.getLocation());
136 LOGGER.debug(dumpDocument(webViewEngine.getDocument()));
137 if (newState == State.SUCCEEDED) {
138 loadProgress.setVisible(false);
139 // bring window to top
140 setAlwaysOnTop(true);
141 setAlwaysOnTop(false);
142 location = webViewEngine.getLocation();
143 setTitle("DavMail: " + location);
144 LOGGER.debug("Webview location: " + location);
145 if (LOGGER.isDebugEnabled()) {
146 LOGGER.debug(dumpDocument(webViewEngine.getDocument()));
147 }
148 if (location.startsWith(redirectUri)) {
149 LOGGER.debug("Location starts with redirectUri, check code");
150
151 isAuthenticated = location.contains("code=") && location.contains("&session_state=");
152 if (!isAuthenticated && location.contains("error=")) {
153 errorCode = location.substring(location.indexOf("error="));
154 }
155 close();
156 }
157 } else if (newState == State.FAILED) {
158 Throwable e = webViewEngine.getLoadWorker().getException();
159 if (e != null) {
160 LOGGER.error(e + " " + e.getMessage());
161 errorCode = e.getMessage();
162 }
163 close();
164 }
165
166 }
167
168 });
169 webViewEngine.load(url);
170
171
172 }
173
174 public String dumpDocument(Document document) {
175 String result;
176 try {
177 ByteArrayOutputStream baos = new ByteArrayOutputStream();
178 Transformer transformer = TransformerFactory.newInstance().newTransformer();
179 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
180 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
181 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
182 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
183 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
184
185 transformer.transform(new DOMSource(document),
186 new StreamResult(new OutputStreamWriter(baos, "UTF-8")));
187 result = baos.toString("UTF-8");
188 } catch (Exception e) {
189 result = e + " " + e.getMessage();
190 }
191 return result;
192 }
43 String errorCode = null;
44 String code = null;
19345
19446 String resource = "https://outlook.office365.com";
19547 String ewsUrl = resource + "/EWS/Exchange.asmx";
19648 String authorizeUrl = "https://login.microsoftonline.com/common/oauth2/authorize";
49
50 private O365InteractiveAuthenticatorFrame o365InteractiveAuthenticatorFrame;
19751
19852 private String username;
19953 private String password;
255109 }
256110 }
257111 });
258 Platform.setImplicitExit(false);
259
260 // Run initFX as JavaFX-Thread
261 Platform.runLater(new Runnable() {
112
113 SwingUtilities.invokeLater(new Runnable() {
262114 @Override
263115 public void run() {
264116 try {
265 initFX(fxPanel, initUrl, redirectUri);
266 } catch (Throwable e) {
267 LOGGER.error(e + " " + e.getMessage());
268 errorCode = e.getMessage();
269 close();
117 o365InteractiveAuthenticatorFrame = new O365InteractiveAuthenticatorFrame();
118 o365InteractiveAuthenticatorFrame.setO365InteractiveAuthenticator(O365InteractiveAuthenticator.this);
119 o365InteractiveAuthenticatorFrame.authenticate(initUrl, redirectUri);
120 } catch (NoClassDefFoundError e) {
121 errorCode = "Unable to load JavaFX (OpenJFX)";
270122 }
271123 }
272124 });
273125
274126 int count = 0;
275127
276 while (!isAuthenticated && isVisible() && count++ < MAX_COUNT) {
128 while (!isAuthenticated && errorCode == null && count++ < MAX_COUNT) {
277129 try {
278130 Thread.sleep(1000);
279131 } catch (InterruptedException e) {
280132 // ignore
281133 }
282134 }
283 if (isVisible()) {
284 close();
135
136 if (count > MAX_COUNT) {
137 errorCode = "Timed out waiting for interactive authentication";
138 }
139
140 if (o365InteractiveAuthenticatorFrame != null && o365InteractiveAuthenticatorFrame.isVisible()) {
141 o365InteractiveAuthenticatorFrame.close();
285142 }
286143
287144 if (isAuthenticated) {
288 LOGGER.debug("Authenticated location: " + location);
289 String code = location.substring(location.indexOf("code=") + 5, location.indexOf("&session_state="));
290 String sessionState = location.substring(location.lastIndexOf('='));
291
292 LOGGER.debug("Authentication Code: " + code);
293 LOGGER.debug("Authentication session state: " + sessionState);
294
295145 token = new O365Token(clientId, redirectUri, code);
296146
297147 LOGGER.debug("Authenticated username: " + token.getUsername());
298148 if (username != null && !username.isEmpty() && !username.equalsIgnoreCase(token.getUsername())) {
299 throw new IOException("Authenticated username " + token.getUsername() + " does not match " + username);
149 throw new DavMailAuthenticationException("Authenticated username " + token.getUsername() + " does not match " + username);
300150 }
301151
302152 } else {
303153 LOGGER.error("Authentication failed " + errorCode);
304 throw new IOException("Authentication failed " + errorCode);
305 }
306 }
307
308 void close() {
309 setVisible(false);
310 dispose();
154 throw new DavMailException("EXCEPTION_AUTHENTICATION_FAILED_REASON", errorCode);
155 }
311156 }
312157
313158 public static void main(String[] argv) {
315160 Settings.setDefaultSettings();
316161 //Settings.setLoggingLevel("httpclient.wire", Level.DEBUG);
317162
318 O365InteractiveAuthenticator authenticationFrame = new O365InteractiveAuthenticator();
319 authenticationFrame.setUsername("");
320 authenticationFrame.authenticate();
163 O365InteractiveAuthenticator authenticator = new O365InteractiveAuthenticator();
164 authenticator.setUsername("");
165 authenticator.authenticate();
321166
322167 // switch to EWS url
323 HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(authenticationFrame.ewsUrl);
324 DavGatewayHttpClientFacade.createMultiThreadedHttpConnectionManager(httpClient);
168 HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(authenticator.ewsUrl);
325169
326170 GetFolderMethod checkMethod = new GetFolderMethod(BaseShape.ID_ONLY, DistinguishedFolderId.getInstance(null, DistinguishedFolderId.Name.root), null);
327 checkMethod.setRequestHeader("Authorization", "Bearer " + authenticationFrame.getToken().getAccessToken());
171 checkMethod.setRequestHeader("Authorization", "Bearer " + authenticator.getToken().getAccessToken());
328172 try {
329173 //checkMethod.setServerVersion(serverVersion);
330174 httpClient.executeMethod(checkMethod);
339183 int i = 0;
340184 while (i++ < 12 * 60 * 2) {
341185 GetUserConfigurationMethod getUserConfigurationMethod = new GetUserConfigurationMethod();
342 getUserConfigurationMethod.setRequestHeader("Authorization", "Bearer " + authenticationFrame.getToken().getAccessToken());
186 getUserConfigurationMethod.setRequestHeader("Authorization", "Bearer " + authenticator.getToken().getAccessToken());
343187 httpClient.executeMethod(getUserConfigurationMethod);
344188 System.out.println(getUserConfigurationMethod.getResponseItem());
345189
0 /*
1 * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
2 * Copyright (C) 2010 Mickael Guessant
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 package davmail.exchange.auth;
20
21 import davmail.BundleMessage;
22 import davmail.http.DavGatewayHttpClientFacade;
23 import davmail.ui.tray.DavGatewayTray;
24 import davmail.util.IOUtil;
25 import javafx.application.Platform;
26 import javafx.beans.value.ChangeListener;
27 import javafx.beans.value.ObservableValue;
28 import javafx.concurrent.Worker;
29 import javafx.embed.swing.JFXPanel;
30 import javafx.scene.Scene;
31 import javafx.scene.control.ProgressBar;
32 import javafx.scene.layout.StackPane;
33 import javafx.scene.web.WebEngine;
34 import javafx.scene.web.WebView;
35 import org.apache.log4j.Logger;
36 import org.w3c.dom.Document;
37
38 import javax.swing.*;
39 import javax.xml.transform.OutputKeys;
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.TransformerFactory;
42 import javax.xml.transform.dom.DOMSource;
43 import javax.xml.transform.stream.StreamResult;
44 import java.awt.*;
45 import java.awt.event.WindowAdapter;
46 import java.awt.event.WindowEvent;
47 import java.io.*;
48 import java.net.*;
49 import java.util.List;
50 import java.util.Map;
51
52 public class O365InteractiveAuthenticatorFrame extends JFrame {
53 private static final Logger LOGGER = Logger.getLogger(O365InteractiveAuthenticatorFrame.class);
54
55 private O365InteractiveAuthenticator authenticator;
56
57 static {
58 // register a stream handler for msauth protocol
59 try {
60 URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
61 @Override
62 public URLStreamHandler createURLStreamHandler(String protocol) {
63 if ("msauth".equals(protocol) || "urn".equals(protocol)) {
64 return new URLStreamHandler() {
65 @Override
66 protected URLConnection openConnection(URL u) {
67 return new URLConnection(u) {
68 @Override
69 public void connect() {
70 // ignore
71 }
72 };
73 }
74 };
75 } else if ("https".equals(protocol)) {
76 return new sun.net.www.protocol.https.Handler() {
77 @Override
78 protected URLConnection openConnection(URL url) throws IOException {
79 return openConnection(url, null);
80 }
81
82 @Override
83 protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {
84 LOGGER.debug("openConnection " + url);
85
86 if (url.toExternalForm().endsWith("/common/handlers/watson")) {
87 LOGGER.warn("Failed: form calls watson");
88 }
89 final HttpURLConnection httpsURLConnection = (HttpURLConnection) super.openConnection(url, proxy);
90 if (("login.microsoftonline.com".equals(url.getHost()) && "/common/oauth2/authorize".equals(url.getPath()))
91 || ("login.live.com".equals(url.getHost()) && "/oauth20_authorize.srf".equals(url.getPath()))
92 || ("login.live.com".equals(url.getHost()) && "/ppsecure/post.srf".equals(url.getPath()))
93 || ("login.microsoftonline.com".equals(url.getHost()) && "/common/login".equals(url.getPath()))
94 || ("login.microsoftonline.com".equals(url.getHost()) && "/common/SAS/ProcessAuth".equals(url.getPath()))
95 ) {
96 LOGGER.debug("Disable integrity check on external resources");
97
98 return new HttpURLConnection(url) {
99 @Override
100 public void setRequestMethod(String method) throws ProtocolException {
101 httpsURLConnection.setRequestMethod(method);
102 }
103
104 @Override
105 public void setInstanceFollowRedirects(boolean followRedirects) {
106 httpsURLConnection.setInstanceFollowRedirects(followRedirects);
107 }
108
109 @Override
110 public boolean getInstanceFollowRedirects() {
111 return httpsURLConnection.getInstanceFollowRedirects();
112 }
113
114 @Override
115 public String getRequestMethod() {
116 return httpsURLConnection.getRequestMethod();
117 }
118
119 @Override
120 public int getResponseCode() throws IOException {
121 return httpsURLConnection.getResponseCode();
122 }
123
124 @Override
125 public String getResponseMessage() throws IOException {
126 return httpsURLConnection.getResponseMessage();
127 }
128
129 @Override
130 public Map<String,List<String>> getHeaderFields() {
131 LOGGER.debug(httpsURLConnection.getHeaderFields());
132 return httpsURLConnection.getHeaderFields();
133 }
134
135 @Override
136 public String getHeaderField(String name) {
137 return httpsURLConnection.getHeaderField(name);
138 }
139
140 @Override
141 public String getHeaderField(int n) {
142 return httpsURLConnection.getHeaderField(n);
143 }
144
145 @Override
146 public void disconnect() {
147 httpsURLConnection.disconnect();
148 }
149
150 @Override
151 public void setDoOutput(boolean dooutput) {
152 httpsURLConnection.setDoOutput(dooutput);
153 }
154
155 @Override
156 public boolean usingProxy() {
157 return httpsURLConnection.usingProxy();
158 }
159
160 @Override
161 public void connect() throws IOException {
162 httpsURLConnection.connect();
163 }
164
165 @Override
166 public InputStream getInputStream() throws IOException {
167 byte[] content = IOUtil.readFully(httpsURLConnection.getInputStream());
168 String contentAsString = new String(content, "UTF-8");
169 LOGGER.debug(contentAsString);
170 ByteArrayOutputStream baos = new ByteArrayOutputStream();
171 baos.write(contentAsString.replaceAll("integrity", "integrity.disabled").getBytes("UTF-8"));
172 return new ByteArrayInputStream(baos.toByteArray());
173 }
174
175 @Override
176 public OutputStream getOutputStream() throws IOException {
177 return httpsURLConnection.getOutputStream();
178 }
179
180 @Override
181 public InputStream getErrorStream() {
182 return httpsURLConnection.getErrorStream();
183 }
184
185 };
186
187 } else {
188 return httpsURLConnection;
189 }
190 }
191
192 };
193 }
194 return null;
195 }
196 });
197 } catch (Throwable t) {
198 LOGGER.warn("Unable to register protocol handler");
199 }
200 }
201
202 String location;
203 final JFXPanel fxPanel = new JFXPanel();
204
205 public O365InteractiveAuthenticatorFrame() {
206 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
207 addWindowListener(new WindowAdapter() {
208 @Override
209 public void windowClosing(WindowEvent e) {
210 if (!authenticator.isAuthenticated && authenticator.errorCode == null) {
211 authenticator.errorCode = "user closed authentication window";
212 }
213 }
214 });
215
216 setTitle(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
217 try {
218 setIconImages(DavGatewayTray.getFrameIcons());
219 } catch (NoSuchMethodError error) {
220 DavGatewayTray.debug(new BundleMessage("LOG_UNABLE_TO_SET_ICON_IMAGE"));
221 }
222
223 JPanel mainPanel = new JPanel();
224
225 mainPanel.add(fxPanel);
226 add(BorderLayout.CENTER, mainPanel);
227
228 pack();
229 setResizable(true);
230 // center frame
231 setSize(600, 600);
232 setLocationRelativeTo(null);
233 setVisible(true);
234 // bring window to top
235 setAlwaysOnTop(true);
236 setAlwaysOnTop(false);
237 }
238
239 public void setO365InteractiveAuthenticator(O365InteractiveAuthenticator authenticator) {
240 this.authenticator = authenticator;
241 }
242
243 private void initFX(final JFXPanel fxPanel, final String url, final String redirectUri) {
244 WebView webView = new WebView();
245 final WebEngine webViewEngine = webView.getEngine();
246
247 final ProgressBar loadProgress = new ProgressBar();
248 loadProgress.progressProperty().bind(webViewEngine.getLoadWorker().progressProperty());
249
250 StackPane hBox = new StackPane();
251 hBox.getChildren().setAll(webView, loadProgress);
252 Scene scene = new Scene(hBox);
253 fxPanel.setScene(scene);
254
255 webViewEngine.setUserAgent(DavGatewayHttpClientFacade.getUserAgent());
256
257 webViewEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
258 @Override
259 public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
260 if (newState == Worker.State.SUCCEEDED) {
261 loadProgress.setVisible(false);
262 location = webViewEngine.getLocation();
263 updateTitleAndFocus(location);
264 LOGGER.debug("Webview location: " + location);
265 // override console.log
266 O365InteractiveJSLogger.register(webViewEngine);
267 if (LOGGER.isDebugEnabled()) {
268 LOGGER.debug(dumpDocument(webViewEngine.getDocument()));
269 }
270 if (location.startsWith(redirectUri)) {
271 LOGGER.debug("Location starts with redirectUri, check code");
272
273 authenticator.isAuthenticated = location.contains("code=") && location.contains("&session_state=");
274 if (!authenticator.isAuthenticated && location.contains("error=")) {
275 authenticator.errorCode = location.substring(location.indexOf("error="));
276 }
277 if (authenticator.isAuthenticated) {
278 LOGGER.debug("Authenticated location: " + location);
279 String code = location.substring(location.indexOf("code=") + 5, location.indexOf("&session_state="));
280 String sessionState = location.substring(location.lastIndexOf('='));
281
282 LOGGER.debug("Authentication Code: " + code);
283 LOGGER.debug("Authentication session state: " + sessionState);
284 authenticator.code = code;
285 }
286 close();
287 }
288 } else if (newState == Worker.State.FAILED) {
289 Throwable e = webViewEngine.getLoadWorker().getException();
290 if (e != null) {
291 LOGGER.error(e + " " + e.getMessage());
292 authenticator.errorCode = e.getMessage();
293 if (authenticator.errorCode == null) {
294 authenticator.errorCode = e.toString();
295 }
296 }
297 close();
298 }
299
300 }
301
302 });
303 webViewEngine.load(url);
304 }
305
306 private void updateTitleAndFocus(final String location) {
307 SwingUtilities.invokeLater(new Runnable() {
308 @Override
309 public void run() {
310 setState(JFrame.NORMAL);
311 setAlwaysOnTop(true);
312 setAlwaysOnTop(false);
313 setTitle("DavMail: " + location);
314 }
315 });
316 }
317
318 public String dumpDocument(Document document) {
319 String result;
320 try {
321 ByteArrayOutputStream baos = new ByteArrayOutputStream();
322 Transformer transformer = TransformerFactory.newInstance().newTransformer();
323 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
324 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
325 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
326 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
327 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
328
329 transformer.transform(new DOMSource(document),
330 new StreamResult(new OutputStreamWriter(baos, "UTF-8")));
331 result = baos.toString("UTF-8");
332 } catch (Exception e) {
333 result = e + " " + e.getMessage();
334 }
335 return result;
336 }
337
338 public void authenticate(final String initUrl, final String redirectUri) {
339 // Run initFX as JavaFX-Thread
340 Platform.runLater(new Runnable() {
341 @Override
342 public void run() {
343 try {
344 Platform.setImplicitExit(false);
345
346 initFX(fxPanel, initUrl, redirectUri);
347 } catch (Throwable e) {
348 LOGGER.error(e + " " + e.getMessage());
349 authenticator.errorCode = e.getMessage();
350 if (authenticator.errorCode == null) {
351 authenticator.errorCode = e.toString();
352 }
353 close();
354 }
355 }
356 });
357
358
359 }
360
361 public void close() {
362 SwingUtilities.invokeLater(new Runnable() {
363 @Override
364 public void run() {
365 setVisible(false);
366 dispose();
367 }
368 });
369 }
370
371 }
0 /*
1 * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
2 * Copyright (C) 2010 Mickael Guessant
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 package davmail.exchange.auth;
20
21 import javafx.scene.web.WebEngine;
22 import netscape.javascript.JSObject;
23 import org.apache.log4j.Logger;
24
25 public class O365InteractiveJSLogger {
26 private static final Logger LOGGER = Logger.getLogger(O365InteractiveJSLogger.class);
27 public void log(String message) {
28 LOGGER.info(message);
29 }
30
31 public static void register(WebEngine webEngine) {
32 JSObject window = (JSObject) webEngine.executeScript("window");
33 window.setMember("davmail", new O365InteractiveJSLogger());
34 webEngine.executeScript("console.log = function(message) { davmail.log(message); }");
35 }
36 }
0 /*
1 * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
2 * Copyright (C) 2010 Mickael Guessant
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 package davmail.exchange.auth;
20
21 import davmail.Settings;
22 import org.apache.log4j.Logger;
23
24 import java.io.IOException;
25
26 /**
27 * Experimental: load Oauth2 token from settings
28 */
29 public class O365StoredTokenAuthenticator implements ExchangeAuthenticator {
30 private static final Logger LOGGER = Logger.getLogger(O365StoredTokenAuthenticator.class);
31
32 String resource = "https://outlook.office365.com";
33 String ewsUrl = resource + "/EWS/Exchange.asmx";
34
35 private String username;
36 private O365Token token;
37
38 @Override
39 public void setUsername(String username) {
40 this.username = username;
41 }
42
43 @Override
44 public void setPassword(String password) {
45 // unused
46 }
47
48 @Override
49 public void authenticate() throws IOException {
50 // common DavMail client id
51 final String clientId = Settings.getProperty("davmail.oauth.clientId", "facd6cff-a294-4415-b59f-c5b01937d7bd");
52 // standard native app redirectUri
53 final String redirectUri = Settings.getProperty("davmail.oauth.redirectUri", "https://login.microsoftonline.com/common/oauth2/nativeclient");
54 String refreshToken = Settings.getProperty("davmail.oauth."+username.toLowerCase()+".refreshToken");
55 if (refreshToken == null) {
56 // single user mode
57 refreshToken = Settings.getProperty("davmail.oauth.refreshToken");
58 }
59 String accessToken = Settings.getProperty("davmail.oauth.accessToken");
60 if (refreshToken == null && accessToken == null) {
61 throw new IOException("Missing token");
62 }
63
64 token = new O365Token(clientId, redirectUri);
65 if (accessToken != null) {
66 // for tests only: load access token, will expire in at most one hour
67 token.setAccessToken(accessToken);
68 } else {
69 token.setRefreshToken(refreshToken);
70 token.refreshToken();
71 }
72 }
73
74 @Override
75 public O365Token getToken() throws IOException {
76 return token;
77 }
78
79 @Override
80 public String getEWSUrl() {
81 return ewsUrl;
82 }
83 }
1818
1919 package davmail.exchange.auth;
2020
21 import davmail.Settings;
2122 import davmail.http.DavGatewayHttpClientFacade;
2223 import davmail.http.RestMethod;
2324 import davmail.util.IOUtil;
3132 import java.util.Date;
3233
3334 public class O365Token {
34 protected final String URL = "https://login.microsoftonline.com/common/oauth2/token";
35 protected final String TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/token";
36 protected final String RESOURCE_URL = "https://outlook.office365.com/";
3537
3638 protected static final Logger LOGGER = Logger.getLogger(O365Token.class);
3739
4244 private String accessToken;
4345 private long expireson;
4446
47 public O365Token(String clientId, String redirectUri) {
48 this.clientId = clientId;
49 this.redirectUri = redirectUri;
50 }
51
4552 public O365Token(String clientId, String redirectUri, String code) throws IOException {
4653 this.clientId = clientId;
4754 this.redirectUri = redirectUri;
4855
49 RestMethod tokenMethod = new RestMethod(URL);
56 RestMethod tokenMethod = new RestMethod(TOKEN_URL);
5057 tokenMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
5158 tokenMethod.addParameter("grant_type", "authorization_code");
5259 tokenMethod.addParameter("code", code);
6370
6471 public void setJsonToken(JSONObject jsonToken) throws IOException {
6572 try {
73 if (jsonToken.opt("error") != null) {
74 throw new IOException(jsonToken.optString("error")+" "+jsonToken.optString("error_description"));
75 }
6676 // access token expires after one hour
6777 accessToken = jsonToken.getString("access_token");
6878 // precious refresh token
7686 JSONObject tokenBody = new JSONObject(decodedBearer);
7787 LOGGER.debug("Token: " + tokenBody);
7888 username = tokenBody.getString("unique_name");
89
90 if (Settings.getBooleanProperty("davmail.oauth.persistToken", false)) {
91 Settings.setProperty("davmail.oauth."+username.toLowerCase()+".refreshToken", refreshToken);
92 Settings.save();
93 }
7994 } catch (JSONException e) {
8095 throw new IOException("Exception parsing token", e);
8196 }
101116
102117 public void setAccessToken(String accessToken) {
103118 this.accessToken = accessToken;
119 // assume unexpired token
120 expireson = System.currentTimeMillis() + 1000 * 60 * 60;
104121 }
105122
106 private void refreshToken() throws IOException {
107 String url = "https://login.microsoftonline.com/common/oauth2/token";
108 RestMethod tokenMethod = new RestMethod(url);
123 public void refreshToken() throws IOException {
124 RestMethod tokenMethod = new RestMethod(TOKEN_URL);
109125 tokenMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
110126 tokenMethod.addParameter("grant_type", "refresh_token");
111127 tokenMethod.addParameter("refresh_token", refreshToken);
112128 tokenMethod.addParameter("redirect_uri", redirectUri);
113129 tokenMethod.addParameter("client_id", clientId);
130 tokenMethod.addParameter("resource", "https://outlook.office365.com/");
114131
115132 executeMethod(tokenMethod);
116133 }
118135 private void executeMethod(RestMethod tokenMethod) throws IOException {
119136 HttpClient httpClient = null;
120137 try {
121 httpClient = DavGatewayHttpClientFacade.getInstance(URL);
138 httpClient = DavGatewayHttpClientFacade.getInstance(RESOURCE_URL);
122139 httpClient.executeMethod(tokenMethod);
123140 setJsonToken(tokenMethod.getJsonResponse());
124141
131148 }
132149
133150 }
151
152 public void setRefreshToken(String refreshToken) {
153 this.refreshToken = refreshToken;
154 }
134155 }
11521152 protected Folder buildFolder(EWSMethod.Item item) {
11531153 Folder folder = new Folder();
11541154 folder.folderId = new FolderId(item);
1155 folder.displayName = item.get(Field.get("folderDisplayName").getResponseName());
1155 folder.displayName = encodeSlash(item.get(Field.get("folderDisplayName").getResponseName()));
11561156 folder.folderClass = item.get(Field.get("folderclass").getResponseName());
11571157 folder.etag = item.get(Field.get("lastmodified").getResponseName());
11581158 folder.ctag = item.get(Field.get("ctag").getResponseName());
12461246 EWSMethod.Item folder = new EWSMethod.Item();
12471247 folder.type = "Folder";
12481248 folder.put("FolderClass", folderClass);
1249 folder.put("DisplayName", path.folderName);
1249 folder.put("DisplayName", decodeSlash(path.folderName));
12501250 // TODO: handle properties
12511251 CreateFolderMethod createFolderMethod = new CreateFolderMethod(getFolderId(path.parentPath), folder);
12521252 executeMethod(createFolderMethod);
27632763 CreateItemMethod createItemMethod = new CreateItemMethod(MessageDisposition.SaveOnly, SendMeetingInvitations.SendToNone, getFolderId("davmailtemp"), item);
27642764 executeMethod(createItemMethod);
27652765 item = createItemMethod.getResponseItem();
2766 if (item == null) {
2767 throw new IOException("Empty timezone item");
2768 }
27662769 VCalendar vCalendar = new VCalendar(getContent(new ItemId(item)), email, null);
27672770 this.vTimezone = vCalendar.getVTimezone();
27682771 // delete temporary folder
29082911 parentFolderId,
29092912 FOLDER_PROPERTIES,
29102913 new TwoOperandExpression(TwoOperandExpression.Operator.IsEqualTo,
2911 Field.get("folderDisplayName"), folderName),
2914 Field.get("folderDisplayName"), decodeSlash(folderName)),
29122915 0, 1
29132916 );
29142917 executeMethod(findFolderMethod);
29172920 folderId = new FolderId(item);
29182921 }
29192922 return folderId;
2923 }
2924
2925 private String decodeSlash(String folderName) {
2926 if (folderName.contains("_xF8FF_")) {
2927 return folderName.replaceAll("_xF8FF_", "/");
2928 } else {
2929 return folderName;
2930 }
2931 }
2932
2933 private String encodeSlash(String folderName) {
2934 if (folderName.contains("/")) {
2935 return folderName.replaceAll("/", "_xF8FF_");
2936 } else {
2937 return folderName;
2938 }
29202939 }
29212940
29222941 long throttlingTimestamp = 0;
30933112 }
30943113
30953114 protected String convertDateFromExchange(String exchangeDateValue) throws DavMailException {
3096 String zuluDateValue = null;
3097 if (exchangeDateValue != null) {
3098 try {
3099 zuluDateValue = getZuluDateFormat().format(getExchangeZuluDateFormat().parse(exchangeDateValue));
3100 } catch (ParseException e) {
3115 // yyyy-MM-dd'T'HH:mm:ss'Z' to yyyyMMdd'T'HHmmss'Z'
3116 if (exchangeDateValue == null) {
3117 return null;
3118 } else {
3119 if (exchangeDateValue.length() != 20) {
31013120 throw new DavMailException("EXCEPTION_INVALID_DATE", exchangeDateValue);
31023121 }
3103 }
3104 return zuluDateValue;
3122 StringBuilder buffer = new StringBuilder();
3123 for (int i = 0; i < exchangeDateValue.length(); i++) {
3124 if (i == 4 || i == 7 || i == 13 || i == 16) {
3125 i++;
3126 }
3127 buffer.append(exchangeDateValue.charAt(i));
3128 }
3129 return buffer.toString();
3130 }
31053131 }
31063132
31073133 protected String convertCalendarDateToExchange(String vcalendarDateValue) throws DavMailException {
5858
5959 }
6060
61 protected Item createResponseItem() {
62 if (responseItems.isEmpty()) {
63 Item responseItem = new Item();
64 responseItems.add(responseItem);
65 return responseItem;
66 } else {
67 return responseItems.get(0);
68 }
69 }
70
7161 @Override
7262 protected void handleCustom(XMLStreamReader reader) throws XMLStreamException {
7363 if (XMLStreamUtil.isStartTag(reader, "PictureData")) {
1919 package davmail.exchange.ews;
2020
2121 import davmail.exchange.ExchangeSession;
22 import org.apache.log4j.Logger;
2223
2324 import java.io.IOException;
2425 import java.io.Writer;
2930 * Handle calendar item recurrence update
3031 */
3132 public class RecurrenceFieldUpdate extends FieldUpdate {
33 public static final Logger LOGGER = Logger.getLogger(RecurrenceFieldUpdate.class);
3234 static final HashMap<String, String> calDayToDayOfWeek = new HashMap<String, String>();
3335 static {
3436 calDayToDayOfWeek.put("SU", "Sunday");
5456 public void setByDay(String[] days) {
5557 byDays = new HashSet<String>();
5658 for (String day: days) {
57 byDays.add(calDayToDayOfWeek.get(day));
59 String value = calDayToDayOfWeek.get(day);
60 if (value == null) {
61 LOGGER.warn("Invalid day value: "+day);
62 } else {
63 byDays.add(value);
64 }
5865 }
5966 }
6067
641641 // separate domain from username in credentials
642642 AuthScope authScope = new AuthScope(null, -1);
643643 NTCredentials credentials = (NTCredentials) httpClient.getState().getCredentials(authScope);
644 setCredentials(httpClient, credentials.getUserName(), credentials.getPassword());
644 if (credentials != null && credentials.getDomain() == null) {
645 setCredentials(httpClient, credentials.getUserName(), credentials.getPassword());
646 }
645647 }
646648
647649 /**
7878 public NotificationDialog(String to, String cc, String subject, String description) {
7979 hasRecipients = to != null || cc != null;
8080 setModal(true);
81 setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
81 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
8282 setTitle(BundleMessage.format("UI_CALDAV_NOTIFICATION"));
8383 try {
8484 setIconImages(DavGatewayTray.getFrameIcons());
266266 Class.forName("javafx.embed.swing.JFXPanel");
267267 isJavaFXAvailable = true;
268268 } catch (ClassNotFoundException e) {
269 // ignore
270 } catch (NoClassDefFoundError e) {
269271 // ignore
270272 }
271273 return isJavaFXAvailable;
901903 pack();
902904 //setResizable(false);
903905 // center frame
904 setLocation(getToolkit().getScreenSize().width / 2 -
905 getSize().width / 2,
906 getToolkit().getScreenSize().height / 2 -
907 getSize().height / 2);
906 setLocationRelativeTo(null);
908907 urlField.requestFocus();
909908 }
910909 }
4040 */
4141 public class AwtGatewayTray implements DavGatewayTrayInterface {
4242 protected static final String TRAY_PNG = "tray.png";
43 protected static final String TRAY128_PNG = "tray128.png";
4443
4544 protected static final String TRAY_ACTIVE_PNG = "tray2.png";
4645 protected static final String TRAY_INACTIVE_PNG = "trayinactive.png";
4746
47 protected static final String TRAY128_PNG = "tray128.png";
48 protected static final String TRAY128_ACTIVE_PNG = "tray128active.png";
49 protected static final String TRAY128_INACTIVE_PNG = "tray128inactive.png";
4850
4951 protected AwtGatewayTray() {
5052 }
5658 static TrayIcon trayIcon;
5759 protected static ArrayList<Image> frameIcons;
5860 protected static BufferedImage image;
59 protected static BufferedImage image2;
61 protected static BufferedImage activeImage;
6062 protected static BufferedImage inactiveImage;
6163 static LogBrokerMonitor logBrokerMonitor;
6264 private boolean isActive = true;
7981 SwingUtilities.invokeLater(new Runnable() {
8082 public void run() {
8183 if (trayIcon.getImage().equals(image)) {
82 trayIcon.setImage(image2);
84 trayIcon.setImage(activeImage);
8385 } else {
8486 trayIcon.setImage(image);
8587 }
199201
200202 protected void loadIcons() {
201203 image = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY_PNG));
202 image2 = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY_ACTIVE_PNG));
204 activeImage = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY_ACTIVE_PNG));
203205 inactiveImage = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY_INACTIVE_PNG));
204206
205207 frameIcons = new ArrayList<Image>();
228228 String javaVersion = System.getProperty("java.version");
229229 String arch = System.getProperty("sun.arch.data.model");
230230 LOGGER.debug("OS Name: " + System.getProperty("os.name") +
231 " Java version: " + javaVersion + ((arch != null) ? ' ' + arch : "") +
232 " System tray " + (SystemTray.isSupported() ? "" : "not ") + "supported " +
233 ((currentDesktop == null) ? "" : "Current Desktop: " + currentDesktop)
231 " Java version: " + javaVersion + ((arch != null) ? ' ' + arch : "") +
232 " System tray " + (SystemTray.isSupported() ? "" : "not ") + "supported " +
233 ((currentDesktop == null) ? "" : "Current Desktop: " + currentDesktop)
234234 );
235
236 if (Settings.isLinux()) {
237 // enable anti aliasing on linux
238 System.setProperty("awt.useSystemAAFontSettings", "on");
239 System.setProperty("swing.aatext", "true");
240 }
235241
236242 if (!Settings.getBooleanProperty("davmail.server")) {
237243 if ("GNOME-Classic:GNOME".equals(currentDesktop) || "ubuntu:GNOME".equals(currentDesktop)) {
238244 LOGGER.info("System tray is not supported on Gnome, will switch to frame mode");
239 } else
240 if (!notray) {
245 } else if (!notray) {
241246 if ("Unity".equals(currentDesktop)) {
242247 LOGGER.info("Detected Unity desktop, please follow instructions at " +
243248 "http://davmail.sourceforge.net/linuxsetup.html to restore normal systray " +
244249 "or run DavMail in server mode");
245250 }
246 // first try to load SWT before with Java AWT
247 ClassLoader classloader = DavGatewayTray.class.getClassLoader();
248 try {
249 // trigger ClassNotFoundException
250 classloader.loadClass("org.eclipse.swt.SWT");
251 // SWT available, create tray
252 davGatewayTray = new SwtGatewayTray();
253 davGatewayTray.init();
254 } catch (ClassNotFoundException e) {
255 DavGatewayTray.info(new BundleMessage("LOG_SWT_NOT_AVAILABLE"));
256 } catch (Throwable e) {
257 DavGatewayTray.info(new BundleMessage("LOG_SWT_NOT_AVAILABLE"));
258 davGatewayTray = null;
251 if (Settings.O365_INTERACTIVE.equals(Settings.getProperty("davmail.mode"))) {
252 LOGGER.info("O365Interactive is not compatible with SWT, do not try to create SWT tray");
253 } else {
254 // first try to load SWT before with Java AWT
255 ClassLoader classloader = DavGatewayTray.class.getClassLoader();
256 try {
257 // trigger ClassNotFoundException
258 classloader.loadClass("org.eclipse.swt.SWT");
259 // SWT available, create tray
260 davGatewayTray = new SwtGatewayTray();
261 davGatewayTray.init();
262 } catch (ClassNotFoundException e) {
263 DavGatewayTray.info(new BundleMessage("LOG_SWT_NOT_AVAILABLE"));
264 } catch (Throwable e) {
265 DavGatewayTray.info(new BundleMessage("LOG_SWT_NOT_AVAILABLE"));
266 davGatewayTray = null;
267 }
259268 }
260 // try java6 tray support
261 if (davGatewayTray == null) {
269 // try java6 tray support, except on Linux
270 if (davGatewayTray == null /*&& !isLinux()*/) {
262271 try {
263272 if (SystemTray.isSupported()) {
264273 if (isOSX()) {
305314 }
306315
307316 /**
317 * Test if running on Linux
318 *
319 * @return true on Linux
320 */
321 public static boolean isLinux() {
322 return System.getProperty("os.name").toLowerCase().startsWith("linux");
323 }
324
325 /**
308326 * Load image with current class loader.
309327 *
310328 * @param fileName image resource file name
344362 int imageType = BufferedImage.TYPE_INT_ARGB;
345363 if (backgroundColorString != null && backgroundColorString.length() == 7
346364 && backgroundColorString.startsWith("#")) {
347 int red = Integer.parseInt(backgroundColorString.substring(1,3), 16);
348 int green = Integer.parseInt(backgroundColorString.substring(3,5), 16);
349 int blue = Integer.parseInt(backgroundColorString.substring(5,7), 16);
365 int red = Integer.parseInt(backgroundColorString.substring(1, 3), 16);
366 int green = Integer.parseInt(backgroundColorString.substring(3, 5), 16);
367 int blue = Integer.parseInt(backgroundColorString.substring(5, 7), 16);
350368 backgroundColor = new Color(red, green, blue);
351369 imageType = BufferedImage.TYPE_INT_RGB;
352370 }
265265 }
266266
267267 protected void createAndShowGUI() {
268 System.setProperty("swing.defaultlaf", UIManager.getSystemLookAndFeelClassName());
269
270 image = DavGatewayTray.loadImage(AwtGatewayTray.TRAY_PNG);
271 activeImage = DavGatewayTray.loadImage(AwtGatewayTray.TRAY_ACTIVE_PNG);
272 inactiveImage = DavGatewayTray.loadImage(AwtGatewayTray.TRAY_INACTIVE_PNG);
268 // set cross platform look and feel on Linux, except is swing.defaultlaf is set
269 if (Settings.isLinux() && System.getProperty("swing.defaultlaf") == null) {
270 System.setProperty("swing.defaultlaf", UIManager.getCrossPlatformLookAndFeelClassName());
271 } else {
272 System.setProperty("swing.defaultlaf", UIManager.getSystemLookAndFeelClassName());
273 }
274 String imageName = AwtGatewayTray.TRAY_PNG;
275 String activeImageName = AwtGatewayTray.TRAY_ACTIVE_PNG;
276 String inactiveImageName = AwtGatewayTray.TRAY_INACTIVE_PNG;
277 // use hi res icons on Linux
278 if (Settings.isLinux()) {
279 imageName = AwtGatewayTray.TRAY128_PNG;
280 activeImageName = AwtGatewayTray.TRAY128_ACTIVE_PNG;
281 inactiveImageName = AwtGatewayTray.TRAY128_INACTIVE_PNG;
282 }
283 image = DavGatewayTray.loadImage(imageName);
284 activeImage = DavGatewayTray.loadImage(activeImageName);
285 inactiveImage = DavGatewayTray.loadImage(inactiveImageName);
273286
274287 frameIcons = new ArrayList<Image>();
275288 frameIcons.add(image);
310323 settingsFrame = new SettingsFrame();
311324 buildMenu();
312325
313 mainFrame.setMinimumSize(new Dimension(400, 180));
326 mainFrame.setMinimumSize(new Dimension(400, 250));
314327 mainFrame.pack();
315328 // workaround MacOSX
316329 if (mainFrame.getSize().width < 400 || mainFrame.getSize().height < 180) {
6363 @Override
6464 protected void loadIcons() {
6565 image = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(OSX_TRAY_PNG));
66 image2 = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(OSX_TRAY_ACTIVE_PNG));
66 activeImage = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(OSX_TRAY_ACTIVE_PNG));
6767 inactiveImage = DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(OSX_TRAY_INACTIVE_PNG));
6868
6969 frameIcons = new ArrayList<Image>();
3131 import org.eclipse.swt.SWT;
3232 import org.eclipse.swt.events.DisposeEvent;
3333 import org.eclipse.swt.events.DisposeListener;
34 import org.eclipse.swt.graphics.DeviceData;
35 import org.eclipse.swt.graphics.GC;
3436 import org.eclipse.swt.graphics.Image;
35 import org.eclipse.swt.internal.gtk.GdkRectangle;
36 import org.eclipse.swt.internal.gtk.OS;
37 import org.eclipse.swt.graphics.ImageData;
38 import org.eclipse.swt.internal.gtk.GDK;
39 import org.eclipse.swt.internal.gtk.GTK;
3740 import org.eclipse.swt.widgets.*;
3841
3942 import javax.swing.*;
4649 * Tray icon handler based on SWT
4750 */
4851 public class SwtGatewayTray implements DavGatewayTrayInterface {
52 private static final Logger LOGGER = Logger.getLogger(SwtGatewayTray.class);
53
4954 protected SwtGatewayTray() {
5055 }
5156
183188 throw new IOException(fileName);
184189 }
185190 byte[] imageContent = IOUtil.readFully(imageUrl.openStream());
186 result = new Image(display, new ByteArrayInputStream(imageContent));
191 Image tempImage = new Image(display, new ByteArrayInputStream(imageContent));
192 Image backgroundImage = new Image(null, 24, 24);
193 ImageData imageData = backgroundImage.getImageData();
194 imageData.transparentPixel = imageData.getPixel(0, 0);
195 backgroundImage.dispose();
196 result = new Image(null, imageData);
197
198 GC gc = new GC(result);
199 gc.drawImage(tempImage, 4, 4);
200 tempImage.dispose();
187201
188202 } catch (IOException e) {
189203 DavGatewayTray.warn(new BundleMessage("LOG_UNABLE_TO_LOAD_IMAGE"), e);
195209 * Create tray icon and register frame listeners.
196210 */
197211 public void init() {
198 // register error handler to avoid application crash on concurrent X access from SWT and AWT
199 try {
200 OS.gdk_error_trap_push();
201 } catch (NoClassDefFoundError e) {
202 // ignore
212 if (GTK.GTK3) {
213 throw new RuntimeException("GTK 3 not supported, please set SWT_GTK=0");
203214 }
204 final String systemLookAndFeelClassName = UIManager.getSystemLookAndFeelClassName();
215 GDK.gdk_error_trap_push();
205216 try {
206217 // workaround for bug when SWT and AWT both try to access Gtk
207 if (systemLookAndFeelClassName.contains("gtk")) {
208 System.setProperty("swing.defaultlaf", UIManager.getCrossPlatformLookAndFeelClassName());
209 } else {
210 System.setProperty("swing.defaultlaf", systemLookAndFeelClassName);
211 }
218 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
212219 } catch (Exception e) {
213220 DavGatewayTray.warn(new BundleMessage("LOG_UNABLE_TO_SET_LOOK_AND_FEEL"));
214221 }
217224 @Override
218225 public void run() {
219226 try {
220 display = new Display();
227 DeviceData data = new DeviceData();
228 data.debug = true;
229 display = new Display(data);
221230 shell = new Shell(display);
222231
223232 final Tray tray = display.getSystemTray();
225234
226235 trayItem = new TrayItem(tray, SWT.NONE);
227236 trayItem.setToolTipText(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
228
229 // Under Unity, check if tray is indeed available
230 if (systemLookAndFeelClassName.contains("gtk") &&
231 "Unity".equals(System.getenv("XDG_CURRENT_DESKTOP"))) {
232 GdkRectangle area = new GdkRectangle();
233 OS.gtk_status_icon_get_geometry(trayItem.handle, 0, area, 0);
234
235 if (area.x == 0 && area.y == 0) {
236 throw new Error("System tray not available");
237 }
238 }
239237
240238 frameIcons = new ArrayList<java.awt.Image>();
241239 frameIcons.add(DavGatewayTray.loadImage(AwtGatewayTray.TRAY128_PNG));
277275 aboutItem.setText(BundleMessage.format("UI_ABOUT"));
278276 aboutItem.addListener(SWT.Selection, new Listener() {
279277 public void handleEvent(Event event) {
280 display.asyncExec(
278 SwingUtilities.invokeLater(
281279 new Runnable() {
282280 public void run() {
283281 if (aboutFrame == null) {
292290 }
293291 });
294292
293 // create menu item for the default action
295294 trayItem.addListener(SWT.DefaultSelection, new Listener() {
296295 public void handleEvent(Event event) {
297 display.asyncExec(
296 SwingUtilities.invokeLater(
298297 new Runnable() {
299298 public void run() {
300299 // create frame on first call
310309 }
311310 });
312311
313 // create menu item for the default action
314312 MenuItem defaultItem = new MenuItem(popup, SWT.PUSH);
315313 defaultItem.setText(BundleMessage.format("UI_SETTINGS"));
316314 defaultItem.addListener(SWT.Selection, new Listener() {
317315 public void handleEvent(Event event) {
318 display.asyncExec(
316 SwingUtilities.invokeLater(
319317 new Runnable() {
320318 public void run() {
321319 // create frame on first call
335333 logItem.setText(BundleMessage.format("UI_SHOW_LOGS"));
336334 logItem.addListener(SWT.Selection, new Listener() {
337335 public void handleEvent(Event event) {
338 display.asyncExec(
336 SwingUtilities.invokeLater(
339337 new Runnable() {
340338 public void run() {
341339
368366
369367 // display settings frame on first start
370368 if (Settings.isFirstStart()) {
371 // create frame on first call
372 if (settingsFrame == null) {
373 settingsFrame = new SettingsFrame();
374 }
375 settingsFrame.setVisible(true);
376 settingsFrame.toFront();
377 settingsFrame.requestFocus();
369 SwingUtilities.invokeLater(new Runnable() {
370 @Override
371 public void run() {
372 // create frame on first call
373 if (settingsFrame == null) {
374 settingsFrame = new SettingsFrame();
375 }
376 settingsFrame.setVisible(true);
377 settingsFrame.toFront();
378 settingsFrame.requestFocus();
379 }
380 });
381
378382 }
379383
380384 synchronized (mainThread) {
Binary diff not shown
5555 <li>You will have to give your consent to DavMail access on first call,
5656 check davmail log for exact url or use O365Interactive once</li>
5757 <li>You can use your own application client id instead of DavMail provided value,
58 just create your application and add in davmail.properties:<br/>
58 just create your application at <a href="https://apps.dev.microsoft.com/">https://apps.dev.microsoft.com/</a>
59 or directly on Azure AD if you have the rights, and add in davmail.properties:<br/>
5960 <source>
6061 davmail.oauth.clientId=<i>yourappid</i>
6162 davmail.oauth.redirectUri=https://login.microsoftonline.com/common/oauth2/nativeclient
88 </properties>
99
1010 <body>
11 <section name="DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange Gateway">
11 <section name="DavMail POP/IMAP/SMTP/Caldav/Carddav/LDAP Exchange and Office 365 Gateway">
1212 <table class="about" border="0" style="width: 98%">
1313 <tr>
1414 <td>
2020 contacts to allow recipient address completion in mail compose window and full calendar
2121 support with attendees free/busy display.
2222 </p>
23 <p>DavMail also supports the CardDav protocol to sync address books. This new feature
24 is sponsored by
25 <a href="http://www.defense.gouv.fr/dga/">French Defense / DGA</a>
26 through project
27 <a href="http://www.trustedbird.org">Trustedbird</a>
23 <p>DavMail is developed with <a href="http://www.jetbrains.com/idea/?from=DavMail">JetBrains IntelliJ IDEA</a>.
24 YourKit also supports open source projects, including DavMail, with its full-featured
25 <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>.
2826 </p>
2927 <div style="width: 100%;text-align: center">
3028 <img src="images/davmailArchitecture.png" alt="DavMail Architecture" width="550"
4038 on Windows, Linux (Ubuntu) and Mac OSX. Tested successfully with the Iphone
4139 (gateway running on a server).
4240 </p>
41 <p>DavMail CardDav implementation was sponsored by
42 <a href="http://www.defense.gouv.fr/dga/">French Defense / DGA</a>
43 through project <a href="http://www.trustedbird.org">Trustedbird</a>
44 </p>
4345 </td>
4446 <td>
45 <div style="text-align: center">
47 <div style="text-align: center;padding: 8px;">
4648 <script type="text/javascript"
4749 src="https://www.openhub.net/p/davmail/widgets/project_thin_badge?format=js">
4850 </script>
5860 <a class="download" href="download.html">
5961 Download<br/>DavMail<br/>Gateway
6062 </a>
61 <div style='padding: 20px;'>
62 <a href="http://www.jetbrains.com/idea/"
63 style="position: relative;display:block; width:88px; height:31px; border:0; margin:0;padding:0;text-decoration:none;text-indent:0;">
64 <span style="margin: 0;padding: 0;position: absolute;top: -1px;left: 4px;font-size: 10px;cursor:pointer; background-image:none;border:0;color: #0d3a9e; font-family: trebuchet ms,arial,sans-serif;font-weight: normal;text-align:left;">
65 Developed with
66 </span>
67 <img src="http://www.jetbrains.com/idea/opensource/img/all/banners/idea88x31_white.gif"
68 alt="The best Java IDE" border="0"/>
69 </a>
63 <div style='text-align: center;padding: 20px;'>
64 <a target="_blank" href="http://www.jetbrains.com/idea/?from=DavMail" style="background-image: none;position: relative;display:block; height:32px; border:0; margin:0;padding:0;text-decoration:none;text-indent:0;">
65 <span style="margin: 0;padding: 0;font-size: 10px;cursor:pointer; background-image:none;border:0;color: #0d3a9e; font-family: Gotham SSm A, Gotham SSm B, Helvetica, Arial, sans-serif;font-weight: normal;text-align:left;">
66 Developed with
67 </span>
68
69 <span style="white-space: nowrap;font-size: 18px;font-family: Gotham SSm A, Gotham SSm B, Helvetica, Arial, sans-serif">
70 <img src="http://resources.jetbrains.com/storage/products/intellij-idea/img/meta/intellij-idea_logo_300x300.png" width="32" height="32" alt="The best Java IDE" border="0" />
71 IntelliJ IDEA</span>
72 </a>
73 </div>
74 <div style="text-align: center;padding: 20px;">
75 <a target="_blank" style="background-image: none;" href="https://www.yourkit.com/java/profiler/"><img width="128" alt='Yourkit Java Profiler' border='0' src='https://www.yourkit.com/images/yklogo.png'/></a>
7076 </div>
7177 </td>
7278 </tr>
6363 <p>Download the the platform independent package from Sourceforge and uncompress it with
6464 your favorite tool. If you want improved SWT tray icon add the appropriate SWT library in lib
6565 folder from <a href="http://www.eclipse.org/swt/">http://www.eclipse.org/swt/</a>, e.g.
66 swt-4.6.3-gtk-linux-x86_64.jar
66 swt-4.9-gtk-linux-x86_64.jar
6767 </p>
6868 <p>Just run <code>davmail</code> to launch DavMail.
6969 You should now see the DavMail gateway icon in the tray :
2323
2424 <p>The following documentation describes how to run DavMail as a non root user.
2525 The <a href="https://packages.debian.org/sid/davmail">DavMail Debian package</a>
26 and <a href="https://software.opensuse.org//download.html?project=home%3Amguessan%3Adavmail&package=davmail">DavMail RPM package</a>
26 and <a href="https://software.opensuse.org/download.html?project=home%3Amguessan%3Adavmail&amp;package=davmail">DavMail RPM package</a>
2727 include init scripts to run DavMail as a service.
2828 </p>
2929
0 /*
1 * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
2 * Copyright (C) 2010 Mickael Guessant
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 package davmail.exchange;
20
21 import davmail.AbstractDavMailTestCase;
22 import davmail.Settings;
23
24 import java.io.IOException;
25
26 public class TestAuthenticators extends AbstractDavMailTestCase {
27
28 public void testEWSAuthenticator() throws IOException {
29 Settings.setProperty("davmail.mode", Settings.EWS);
30 ExchangeSessionFactory.checkConfig();
31 // try application password for MFA enabledusers
32 ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"),
33 Settings.getProperty("davmail.app.password"));
34 }
35
36 public void testO365Authenticator() throws IOException {
37 Settings.setProperty("davmail.mode", Settings.O365);
38 ExchangeSessionFactory.checkConfig();
39 // try application password for MFA enabledusers
40 ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"),
41 Settings.getProperty("davmail.app.password"));
42 }
43
44 public void testO365ModernAuthenticator() throws IOException {
45 Settings.setProperty("davmail.mode", Settings.O365_MODERN);
46 ExchangeSessionFactory.checkConfig();
47 // use normal user password
48 ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"),
49 Settings.getProperty("davmail.password"));
50 }
51
52 public void testO365InteractiveAuthenticator() throws IOException {
53 Settings.setProperty("davmail.mode", Settings.O365_INTERACTIVE);
54 ExchangeSessionFactory.checkConfig();
55 // password entered by end user
56 ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"),
57 "unused");
58 }
59 }
132132 assertNotNull(folder);
133133 }
134134
135 public void testCreateSlashFolder() throws IOException {
136 String folderName = "test_xF8FF_slash";
137 session.deleteFolder(folderName);
138 session.createMessageFolder(folderName);
139 ExchangeSession.Folder folder = session.getFolder(folderName);
140 assertEquals(folderName, folder.displayName);
141 }
142
135143 }