microsoft.exchange.webservices.data.autodiscover.AutodiscoverService Maven / Gradle / Ivy
/*
* The MIT License
* Copyright (c) 2012 Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package microsoft.exchange.webservices.data.autodiscover;
import microsoft.exchange.webservices.data.autodiscover.configuration.ConfigurationSettingsBase;
import microsoft.exchange.webservices.data.autodiscover.configuration.outlook.OutlookConfigurationSettings;
import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverEndpoints;
import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverErrorCode;
import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverLocalException;
import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverRemoteException;
import microsoft.exchange.webservices.data.autodiscover.request.AutodiscoverRequest;
import microsoft.exchange.webservices.data.autodiscover.request.GetDomainSettingsRequest;
import microsoft.exchange.webservices.data.autodiscover.request.GetUserSettingsRequest;
import microsoft.exchange.webservices.data.autodiscover.response.GetDomainSettingsResponse;
import microsoft.exchange.webservices.data.autodiscover.response.GetDomainSettingsResponseCollection;
import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponse;
import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponseCollection;
import microsoft.exchange.webservices.data.core.EwsUtilities;
import microsoft.exchange.webservices.data.core.EwsXmlReader;
import microsoft.exchange.webservices.data.core.ExchangeServiceBase;
import microsoft.exchange.webservices.data.core.request.HttpClientWebRequest;
import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
import microsoft.exchange.webservices.data.credential.WSSecurityBasedCredentials;
import microsoft.exchange.webservices.data.autodiscover.enumeration.DomainSettingName;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
import microsoft.exchange.webservices.data.autodiscover.enumeration.UserSettingName;
import microsoft.exchange.webservices.data.core.exception.misc.ArgumentException;
import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
import microsoft.exchange.webservices.data.core.exception.misc.FormatException;
import microsoft.exchange.webservices.data.autodiscover.exception.MaximumRedirectionHopsExceededException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException;
import microsoft.exchange.webservices.data.misc.OutParam;
import microsoft.exchange.webservices.data.security.XmlNodeType;
import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
/**
* Represents a binding to the Exchange Autodiscover Service.
*/
public class AutodiscoverService extends ExchangeServiceBase
implements IAutodiscoverRedirectionUrl, IFunctionDelegate {
// region Private members
/**
* The domain.
*/
private String domain;
/**
* The is external.
*/
private Boolean isExternal = true;
/**
* The url.
*/
private URI url;
/**
* The redirection url validation callback.
*/
private IAutodiscoverRedirectionUrl
redirectionUrlValidationCallback;
/**
* The dns client.
*/
private AutodiscoverDnsClient dnsClient;
/**
* The dns server address.
*/
private String dnsServerAddress;
/**
* The enable scp lookup.
*/
private boolean enableScpLookup = true;
// Autodiscover legacy path
/**
* The Constant AutodiscoverLegacyPath.
*/
private static final String AutodiscoverLegacyPath =
"/autodiscover/autodiscover.xml";
// Autodiscover legacy HTTPS Url
/**
* The Constant AutodiscoverLegacyHttpsUrl.
*/
private static final String AutodiscoverLegacyHttpsUrl = "https://%s" +
AutodiscoverLegacyPath;
// Autodiscover legacy HTTP Url
/**
* The Constant AutodiscoverLegacyHttpUrl.
*/
private static final String AutodiscoverLegacyHttpUrl = "http://%s" +
AutodiscoverLegacyPath;
// Autodiscover SOAP HTTPS Url
/**
* The Constant AutodiscoverSoapHttpsUrl.
*/
private static final String AutodiscoverSoapHttpsUrl =
"https://%s/autodiscover/autodiscover.svc";
// Autodiscover SOAP WS-Security HTTPS Url
/**
* The Constant AutodiscoverSoapWsSecurityHttpsUrl.
*/
private static final String AutodiscoverSoapWsSecurityHttpsUrl =
AutodiscoverSoapHttpsUrl +
"/wssecurity";
/**
* Autodiscover SOAP WS-Security symmetrickey HTTPS Url
*/
private static final String AutodiscoverSoapWsSecuritySymmetricKeyHttpsUrl =
AutodiscoverSoapHttpsUrl + "/wssecurity/symmetrickey";
/**
* Autodiscover SOAP WS-Security x509cert HTTPS Url
*/
private static final String AutodiscoverSoapWsSecurityX509CertHttpsUrl =
AutodiscoverSoapHttpsUrl + "/wssecurity/x509cert";
// Autodiscover request namespace
/**
* The Constant AutodiscoverRequestNamespace.
*/
private static final String AutodiscoverRequestNamespace =
"http://schemas.microsoft.com/exchange/autodiscover/" +
"outlook/requestschema/2006";
// Maximum number of Url (or address) redirections that will be followed by
// an Autodiscover call
/**
* The Constant AutodiscoverMaxRedirections.
*/
protected static final int AutodiscoverMaxRedirections = 10;
// HTTP header indicating that SOAP Autodiscover service is enabled.
/**
* The Constant AutodiscoverSoapEnabledHeaderName.
*/
private static final String AutodiscoverSoapEnabledHeaderName =
"X-SOAP-Enabled";
// HTTP header indicating that WS-Security Autodiscover service is enabled.
/**
* The Constant AutodiscoverWsSecurityEnabledHeaderName.
*/
private static final String AutodiscoverWsSecurityEnabledHeaderName =
"X-WSSecurity-Enabled";
/**
* HTTP header indicating that WS-Security/SymmetricKey Autodiscover service is enabled.
*/
private static final String AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName =
"X-WSSecurity-SymmetricKey-Enabled";
/**
* HTTP header indicating that WS-Security/X509Cert Autodiscover service is enabled.
*/
private static final String AutodiscoverWsSecurityX509CertEnabledHeaderName =
"X-WSSecurity-X509Cert-Enabled";
// Minimum request version for Autodiscover SOAP service.
/**
* The Constant MinimumRequestVersionForAutoDiscoverSoapService.
*/
private static final ExchangeVersion
MinimumRequestVersionForAutoDiscoverSoapService =
ExchangeVersion.Exchange2010;
/**
* Default implementation of AutodiscoverRedirectionUrlValidationCallback.
* Always returns true indicating that the URL can be used.
*
* @param redirectionUrl the redirection url
* @return Returns true.
* @throws AutodiscoverLocalException the autodiscover local exception
*/
private boolean defaultAutodiscoverRedirectionUrlValidationCallback(
String redirectionUrl) throws AutodiscoverLocalException {
throw new AutodiscoverLocalException(String.format(
"Autodiscover blocked a potentially insecure redirection to %s. To allow Autodiscover to follow the "
+ "redirection, use the AutodiscoverUrl(string, AutodiscoverRedirectionUrlValidationCallback) "
+ "overload.", redirectionUrl));
}
// Legacy Autodiscover
/**
* Calls the Autodiscover service to get configuration settings at the
* specified URL.
*
* @param the generic type
* @param cls the cls
* @param emailAddress the email address
* @param url the url
* @return The requested configuration settings. (TSettings The type of the
* settings to retrieve)
* @throws Exception the exception
*/
private
TSettings getLegacyUserSettingsAtUrl(
Class cls, String emailAddress, URI url)
throws Exception {
this
.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Trying to call Autodiscover for %s on %s.", emailAddress, url));
TSettings settings = cls.newInstance();
HttpWebRequest request = null;
try {
request = this.prepareHttpWebRequestForUrl(url);
this.traceHttpRequestHeaders(
TraceFlags.AutodiscoverRequestHttpHeaders,
request);
// OutputStreamWriter out = new
// OutputStreamWriter(request.getOutputStream());
OutputStream urlOutStream = request.getOutputStream();
// If tracing is enabled, we generate the request in-memory so that we
// can pass it along to the ITraceListener. Then we copy the stream to
// the request stream.
if (this.isTraceEnabledFor(TraceFlags.AutodiscoverRequest)) {
ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(memoryStream);
this.writeLegacyAutodiscoverRequest(emailAddress, settings, writer);
writer.flush();
this.traceXml(TraceFlags.AutodiscoverRequest, memoryStream);
// out.write(memoryStream.toString());
// out.close();
memoryStream.writeTo(urlOutStream);
urlOutStream.flush();
urlOutStream.close();
memoryStream.close();
} else {
PrintWriter writer = new PrintWriter(urlOutStream);
this.writeLegacyAutodiscoverRequest(emailAddress, settings, writer);
/* Flush Start */
writer.flush();
urlOutStream.flush();
urlOutStream.close();
/* Flush End */
}
request.executeRequest();
request.getResponseCode();
URI redirectUrl;
OutParam outParam = new OutParam();
if (this.tryGetRedirectionResponse(request, outParam)) {
redirectUrl = outParam.getParam();
settings.makeRedirectionResponse(redirectUrl);
return settings;
}
InputStream serviceResponseStream = request.getInputStream();
// If tracing is enabled, we read the entire response into a
// MemoryStream so that we
// can pass it along to the ITraceListener. Then we parse the response
// from the
// MemoryStream.
if (this.isTraceEnabledFor(TraceFlags.AutodiscoverResponse)) {
ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
while (true) {
int data = serviceResponseStream.read();
if (-1 == data) {
break;
} else {
memoryStream.write(data);
}
}
memoryStream.flush();
this.traceResponse(request, memoryStream);
ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(
memoryStream.toByteArray());
EwsXmlReader reader = new EwsXmlReader(memoryStreamIn);
reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
settings.loadFromXml(reader);
} else {
EwsXmlReader reader = new EwsXmlReader(serviceResponseStream);
reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
settings.loadFromXml(reader);
}
serviceResponseStream.close();
} finally {
if (request != null) {
try {
request.close();
} catch (Exception e2) {
// Ignore exception while closing the request.
}
}
}
return settings;
}
/**
* Writes the autodiscover request.
*
* @param emailAddress the email address
* @param settings the settings
* @param writer the writer
* @throws java.io.IOException Signals that an I/O exception has occurred.
*/
private void writeLegacyAutodiscoverRequest(String emailAddress,
ConfigurationSettingsBase settings, PrintWriter writer)
throws IOException {
writer.write(String.format("", AutodiscoverRequestNamespace));
writer.write("");
writer.write(String.format("%s ",
emailAddress));
writer.write(
String.format("%s ", settings.getNamespace()));
writer.write(" ");
writer.write(" ");
}
/**
* Gets a redirection URL to an SSL-enabled Autodiscover service from the
* standard non-SSL Autodiscover URL.
*
* @param domainName the domain name
* @return A valid SSL-enabled redirection URL. (May be null)
* @throws EWSHttpException the EWS http exception
* @throws XMLStreamException the XML stream exception
* @throws IOException Signals that an I/O exception has occurred.
* @throws ServiceLocalException the service local exception
* @throws URISyntaxException the uRI syntax exception
*/
private URI getRedirectUrl(String domainName)
throws EWSHttpException, XMLStreamException, IOException, ServiceLocalException, URISyntaxException {
String url = String.format(AutodiscoverLegacyHttpUrl, "autodiscover." + domainName);
traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Trying to get Autodiscover redirection URL from %s.", url));
HttpWebRequest request = null;
try {
request = new HttpClientWebRequest(httpClient, httpContext);
request.setProxy(getWebProxy());
try {
request.setUrl(URI.create(url).toURL());
} catch (MalformedURLException e) {
String strErr = String.format("Incorrect format : %s", url);
throw new ServiceLocalException(strErr);
}
request.setRequestMethod("GET");
request.setAllowAutoRedirect(false);
request.setTimeout(getTimeout());
// Do NOT allow authentication as this single request will be made over plain HTTP.
request.setAllowAuthentication(false);
prepareCredentials(request);
request.prepareConnection();
try {
request.executeRequest();
} catch (IOException e) {
traceMessage(TraceFlags.AutodiscoverConfiguration, "No Autodiscover redirection URL was returned.");
return null;
}
OutParam outParam = new OutParam();
if (tryGetRedirectionResponse(request, outParam)) {
return outParam.getParam();
}
} finally {
if (request != null) {
try {
request.close();
} catch (Exception e) {
// Ignore exception when closing the request
}
}
}
traceMessage(TraceFlags.AutodiscoverConfiguration, "No Autodiscover redirection URL was returned.");
return null;
}
/**
* Tries the get redirection response.
*
* @param request the request
* @param redirectUrl the redirect URL
* @return true if a valid redirection URL was found
* @throws XMLStreamException the XML stream exception
* @throws IOException signals that an I/O exception has occurred.
* @throws EWSHttpException the EWS http exception
*/
private boolean tryGetRedirectionResponse(HttpWebRequest request,
OutParam redirectUrl) throws XMLStreamException, IOException,
EWSHttpException {
// redirectUrl = null;
if (AutodiscoverRequest.isRedirectionResponse(request)) {
// Get the redirect location and verify that it's valid.
String location = request.getResponseHeaderField("Location");
if (!(location == null || location.isEmpty())) {
try {
redirectUrl.setParam(new URI(location));
// Check if URL is SSL and that the path matches.
if ((redirectUrl.getParam().getScheme().toLowerCase()
.equals("https")) &&
(redirectUrl.getParam().getPath()
.equalsIgnoreCase(
AutodiscoverLegacyPath))) {
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Redirection URL found: '%s'",
redirectUrl.getParam().toString()));
return true;
}
} catch (URISyntaxException ex) {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"Invalid redirection URL " +
"was returned: '%s'",
location));
return false;
}
}
}
return false;
}
/**
* Calls the legacy Autodiscover service to retrieve configuration settings.
*
* @param the generic type
* @param cls the cls
* @param emailAddress The email address to retrieve configuration settings for.
* @return The requested configuration settings.
* @throws Exception the exception
*/
protected
TSettings getLegacyUserSettings(
Class cls, String emailAddress) throws Exception {
/*int currentHop = 1;
return this.internalGetConfigurationSettings(cls, emailAddress,
currentHop);*/
// If Url is specified, call service directly.
if (this.url != null) {
// this.Uri is intended for Autodiscover SOAP service, convert to Legacy endpoint URL.
URI autodiscoverUrl = new URI(this.url.toString() + AutodiscoverLegacyPath);
return this.getLegacyUserSettingsAtUrl(cls, emailAddress, autodiscoverUrl);
}
// If Domain is specified, figure out the endpoint Url and call service.
else if (!(this.domain == null || this.domain.isEmpty())) {
URI autodiscoverUrl = new URI(String.format(AutodiscoverLegacyHttpsUrl, this.domain));
return this.getLegacyUserSettingsAtUrl(cls,
emailAddress, autodiscoverUrl);
} else {
// No Url or Domain specified, need to
//figure out which endpoint to use.
int currentHop = 1;
OutParam outParam = new OutParam();
outParam.setParam(currentHop);
List redirectionEmailAddresses = new ArrayList();
return this.internalGetLegacyUserSettings(
cls,
emailAddress,
redirectionEmailAddresses,
outParam);
}
}
/**
* Calls the Autodiscover service to retrieve configuration settings.
*
* @param the generic type
* @param cls the cls
* @param emailAddress The email address to retrieve configuration settings for.
* @param currentHop Current number of redirection urls/addresses attempted so far.
* @return The requested configuration settings.
* @throws Exception the exception
*/
private
TSettings internalGetLegacyUserSettings(
Class cls,
String emailAddress,
List redirectionEmailAddresses,
OutParam currentHop)
throws Exception {
String domainName = EwsUtilities.domainFromEmailAddress(emailAddress);
int scpUrlCount;
OutParam outParamInt = new OutParam();
List urls = this.getAutodiscoverServiceUrls(domainName, outParamInt);
scpUrlCount = outParamInt.getParam();
if (urls.size() == 0) {
throw new ServiceValidationException(
"This Autodiscover request requires that either the Domain or Url be specified.");
}
// Assume caller is not inside the Intranet, regardless of whether SCP
// Urls
// were returned or not. SCP Urls are only relevent if one of them
// returns
// valid Autodiscover settings.
this.isExternal = true;
int currentUrlIndex = 0;
// Used to save exception for later reporting.
Exception delayedException = null;
TSettings settings;
do {
URI autodiscoverUrl = urls.get(currentUrlIndex);
boolean isScpUrl = currentUrlIndex < scpUrlCount;
try {
settings = this.getLegacyUserSettingsAtUrl(cls,
emailAddress, autodiscoverUrl);
switch (settings.getResponseType()) {
case Success:
// Not external if Autodiscover endpoint found via SCP
// returned the settings.
if (isScpUrl) {
this.isExternal = false;
}
this.url = autodiscoverUrl;
return settings;
case RedirectUrl:
if (currentHop.getParam() < AutodiscoverMaxRedirections) {
currentHop.setParam(currentHop.getParam() + 1);
this
.traceMessage(
TraceFlags.AutodiscoverResponse,
String
.format(
"Autodiscover " +
"service " +
"returned " +
"redirection URL '%s'.",
settings
.getRedirectTarget()));
urls.add(currentUrlIndex, new URI(
settings.getRedirectTarget()));
break;
} else {
throw new MaximumRedirectionHopsExceededException();
}
case RedirectAddress:
if (currentHop.getParam() < AutodiscoverMaxRedirections) {
currentHop.setParam(currentHop.getParam() + 1);
this
.traceMessage(
TraceFlags.AutodiscoverResponse,
String
.format(
"Autodiscover " +
"service " +
"returned " +
"redirection email " +
"address '%s'.",
settings
.getRedirectTarget()));
// Bug E14:255576 If this email address was already tried, we may have a loop
// in SCP lookups. Disable consideration of SCP records.
this.disableScpLookupIfDuplicateRedirection(
settings.getRedirectTarget(),
redirectionEmailAddresses);
return this.internalGetLegacyUserSettings(cls,
settings.getRedirectTarget(),
redirectionEmailAddresses,
currentHop);
} else {
throw new MaximumRedirectionHopsExceededException();
}
case Error:
// Don't treat errors from an SCP-based Autodiscover service
// to be conclusive.
// We'll try the next one and record the error for later.
if (isScpUrl) {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
"Error returned by " +
"Autodiscover service " +
"found via SCP, treating " +
"as inconclusive.");
delayedException = new AutodiscoverRemoteException(
"The Autodiscover service returned an error.", settings.getError());
currentUrlIndex++;
} else {
throw new AutodiscoverRemoteException("The Autodiscover service returned an error.", settings.getError());
}
break;
default:
EwsUtilities
.ewsAssert(false, "Autodiscover.GetConfigurationSettings",
"An unexpected error has occured. This code path should never be reached.");
break;
}
} catch (XMLStreamException ex) {
this.traceMessage(TraceFlags.AutodiscoverConfiguration, String
.format("%s failed: XML parsing error: %s", url, ex
.getMessage()));
// The content at the URL wasn't a valid response, let's try the
// next.
currentUrlIndex++;
} catch (IOException ex) {
this.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("%s failed: I/O error: %s",
url, ex.getMessage()));
// The content at the URL wasn't a valid response, let's try the next.
currentUrlIndex++;
} catch (Exception ex) {
HttpWebRequest response = null;
URI redirectUrl;
OutParam outParam1 = new OutParam();
if ((response != null) &&
this.tryGetRedirectionResponse(response, outParam1)) {
redirectUrl = outParam1.getParam();
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format(
"Host returned a redirection to url %s",
redirectUrl.toString()));
currentHop.setParam(currentHop.getParam() + 1);
urls.add(currentUrlIndex, redirectUrl);
} else {
if (response != null) {
this.processHttpErrorResponse(response, ex);
}
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("%s failed: %s (%s)", url, ex
.getClass().getName(), ex.getMessage()));
// The url did not work, let's try the next.
currentUrlIndex++;
}
}
} while (currentUrlIndex < urls.size());
// If we got this far it's because none of the URLs we tried have
// worked. As a next-to-last chance, use GetRedirectUrl to
// try to get a redirection URL using an HTTP GET on a non-SSL
// Autodiscover endpoint. If successful, use this
// redirection URL to get the configuration settings for this email
// address. (This will be a common scenario for
// DataCenter deployments).
URI redirectionUrl = this.getRedirectUrl(domainName);
OutParam outParam = new OutParam();
if ((redirectionUrl != null)
&& this.tryLastChanceHostRedirection(cls, emailAddress,
redirectionUrl, outParam)) {
settings = outParam.getParam();
return settings;
} else {
// Getting a redirection URL from an HTTP GET failed too. As a last
// chance, try to get an appropriate SRV Record
// using DnsQuery. If successful, use this redirection URL to get
// the configuration settings for this email address.
redirectionUrl = this.getRedirectionUrlFromDnsSrvRecord(domainName);
if ((redirectionUrl != null)
&& this.tryLastChanceHostRedirection(cls, emailAddress,
redirectionUrl, outParam)) {
return outParam.getParam();
}
// If there was an earlier exception, throw it.
if (delayedException != null) {
throw delayedException;
}
throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
}
}
/**
* Get an autodiscover SRV record in DNS and construct autodiscover URL.
*
* @param domainName Name of the domain.
* @return Autodiscover URL (may be null if lookup failed)
* @throws Exception the exception
*/
protected URI getRedirectionUrlFromDnsSrvRecord(String domainName)
throws Exception {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"Trying to get Autodiscover host " +
"from DNS SRV record for %s.",
domainName));
String hostname = this.dnsClient
.findAutodiscoverHostFromSrv(domainName);
if (!(hostname == null || hostname.isEmpty())) {
this
.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format(
"Autodiscover host %s was returned.",
hostname));
return new URI(String.format(AutodiscoverLegacyHttpsUrl,
hostname));
} else {
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
"No matching Autodiscover DNS SRV records were found.");
return null;
}
}
/**
* Tries to get Autodiscover settings using redirection Url.
*
* @param the generic type
* @param cls the cls
* @param emailAddress The email address.
* @param redirectionUrl Redirection Url.
* @param settings The settings.
* @return boolean The boolean.
* @throws AutodiscoverLocalException the autodiscover local exception
* @throws AutodiscoverRemoteException the autodiscover remote exception
* @throws Exception the exception
*/
private boolean
tryLastChanceHostRedirection(
Class cls, String emailAddress, URI redirectionUrl,
OutParam settings) throws AutodiscoverLocalException,
AutodiscoverRemoteException, Exception {
List redirectionEmailAddresses = new ArrayList();
// Bug 60274: Performing a non-SSL HTTP GET to retrieve a redirection
// URL is potentially unsafe. We allow the caller
// to specify delegate to be called to determine whether we are allowed
// to use the redirection URL.
if (this
.callRedirectionUrlValidationCallback(redirectionUrl.toString())) {
for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
try {
settings.setParam(this.getLegacyUserSettingsAtUrl(cls,
emailAddress, redirectionUrl));
switch (settings.getParam().getResponseType()) {
case Success:
return true;
case Error:
throw new AutodiscoverRemoteException("The Autodiscover service returned an error.", settings.getParam()
.getError());
case RedirectAddress:
// If this email address was already tried,
//we may have a loop
// in SCP lookups. Disable consideration of SCP records.
this.disableScpLookupIfDuplicateRedirection(settings.getParam().getRedirectTarget(),
redirectionEmailAddresses);
OutParam outParam = new OutParam();
outParam.setParam(currentHop);
settings.setParam(
this.internalGetLegacyUserSettings(cls,
emailAddress,
redirectionEmailAddresses,
outParam));
currentHop = outParam.getParam();
return true;
case RedirectUrl:
try {
redirectionUrl = new URI(settings.getParam()
.getRedirectTarget());
} catch (URISyntaxException ex) {
this
.traceMessage(
TraceFlags.
AutodiscoverConfiguration,
String
.format(
"Service " +
"returned " +
"invalid " +
"redirection " +
"URL %s",
settings
.getParam()
.getRedirectTarget()));
return false;
}
break;
default:
String failureMessage = String.format(
"Autodiscover call at %s failed with error %s, target %s",
redirectionUrl,
settings.getParam().getResponseType(),
settings.getParam().getRedirectTarget());
this.traceMessage(
TraceFlags.AutodiscoverConfiguration, failureMessage);
return false;
}
} catch (XMLStreamException ex) {
// If the response is malformed, it wasn't a valid
// Autodiscover endpoint.
this
.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format(
"%s failed: XML parsing error: %s",
redirectionUrl.toString(), ex
.getMessage()));
return false;
} catch (IOException ex) {
this.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("%s failed: I/O error: %s",
redirectionUrl, ex.getMessage()));
return false;
} catch (Exception ex) {
// TODO: BUG response is always null
HttpWebRequest response = null;
OutParam outParam = new OutParam();
if ((response != null)
&& this.tryGetRedirectionResponse(response,
outParam)) {
redirectionUrl = outParam.getParam();
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"Host returned a " +
"redirection" +
" to url %s",
redirectionUrl));
} else {
if (response != null) {
this.processHttpErrorResponse(response, ex);
}
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("%s failed: %s (%s)",
url, ex.getClass().getName(),
ex.getMessage()));
return false;
}
}
}
}
return false;
}
/**
* Disables SCP lookup if duplicate email address redirection.
*
* @param emailAddress The email address to use.
* @param redirectionEmailAddresses The list of prior redirection email addresses.
*/
private void disableScpLookupIfDuplicateRedirection(
String emailAddress,
List redirectionEmailAddresses) {
// SMTP addresses are case-insensitive so entries are converted to lower-case.
emailAddress = emailAddress.toLowerCase();
if (redirectionEmailAddresses.contains(emailAddress)) {
this.enableScpLookup = false;
} else {
redirectionEmailAddresses.add(emailAddress);
}
}
/**
* Gets user settings from Autodiscover legacy endpoint.
*
* @param emailAddress The email address to use.
* @param requestedSettings The requested settings.
* @return GetUserSettingsResponse
* @throws Exception on error
*/
protected GetUserSettingsResponse internalGetLegacyUserSettings(
String emailAddress,
List requestedSettings) throws Exception {
// Cannot call legacy Autodiscover service with WindowsLive and other WSSecurity-based credential
if ((this.getCredentials() != null) && (this.getCredentials() instanceof WSSecurityBasedCredentials)) {
throw new AutodiscoverLocalException(
"WindowsLiveCredentials can't be used with this Autodiscover endpoint.");
}
OutlookConfigurationSettings settings = this.getLegacyUserSettings(
OutlookConfigurationSettings.class,
emailAddress);
return settings.convertSettings(emailAddress, requestedSettings);
}
/**
* Calls the SOAP Autodiscover service
* for user settings for a single SMTP address.
*
* @param smtpAddress SMTP address.
* @param requestedSettings The requested settings.
* @return GetUserSettingsResponse
* @throws Exception on error
*/
protected GetUserSettingsResponse internalGetSoapUserSettings(
String smtpAddress,
List requestedSettings) throws Exception {
List smtpAddresses = new ArrayList();
smtpAddresses.add(smtpAddress);
List redirectionEmailAddresses = new ArrayList();
redirectionEmailAddresses.add(smtpAddress.toLowerCase());
for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
GetUserSettingsResponse response = this.getUserSettings(smtpAddresses,
requestedSettings).getTResponseAtIndex(0);
switch (response.getErrorCode()) {
case RedirectAddress:
this.traceMessage(
TraceFlags.AutodiscoverResponse,
String.format("Autodiscover service returned redirection email address '%s'.",
response.getRedirectTarget()));
smtpAddresses.clear();
smtpAddresses.add(response.getRedirectTarget().
toLowerCase());
this.url = null;
this.domain = null;
// If this email address was already tried,
//we may have a loop
// in SCP lookups. Disable consideration of SCP records.
this.disableScpLookupIfDuplicateRedirection(response.getRedirectTarget(),
redirectionEmailAddresses);
break;
case RedirectUrl:
this.traceMessage(
TraceFlags.AutodiscoverResponse,
String.format("Autodiscover service returned redirection URL '%s'.",
response.getRedirectTarget()));
//this.url = new URI(response.getRedirectTarget());
this.url = this.getCredentials().adjustUrl(new URI(response.getRedirectTarget()));
break;
case NoError:
default:
return response;
}
}
throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
}
/**
* Gets the user settings using Autodiscover SOAP service.
*
* @param smtpAddresses The SMTP addresses of the users.
* @param settings The settings.
* @return GetUserSettingsResponseCollection Object.
* @throws Exception the exception
*/
protected GetUserSettingsResponseCollection getUserSettings(
final List smtpAddresses, List settings)
throws Exception {
EwsUtilities.validateParam(smtpAddresses, "smtpAddresses");
EwsUtilities.validateParam(settings, "settings");
return this.getSettings(
GetUserSettingsResponseCollection.class, UserSettingName.class,
smtpAddresses, settings, null, this,
new IFuncDelegate() {
public String func() throws FormatException {
return EwsUtilities
.domainFromEmailAddress(smtpAddresses.get(0));
}
});
}
/**
* Gets user or domain settings using Autodiscover SOAP service.
*
* @param the generic type
* @param the generic type
* @param cls the cls
* @param cls1 the cls1
* @param identities Either the domains or the SMTP addresses of the users.
* @param settings The settings.
* @param requestedVersion Requested version of the Exchange service.
* @param getSettingsMethod The method to use.
* @param getDomainMethod The method to calculate the domain value.
* @return TGetSettingsResponse Collection.
* @throws Exception the exception
*/
private
TGetSettingsResponseCollection getSettings(
Class cls,
Class cls1,
List identities,
List settings,
ExchangeVersion requestedVersion,
IFunctionDelegate, List,
TGetSettingsResponseCollection> getSettingsMethod,
IFuncDelegate getDomainMethod) throws Exception {
TGetSettingsResponseCollection response;
// Autodiscover service only exists in E14 or later.
if (this.getRequestedServerVersion().compareTo(
MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
throw new ServiceVersionException(String.format(
"The Autodiscover service only supports %s or a later version.",
MinimumRequestVersionForAutoDiscoverSoapService));
}
// If Url is specified, call service directly.
if (this.url != null) {
URI autodiscoverUrl = this.url;
response = getSettingsMethod.func(identities, settings,
requestedVersion, this.url);
this.url = autodiscoverUrl;
return response;
}
// If Domain is specified, determine endpoint Url and call service.
else if (!(this.domain == null || this.domain.isEmpty())) {
URI autodiscoverUrl = this.getAutodiscoverEndpointUrl(this.domain);
response = getSettingsMethod.func(identities, settings,
requestedVersion,
autodiscoverUrl);
// If we got this far, response was successful, set Url.
this.url = autodiscoverUrl;
return response;
}
// No Url or Domain specified, need to figure out which endpoint(s) to
// try.
else {
// Assume caller is not inside the Intranet, regardless of whether
// SCP Urls
// were returned or not. SCP Urls are only relevent if one of them
// returns
// valid Autodiscover settings.
this.isExternal = true;
URI autodiscoverUrl;
String domainName = getDomainMethod.func();
int scpHostCount;
OutParam outParam = new OutParam();
List hosts = this.getAutodiscoverServiceHosts(domainName,
outParam);
scpHostCount = outParam.getParam();
if (hosts.size() == 0) {
throw new ServiceValidationException(
"This Autodiscover request requires that either the Domain or Url be specified.");
}
for (int currentHostIndex = 0; currentHostIndex < hosts.size(); currentHostIndex++) {
String host = hosts.get(currentHostIndex);
boolean isScpHost = currentHostIndex < scpHostCount;
OutParam outParams = new OutParam();
if (this.tryGetAutodiscoverEndpointUrl(host, outParams)) {
autodiscoverUrl = outParams.getParam();
response = getSettingsMethod.func(identities, settings,
requestedVersion,
autodiscoverUrl);
// If we got this far, the response was successful, set Url.
this.url = autodiscoverUrl;
// Not external if Autodiscover endpoint found via SCP
// returned the settings.
if (isScpHost) {
this.isExternal = false;
}
return response;
}
}
// Next-to-last chance: try unauthenticated GET over HTTP to be
// redirected to appropriate service endpoint.
autodiscoverUrl = this.getRedirectUrl(domainName);
OutParam outParamUrl = new OutParam();
if ((autodiscoverUrl != null) &&
this
.callRedirectionUrlValidationCallback(
autodiscoverUrl.toString()) &&
this.tryGetAutodiscoverEndpointUrl(autodiscoverUrl
.getHost(), outParamUrl)) {
autodiscoverUrl = outParamUrl.getParam();
response = getSettingsMethod.func(identities, settings,
requestedVersion,
autodiscoverUrl);
// If we got this far, the response was successful, set Url.
this.url = autodiscoverUrl;
return response;
}
// Last Chance: try to read autodiscover SRV Record from DNS. If we
// find one, use
// the hostname returned to construct an Autodiscover endpoint URL.
autodiscoverUrl = this
.getRedirectionUrlFromDnsSrvRecord(domainName);
if ((autodiscoverUrl != null) &&
this
.callRedirectionUrlValidationCallback(
autodiscoverUrl.toString()) &&
this.tryGetAutodiscoverEndpointUrl(autodiscoverUrl
.getHost(), outParamUrl)) {
autodiscoverUrl = outParamUrl.getParam();
response = getSettingsMethod.func(identities, settings,
requestedVersion,
autodiscoverUrl);
// If we got this far, the response was successful, set Url.
this.url = autodiscoverUrl;
return response;
} else {
throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
}
}
}
/**
* Gets settings for one or more users.
*
* @param smtpAddresses The SMTP addresses of the users.
* @param settings The settings.
* @param requestedVersion Requested version of the Exchange service.
* @param autodiscoverUrl The autodiscover URL.
* @return GetUserSettingsResponse collection.
* @throws ServiceLocalException the service local exception
* @throws Exception the exception
*/
private GetUserSettingsResponseCollection internalGetUserSettings(
List smtpAddresses, List settings,
ExchangeVersion requestedVersion,
URI autodiscoverUrl) throws ServiceLocalException, Exception {
// The response to GetUserSettings can be a redirection. Execute
// GetUserSettings until we get back
// a valid response or we've followed too many redirections.
for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
GetUserSettingsRequest request = new GetUserSettingsRequest(this,
autodiscoverUrl);
request.setSmtpAddresses(smtpAddresses);
request.setSettings(settings);
GetUserSettingsResponseCollection response = request.execute();
// Did we get redirected?
if (response.getErrorCode() == AutodiscoverErrorCode.RedirectUrl
&& response.getRedirectionUrl() != null) {
this.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("Request to %s returned redirection to %s",
autodiscoverUrl.toString(), response.getRedirectionUrl()));
autodiscoverUrl = response.getRedirectionUrl();
} else {
return response;
}
}
this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
"Maximum number of redirection hops %d exceeded",
AutodiscoverMaxRedirections));
throw new MaximumRedirectionHopsExceededException();
}
/**
* Gets the domain settings using Autodiscover SOAP service.
*
* @param domains The domains.
* @param settings The settings.
* @param requestedVersion Requested version of the Exchange service.
* @return GetDomainSettingsResponse collection.
* @throws Exception the exception
*/
protected GetDomainSettingsResponseCollection getDomainSettings(
final List domains, List settings,
ExchangeVersion requestedVersion)
throws Exception {
EwsUtilities.validateParam(domains, "domains");
EwsUtilities.validateParam(settings, "settings");
return this.getSettings(
GetDomainSettingsResponseCollection.class,
DomainSettingName.class, domains, settings,
requestedVersion, this,
new IFuncDelegate() {
public String func() {
return domains.get(0);
}
});
}
/**
* Gets settings for one or more domains.
*
* @param domains The domains.
* @param settings The settings.
* @param requestedVersion Requested version of the Exchange service.
* @param autodiscoverUrl The autodiscover URL.
* @return GetDomainSettingsResponse Collection.
* @throws ServiceLocalException the service local exception
* @throws Exception the exception
*/
private GetDomainSettingsResponseCollection internalGetDomainSettings(
List domains, List settings,
ExchangeVersion requestedVersion,
URI autodiscoverUrl) throws ServiceLocalException, Exception {
// The response to GetDomainSettings can be a redirection. Execute
// GetDomainSettings until we get back
// a valid response or we've followed too many redirections.
for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
GetDomainSettingsRequest request = new GetDomainSettingsRequest(
this, autodiscoverUrl);
request.setDomains(domains);
request.setSettings(settings);
request.setRequestedVersion(requestedVersion);
GetDomainSettingsResponseCollection response = request.execute();
// Did we get redirected?
if (response.getErrorCode() == AutodiscoverErrorCode.RedirectUrl
&& response.getRedirectionUrl() != null) {
autodiscoverUrl = response.getRedirectionUrl();
} else {
return response;
}
}
this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
"Maximum number of redirection hops %d exceeded",
AutodiscoverMaxRedirections));
throw new MaximumRedirectionHopsExceededException();
}
/**
* Gets the autodiscover endpoint URL.
*
* @param host The host.
* @return URI The URI.
* @throws Exception the exception
*/
private URI getAutodiscoverEndpointUrl(String host) throws Exception {
URI autodiscoverUrl = null;
OutParam outParam = new OutParam();
if (this.tryGetAutodiscoverEndpointUrl(host, outParam)) {
return autodiscoverUrl;
} else {
throw new AutodiscoverLocalException(
"No appropriate Autodiscover SOAP or WS-Security endpoint is available.");
}
}
/**
* Tries the get Autodiscover Service endpoint URL.
*
* @param host The host.
* @param url the url
* @return boolean The boolean.
* @throws Exception the exception
*/
private boolean tryGetAutodiscoverEndpointUrl(String host,
OutParam url)
throws Exception {
EnumSet endpoints;
OutParam> outParam =
new OutParam>();
if (this.tryGetEnabledEndpointsForHost(host, outParam)) {
endpoints = outParam.getParam();
url
.setParam(new URI(String.format(AutodiscoverSoapHttpsUrl,
host)));
// Make sure that at least one of the non-legacy endpoints is
// available.
if ((!endpoints.contains(AutodiscoverEndpoints.Soap)) &&
(!endpoints.contains(
AutodiscoverEndpoints.WsSecurity))
// (endpoints .contains( AutodiscoverEndpoints.WSSecuritySymmetricKey) ) &&
//(endpoints .contains( AutodiscoverEndpoints.WSSecurityX509Cert))
) {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"No Autodiscover endpoints " +
"are available for host %s",
host));
return false;
}
// If we have WLID credential, make sure that we have a WS-Security
// endpoint
/*
if (this.getCredentials() instanceof WindowsLiveCredentials) {
if (endpoints.contains(AutodiscoverEndpoints.WsSecurity)) {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"No Autodiscover " +
"WS-Security " +
"endpoint is available" +
" for host %s",
host));
return false;
} else {
url.setParam(new URI(String.format(
AutodiscoverSoapWsSecurityHttpsUrl, host)));
}
}
else if (this.getCredentials() instanceof PartnerTokenCredentials)
{
if (endpoints.contains( AutodiscoverEndpoints.WSSecuritySymmetricKey))
{
this.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("No Autodiscover WS-Security/SymmetricKey endpoint is available for host {0}", host));
return false;
}
else
{
url.setParam( new URI(String.format(AutodiscoverSoapWsSecuritySymmetricKeyHttpsUrl, host)));
}
}
else if (this.getCredentials()instanceof X509CertificateCredentials)
{
if ((endpoints.contains(AutodiscoverEndpoints.WSSecurityX509Cert))
{
this.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String.format("No Autodiscover WS-Security/X509Cert endpoint is available for host {0}", host));
return false;
}
else
{
url.setParam( new URI(String.format(AutodiscoverSoapWsSecurityX509CertHttpsUrl, host)));
}
}
*/
return true;
} else {
this
.traceMessage(
TraceFlags.AutodiscoverConfiguration,
String
.format(
"No Autodiscover endpoints " +
"are available for host %s",
host));
return false;
}
}
/**
* Gets the list of autodiscover service URLs.
*
* @param domainName Domain name.
* @param scpHostCount Count of hosts found via SCP lookup.
* @return List of Autodiscover URLs.
* @throws java.net.URISyntaxException the URI Syntax exception
*/
protected List getAutodiscoverServiceUrls(String domainName,
OutParam scpHostCount) throws URISyntaxException {
List urls;
urls = new ArrayList();
scpHostCount.setParam(urls.size());
// As a fallback, add autodiscover URLs base on the domain name.
urls.add(new URI(String.format(AutodiscoverLegacyHttpsUrl,
domainName)));
urls.add(new URI(String.format(AutodiscoverLegacyHttpsUrl,
"autodiscover." + domainName)));
return urls;
}
/**
* Gets the list of autodiscover service hosts.
*
* @param domainName Domain name.
* @param outParam the out param
* @return List of hosts.
* @throws java.net.URISyntaxException the uRI syntax exception
* @throws ClassNotFoundException the class not found exception
*/
protected List getAutodiscoverServiceHosts(String domainName,
OutParam outParam) throws URISyntaxException,
ClassNotFoundException {
List urls = this.getAutodiscoverServiceUrls(domainName, outParam);
List lst = new ArrayList();
for (URI url : urls) {
lst.add(url.getHost());
}
return lst;
}
/**
* Gets the enabled autodiscover endpoints on a specific host.
*
* @param host The host.
* @param endpoints Endpoints found for host.
* @return Flags indicating which endpoints are enabled.
* @throws Exception the exception
*/
private boolean tryGetEnabledEndpointsForHost(String host,
OutParam> endpoints) throws Exception {
this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
"Determining which endpoints are enabled for host %s", host));
// We may get redirected to another host. And therefore need to limit the number of redirections we'll
// tolerate.
for (int currentHop = 0; currentHop < AutodiscoverMaxRedirections; currentHop++) {
URI autoDiscoverUrl = new URI(String.format(AutodiscoverLegacyHttpsUrl, host));
endpoints.setParam(EnumSet.of(AutodiscoverEndpoints.None));
HttpWebRequest request = null;
try {
request = new HttpClientWebRequest(httpClient, httpContext);
request.setProxy(getWebProxy());
try {
request.setUrl(autoDiscoverUrl.toURL());
} catch (MalformedURLException e) {
String strErr = String.format("Incorrect format : %s", url);
throw new ServiceLocalException(strErr);
}
request.setRequestMethod("GET");
request.setAllowAutoRedirect(false);
request.setPreAuthenticate(false);
request.setUseDefaultCredentials(this.getUseDefaultCredentials());
request.setTimeout(getTimeout());
prepareCredentials(request);
request.prepareConnection();
try {
request.executeRequest();
} catch (IOException e) {
return false;
}
OutParam outParam = new OutParam();
if (this.tryGetRedirectionResponse(request, outParam)) {
URI redirectUrl = outParam.getParam();
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Host returned redirection to host '%s'", redirectUrl.getHost()));
host = redirectUrl.getHost();
} else {
endpoints.setParam(this.getEndpointsFromHttpWebResponse(request));
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Host returned enabled endpoint flags: %s", endpoints.getParam().toString()));
return true;
}
} finally {
if (request != null) {
try {
request.close();
} catch (Exception e) {
// Connection can't be closed. We'll ignore this...
}
}
}
}
this.traceMessage(TraceFlags.AutodiscoverConfiguration,
String.format("Maximum number of redirection hops %d exceeded", AutodiscoverMaxRedirections));
throw new MaximumRedirectionHopsExceededException();
}
/**
* Gets the endpoints from HTTP web response.
*
* @param request the request
* @return Endpoints enabled.
* @throws EWSHttpException the EWS http exception
*/
private EnumSet getEndpointsFromHttpWebResponse(
HttpWebRequest request) throws EWSHttpException {
EnumSet endpoints = EnumSet
.noneOf(AutodiscoverEndpoints.class);
endpoints.add(AutodiscoverEndpoints.Legacy);
if (!(request.getResponseHeaders().get(
AutodiscoverSoapEnabledHeaderName) == null || request
.getResponseHeaders().get(AutodiscoverSoapEnabledHeaderName)
.isEmpty())) {
endpoints.add(AutodiscoverEndpoints.Soap);
}
if (!(request.getResponseHeaders().get(
AutodiscoverWsSecurityEnabledHeaderName) == null || request
.getResponseHeaders().get(
AutodiscoverWsSecurityEnabledHeaderName).isEmpty())) {
endpoints.add(AutodiscoverEndpoints.WsSecurity);
}
/* if (! (request.getResponseHeaders().get(
AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName) !=null || request
.getResponseHeaders().get(
AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName).isEmpty()))
{
endpoints .add( AutodiscoverEndpoints.WSSecuritySymmetricKey);
}
if (!(request.getResponseHeaders().get(
AutodiscoverWsSecurityX509CertEnabledHeaderName)!=null ||
request.getResponseHeaders().get(
AutodiscoverWsSecurityX509CertEnabledHeaderName).isEmpty()))
{
endpoints .add(AutodiscoverEndpoints.WSSecurityX509Cert);
}*/
return endpoints;
}
/**
* Traces the response.
*
* @param request the request
* @param memoryStream the memory stream
* @throws XMLStreamException the XML stream exception
* @throws IOException signals that an I/O exception has occurred.
* @throws EWSHttpException the EWS http exception
*/
public void traceResponse(HttpWebRequest request, ByteArrayOutputStream memoryStream) throws XMLStreamException,
IOException, EWSHttpException {
this.processHttpResponseHeaders(
TraceFlags.AutodiscoverResponseHttpHeaders, request);
String contentType = request.getResponseContentType();
if (!(contentType == null || contentType.isEmpty())) {
contentType = contentType.toLowerCase();
if (contentType.toLowerCase().startsWith("text/") ||
contentType.toLowerCase().
startsWith("application/soap")) {
this.traceXml(TraceFlags.AutodiscoverResponse, memoryStream);
} else {
this.traceMessage(TraceFlags.AutodiscoverResponse,
"Non-textual response");
}
}
}
/**
* Creates an HttpWebRequest instance and initializes it with the
* appropriate parameters, based on the configuration of this service
* object.
*
* @param url The URL that the HttpWebRequest should target
* @return HttpWebRequest The HttpWebRequest
* @throws ServiceLocalException the service local exception
* @throws java.net.URISyntaxException the uRI syntax exception
*/
public HttpWebRequest prepareHttpWebRequestForUrl(URI url)
throws ServiceLocalException, URISyntaxException {
return this.prepareHttpWebRequestForUrl(url, false,
// acceptGzipEncoding
false); // allowAutoRedirect
}
/**
* Calls the redirection URL validation callback. If the redirection URL
* validation callback is null, use the default callback which does not
* allow following any redirections.
*
* @param redirectionUrl The redirection URL.
* @return True if redirection should be followed.
* @throws AutodiscoverLocalException the autodiscover local exception
*/
private boolean callRedirectionUrlValidationCallback(String redirectionUrl)
throws AutodiscoverLocalException {
IAutodiscoverRedirectionUrl callback =
(this.redirectionUrlValidationCallback == null) ? this
: this.redirectionUrlValidationCallback;
return callback
.autodiscoverRedirectionUrlValidationCallback(redirectionUrl);
}
/**
* Processes an HTTP error response.
*
* @param httpWebResponse The HTTP web response.
* @throws Exception the exception
*/
@Override public void processHttpErrorResponse(HttpWebRequest httpWebResponse, Exception webException) throws Exception {
this.internalProcessHttpErrorResponse(
httpWebResponse,
webException,
TraceFlags.AutodiscoverResponseHttpHeaders,
TraceFlags.AutodiscoverResponse);
}
/*
* (non-Javadoc)
*
* @see microsoft.exchange.webservices.AutodiscoverRedirectionUrlInterface#
* autodiscoverRedirectionUrlValidationCallback(java.lang.String)
*/
public boolean autodiscoverRedirectionUrlValidationCallback(
String redirectionUrl) throws AutodiscoverLocalException {
return defaultAutodiscoverRedirectionUrlValidationCallback(
redirectionUrl);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @throws ArgumentException on validation error
*/
public AutodiscoverService() throws ArgumentException {
this(ExchangeVersion.Exchange2010);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param requestedServerVersion The requested server version
* @throws ArgumentException on validation error
*/
public AutodiscoverService(ExchangeVersion requestedServerVersion)
throws ArgumentException {
this(null, null, requestedServerVersion);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param domain The domain that will be used to determine the URL of the service
* @throws ArgumentException on validation error
*/
public AutodiscoverService(String domain) throws ArgumentException {
this(null, domain);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param domain The domain that will be used to determine the URL of the service
* @param requestedServerVersion The requested server version
* @throws ArgumentException on validation error
*/
public AutodiscoverService(String domain,
ExchangeVersion requestedServerVersion) throws ArgumentException {
this(null, domain, requestedServerVersion);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param url The URL of the service
* @throws ArgumentException on validation error
*/
public AutodiscoverService(URI url) throws ArgumentException {
this(url, url.getHost());
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param url The URL of the service
* @param requestedServerVersion The requested server version
* @throws ArgumentException on validation error
*/
public AutodiscoverService(URI url,
ExchangeVersion requestedServerVersion) throws ArgumentException {
this(url, url.getHost(), requestedServerVersion);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param url The URL of the service
* @param domain The domain that will be used to determine the URL of the service
* @throws ArgumentException on validation error
*/
public AutodiscoverService(URI url, String domain)
throws ArgumentException {
super();
EwsUtilities.validateDomainNameAllowNull(domain, "domain");
this.url = url;
this.domain = domain;
this.dnsClient = new AutodiscoverDnsClient(this);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param url The URL of the service.
* @param domain The domain that will be used to determine the URL of the
* service.
* @param requestedServerVersion The requested server version.
* @throws ArgumentException on validation error
*/
public AutodiscoverService(URI url, String domain,
ExchangeVersion requestedServerVersion) throws ArgumentException {
super(requestedServerVersion);
EwsUtilities.validateDomainNameAllowNull(domain, "domain");
this.url = url;
this.domain = domain;
this.dnsClient = new AutodiscoverDnsClient(this);
}
/**
* Initializes a new instance of the AutodiscoverService class.
*
* @param service The other service.
* @param requestedServerVersion The requested server version.
*/
public AutodiscoverService(ExchangeServiceBase service,
ExchangeVersion requestedServerVersion) {
super(service, requestedServerVersion);
this.dnsClient = new AutodiscoverDnsClient(this);
}
/**
* Initializes a new instance of the "AutodiscoverService" class.
*
* @param service The service.
*/
public AutodiscoverService(ExchangeServiceBase service) {
super(service, service.getRequestedServerVersion());
}
/**
* Retrieves the specified settings for single SMTP address.
* This method will run the entire Autodiscover "discovery"
* algorithm and will follow address and URL redirections.
* @param userSmtpAddress The SMTP addresses of the user.
* @param userSettingNames The user setting names.
* @return A UserResponse object containing the requested settings for the
* specified user.
* @throws Exception on error
*/
public GetUserSettingsResponse getUserSettings(String userSmtpAddress,
UserSettingName... userSettingNames) throws Exception {
List requestedSettings = new ArrayList();
requestedSettings.addAll(Arrays.asList(userSettingNames));
if (userSmtpAddress == null || userSmtpAddress.isEmpty()) {
throw new ServiceValidationException("A valid SMTP address must be specified.");
}
if (requestedSettings.size() == 0) {
throw new ServiceValidationException("At least one setting must be requested.");
}
if (this.getRequestedServerVersion().compareTo(MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
return this.internalGetLegacyUserSettings(userSmtpAddress,
requestedSettings);
} else {
return this.internalGetSoapUserSettings(userSmtpAddress,
requestedSettings);
}
}
/**
* Retrieves the specified settings for a set of users.
*
* @param userSmtpAddresses the user smtp addresses
* @param userSettingNames The user setting names.
* @return A GetUserSettingsResponseCollection object containing the
* response for each individual user.
* @throws Exception the exception
*/
public GetUserSettingsResponseCollection getUsersSettings(
Iterable userSmtpAddresses,
UserSettingName... userSettingNames) throws Exception {
if (this.getRequestedServerVersion().compareTo(MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
throw new ServiceVersionException(
String.format("The Autodiscover service only supports %s or a later version.",
MinimumRequestVersionForAutoDiscoverSoapService));
}
List smtpAddresses = new ArrayList();
smtpAddresses.addAll((Collection) userSmtpAddresses);
List settings = new ArrayList();
settings.addAll(Arrays.asList(userSettingNames));
return this.getUserSettings(smtpAddresses, settings);
}
/**
* Retrieves the specified settings for a domain.
*
* @param domain The domain.
* @param requestedVersion Requested version of the Exchange service.
* @param domainSettingNames The domain setting names.
* @return A DomainResponse object containing the requested settings for the
* specified domain.
* @throws Exception the exception
*/
public GetDomainSettingsResponse getDomainSettings(String domain,
ExchangeVersion requestedVersion,
DomainSettingName... domainSettingNames) throws Exception {
List domains = new ArrayList(1);
domains.add(domain);
List settings = new ArrayList();
settings.addAll(Arrays.asList(domainSettingNames));
return this.getDomainSettings(domains, settings, requestedVersion).
getTResponseAtIndex(0);
}
/**
* Retrieves the specified settings for a set of domains.
*
* @param domains the domains
* @param requestedVersion Requested version of the Exchange service.
* @param domainSettingNames The domain setting names.
* @return A GetDomainSettingsResponseCollection object containing the
* response for each individual domain.
* @throws Exception the exception
*/
public GetDomainSettingsResponseCollection getDomainSettings(
Iterable domains, ExchangeVersion requestedVersion,
DomainSettingName... domainSettingNames)
throws Exception {
List settings = new ArrayList();
settings.addAll(Arrays.asList(domainSettingNames));
List domainslst = new ArrayList();
domainslst.addAll((Collection) domains);
return this.getDomainSettings(domainslst, settings, requestedVersion);
}
/**
* Gets the domain this service is bound to. When this property is
* set, the domain name is used to automatically determine the Autodiscover service URL.
*
* @return the domain
*/
public String getDomain() {
return this.domain;
}
/**
* Sets the domain this service is bound to. When this property is
* set, the domain
* name is used to automatically determine the Autodiscover service URL.
*
* @param value the new domain
* @throws ArgumentException on validation error
*/
public void setDomain(String value) throws ArgumentException {
EwsUtilities.validateDomainNameAllowNull(value, "Domain");
// If Domain property is set to non-null value, Url property is nulled.
if (value != null) {
this.url = null;
}
this.domain = value;
}
/**
* Gets the url this service is bound to.
*
* @return the url
*/
public URI getUrl() {
return this.url;
}
/**
* Sets the url this service is bound to.
*
* @param value the new url
*/
public void setUrl(URI value) {
// If Url property is set to non-null value, Domain property is set to
// host portion of Url.
if (value != null) {
this.domain = value.getHost();
}
this.url = value;
}
public Boolean isExternal() {
return this.isExternal;
}
protected void setIsExternal(Boolean value) {
this.isExternal = value;
}
/**
* Gets the redirection url validation callback.
*
* @return the redirection url validation callback
*/
public IAutodiscoverRedirectionUrl
getRedirectionUrlValidationCallback() {
return this.redirectionUrlValidationCallback;
}
/**
* Sets the redirection url validation callback.
*
* @param value the new redirection url validation callback
*/
public void setRedirectionUrlValidationCallback(
IAutodiscoverRedirectionUrl value) {
this.redirectionUrlValidationCallback = value;
}
/**
* Gets the dns server address.
*
* @return the dns server address
*/
protected String getDnsServerAddress() {
return this.dnsServerAddress;
}
/**
* Sets the dns server address.
*
* @param value the new dns server address
*/
protected void setDnsServerAddress(String value) {
this.dnsServerAddress = value;
}
/**
* Gets a value indicating whether the AutodiscoverService should
* perform SCP (ServiceConnectionPoint) record lookup when determining
* the Autodiscover service URL.
*
* @return the enable scp lookup
*/
public boolean getEnableScpLookup() {
return this.enableScpLookup;
}
/**
* Sets the enable scp lookup.
*
* @param value the new enable scp lookup
*/
public void setEnableScpLookup(boolean value) {
this.enableScpLookup = value;
}
/*
* (non-Javadoc)
*
* @see
* microsoft.exchange.webservices.FuncDelegateInterface#func(java.util.List,
* java.util.List, java.net.URI)
*/
@Override
public Object func(List arg1, List arg2, ExchangeVersion arg3, URI arg4)
throws ServiceLocalException, Exception {
if (arg2.get(0).getClass().equals(DomainSettingName.class)) {
return internalGetDomainSettings(arg1, arg2, arg3, arg4);
} else if (arg2.get(0).getClass().equals(UserSettingName.class)) {
return internalGetUserSettings(arg1, arg2, arg3, arg4);
} else {
return null;
}
}
}