ews-java-api icon indicating copy to clipboard operation
ews-java-api copied to clipboard

Autodiscover fails if POST /autodiscover/autodiscover.xml is used instead of GET /autodiscover/autodiscover.xml

Open mbialkowskigmc opened this issue 9 years ago • 11 comments

Hi,

I have client application using cloud based Office 365. After upgrade to the ews-java-api 2.0 from previously used Microsoft's EWSJavaAPI 1.2 library, autodiscover stopped working correctly.

After analysing logs I found that in the autodiscover process the previous EWS library was making a GET request to autodiscover service :

2015-11-04 06:39:02.066-08 | FINE | Startup-1_31_6 | org.apache.commons.httpclient.Wire | wire | >> "GET /autodiscover/autodiscover.xml HTTP/1.1[\r][\n]" 2015-11-04 06:39:02.144-08 | FINE | Startup-1_31_6 | org.apache.commons.httpclient.Wire | wire | << "HTTP/1.1 200 OK[\r][\n]" 2015-11-04 06:39:02.144-08 | FINE | Startup-1_31_6 | org.apache.commons.httpclient.Wire | wire | << "X-SOAP-Enabled: True[\r][\n]" 2015-11-04 06:39:02.144-08 | FINE | Startup-1_31_6 | org.apache.commons.httpclient.Wire | wire | << "X-WSSecurity-Enabled: True[\r][\n]"

the ews-java-api 2.0 is using POST instead of GET :

2015-11-04 06:04:24.626-08 | FINE | Startup-1_30_6 | org.apache.http.wire | wire | http-outgoing-3 >> "POST /autodiscover/autodiscover.xml HTTP/1.1[\r][\n]" 2015-11-04 06:04:24.736-08 | FINE | Startup-1_30_6 | org.apache.http.wire | wire | http-outgoing-3 << "HTTP/1.1 200 OK[\r][\n]"

The response for POST to POST /autodiscover/autodiscover.xml is also HTTP 200 OK, however the response is missing the X-SOAP-Enabled: True and X-WSSecurity-Enabled: True headers. Because of that, the ews-java-api assumes only Legacy endpoint is enabled

2015-11-04 06:04:24.782-08 | FINEST | Startup-1_30_6 | microsoft.exchange.webservices.data.misc.EwsTraceListener | trace | AutodiscoverConfiguration - <Trace Tag="AutodiscoverConfiguration" Tid="19" Time="2015-11-04 14:04:24Z">&#xd; Host returned enabled endpoint flags: [Legacy]&#xd; </Trace>

and reports error: microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverLocalException: The Autodiscover service couldn't be located.

When changed the code to use GET instead of POST in AutodiscoverService.tryGetEnabledEndpointsForHost() , things started to work correctly

<Trace Tag="AutodiscoverConfiguration" Tid="19" Time="2015-11-06 10:25:29Z">&#xd; Host returned enabled endpoint flags: [Legacy, Soap, WsSecurity]&#xd; </Trace>

note also that AutodiscoverService.java:1522 has request.setRequestMethod("GET"); however HttpClientWebRequest.java:102 is always using POST request httpPost = new HttpPost(getUrl().toString());

mbialkowskigmc avatar Nov 06 '15 12:11 mbialkowskigmc

Do you have a code sample of what you were using to test the AutoDiscover service? I tried this myself against Office365 using v.2.0 and it worked, but was very slow. (Took around 30 seconds.)

avromf avatar Nov 06 '15 15:11 avromf

I dont have isolated test code, only app. code, relevant fragments are:

AutodiscoverService autodiscoverService = new AutodiscoverService(exchangeVersion); //2010
autodiscoverService.setCredentials(exchangeCredentials);
WebProxy proxy = createWebProxy(proxyConfiguration);
            autodiscoverService.setWebProxy(proxy);

response = autodiscoverService.getUserSettings(userSmtpAddress, InterestedUserSettings);
Map<UserSettingName, Object> userSettingsMap = response.getSettings();

        if (autodiscoverService.isExternal()) {
            EWSUrl = (String) userSettingsMap
                    .get(UserSettingName.ExternalEwsUrl);
        } else {
            EWSUrl = (String) userSettingsMap
                    .get(UserSettingName.InternalEwsUrl);
        }
return EWSUrl;

--------------------

private static final UserSettingName[] InterestedUserSettings = new UserSettingName[] {
            UserSettingName.InternalEwsUrl, UserSettingName.ExternalEwsUrl,
            UserSettingName.UserDN, UserSettingName.UserDisplayName,
            UserSettingName.MailboxDN, UserSettingName.CasVersion,
            UserSettingName.InternalRpcClientServer,
            UserSettingName.InternalMailboxServerDN};


public static WebProxy createWebProxy(ProxyConfiguration proxyConfiguration) {
        if (proxyConfiguration.getCredentials() != null && !StringUtil.isEmptyOrNull(proxyConfiguration.getCredentials().getUsername())) {
            return new WebProxy(proxyConfiguration.getHostname(), proxyConfiguration.getPort(),
                    new WebProxyCredentials(proxyConfiguration.getCredentials().getUsername(),
                            proxyConfiguration.getCredentials().getPassword(), null));
        } else {
            return new WebProxy(proxyConfiguration.getHostname(), proxyConfiguration.getPort());
        }
    }

mbialkowskigmc avatar Nov 06 '15 15:11 mbialkowskigmc

the "fix" I made was

public class HttpClientWebRequest extends HttpWebRequest {
...
private HttpRequestBase httpPost = null;
...
public void prepareConnection() {
    httpPost = "GET".equals(this.getRequestMethod()) ? new HttpGet(getUrl().toString()) : new HttpPost(getUrl().toString());

mbialkowskigmc avatar Nov 06 '15 15:11 mbialkowskigmc

Interesting.

I'm using a different method to look up the URL.

Here is my code:

public static URI autoDiscoverURI(String emailAddress, String password)
{
    URI url = null;
    ExchangeService service = new ExchangeService();
    ExchangeCredentials credentials = new WebCredentials(emailAddress, password);
    service.setCredentials(credentials);
    try
    {
        service.autodiscoverUrl(emailAddress, new IAutodiscoverRedirectionUrl()
        {
            @Override
            public boolean autodiscoverRedirectionUrlValidationCallback(String url) throws AutodiscoverLocalException
            {
                return url.startsWith("https:");
            }
        });
        url = service.getUrl();
    }
    catch (Exception e)
    {
        System.out.println(e.toString());
    }
    finally
    {
        service.close();
    }
    return url;
}

When I tried a variation of your code here, it resulted in the same error you got.

avromf avatar Nov 06 '15 18:11 avromf

We are also running into this issue with Office365 accounts.

dconnelly avatar Nov 15 '15 20:11 dconnelly

I've managed to reproduce this as well and also confirmed that mbialkowskigmc's fix works correct. From the code it definitely appears that the intended request is GET but this is overridden by the default POST request which does not result in the X-SOAP-Enabled being set in the response. This causes AutodiscoverService to fallback to the legacy endpoint which also slows down the autodiscover process noticeably. I'm not familiar enough with the whole autodiscover process (seems like black magic to me and not well documented) but it definitely seems like a GET request was intended, and this is what the C# managed api seems to be doing a well.

I can submit a pull request.

dconnelly avatar Nov 16 '15 23:11 dconnelly

I have same issue. EWSEditor.exe shows me:

Ordered List of Autodiscover endpoints:

https://exchange2010.intern.mycompany.com/autodiscover/autodiscover.xml

I have same code which avromf had post. Additionaly i´ve imported cer-certificate into keystore. Now it looks like that:


Properties systemProps = System.getProperties();
systemProps.put("javax.net.ssl.trustStore",
              "C:/Program Files/Java/jdk1.8.0/jre/lib/security/cacerts");
systemProps.put("javax.net.ssl.trustStorePassword","changeit");
System.setProperties(systemProps);
service = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
ExchangeCredentials credentials = new WebCredentials("FirstnameSecondname", "myPW","EXCHANGE2010.intern.mycompany.com");
service.setCredentials(credentials);
service.setTraceEnabled(true);
service.setTraceFlags(EnumSet.allOf(TraceFlags.class)); 
service.setTraceListener(new ITraceListener() {
            public void trace(String traceType, String traceMessage) {
                System.out.println("Type:" + traceType + " Message:" + traceMessage);
            }
        });
        service.autodiscoverUrl("[email protected]", new                       IAutodiscoverRedirectionUrl() {
                public boolean autodiscoverRedirectionUrlValidationCallback(String url)
                        throws AutodiscoverLocalException {
                            System.out.println("url: " + url.toLowerCase().startsWith("https://"));
                    return url.toLowerCase().startsWith("https://");
                }
            });

But it does not work :( Trace:

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:11Z"> Determining which endpoints are enabled for host mycompany.com </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> Host returned enabled endpoint flags: [Legacy] </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> No Autodiscover endpoints are available for host mycompany.com </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> Determining which endpoints are enabled for host autodiscover.mycompany.com </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> No Autodiscover endpoints are available for host autodiscover.mycompany.com </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> Trying to get Autodiscover redirection URL from http://autodiscover.mycompany.com/autodiscover/autodiscover.xml. </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> No Autodiscover redirection URL was returned. </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:16Z"> Trying to get Autodiscover host from DNS SRV record for mycompany.com. </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:17Z"> DnsQuery returned error 'DNS name not found [response code 3]'. </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:17Z"> No appropriate SRV record was found. </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:11:17Z"> No matching Autodiscover DNS SRV records were found. </Trace>

Type:AutodiscoverResponse Message:<Trace Tag="AutodiscoverResponse" Tid="1" Time="2016-09-09 12:11:43Z"> Autodiscover service call failed with error 'The Autodiscover service couldn't be located.'. Will try legacy service </Trace>

Type:AutodiscoverConfiguration Message:<Trace Tag="AutodiscoverConfiguration" Tid="1" Time="2016-09-09 12:12:00Z"> Trying to call Autodiscover for [email protected] on https://mycompany.com/autodiscover/autodiscover.xml. </Trace>

Type:AutodiscoverRequestHttpHeaders Message:<Trace Tag="AutodiscoverRequestHttpHeaders" Tid="1" Time="2016-09-09 12:12:00Z"> POST /autodiscover/autodiscover.xml HTTP/1.1 Keep-Alive : 300 Content-type : text/xml; charset=utf-8 Accept : text/xml User-Agent : ExchangeServicesClient/0.0.0.0 Connection : Keep-Alive

sVeloper avatar Sep 09 '16 12:09 sVeloper

Hello,

Could you show piece of your code? And what is Java version you use?

pkropachev avatar Nov 26 '18 20:11 pkropachev

Hello,

Could you show piece of your code? And what is Java version you use?

Hi pkropachev. I think I fixed my problem. Thanks!

dan-oneil avatar Nov 30 '18 13:11 dan-oneil

Hello @dan-oneil ! Sorry, I didn't have time to check it. Could you share your solution?

pkropachev avatar Dec 01 '18 18:12 pkropachev

I realized the credentials were being set after the call was made. I made the call after setting credentials in the class that extended ExchangeServer and all is well! Thanks :-)

dan-oneil avatar Dec 03 '18 14:12 dan-oneil