All Downloads are FREE. Search and download functionalities are using the official Maven repository.

microsoft.exchange.webservices.data.AutodiscoverService Maven / Gradle / Ivy

Go to download

The source came from http://archive.msdn.microsoft.com/ewsjavaapi Support for Maven has been added.

The newest version!
/**************************************************************************
 * copyright file="AutodiscoverService.java" company="Microsoft"
 *     Copyright (c) Microsoft Corporation.  All rights reserved.
 * 
 * Defines the AutodiscoverService.java.
 **************************************************************************/
package microsoft.exchange.webservices.data;

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.Collection;
import java.util.EnumSet;
import java.util.List;

import javax.xml.stream.XMLStreamException;

/**
 *Represents a binding to the Exchange Autodiscover Service.
 */
public final 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. */
	boolean enableScpLookup = true;

	// Autodiscover legacy path
	/** The Constant AutodiscoverLegacyPath. */
	private static final String AutodiscoverLegacyPath = 
		"/autodiscover/autodiscover.xml";

	/** Autodiscover legacy Url with protocol fill-in */
	private static final String AutodiscoverLegacyUrl = "%s://%s" + 
			AutodiscoverLegacyPath;

	// 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 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";
	// 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(
				Strings.AutodiscoverRedirectBlocked, 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 = 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 = (URI) 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();
		try {
			request.close(); 
		} catch (Exception e2) {
			request = null;
		} 
		return settings;

	}

	/**
	 * Writes the autodiscover request.
	 * 
	 * @param emailAddress
	 *            the email address
	 * @param settings
	 *            the settings
	 * @param writer
	 *            the writer
	 * @throws 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);

		this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
				"Trying to get Autodiscover redirection URL from %s.", url));

		HttpWebRequest request = new HttpClientWebRequest(this.getSimpleHttpConnectionManager());
		try {
			request.setUrl(URI.create(url).toURL());
		} catch (MalformedURLException e) {
			String strErr = String.format("Incorrect format : %s", url);
			throw new ServiceLocalException(strErr);
		}

		request.setAllowAutoRedirect(false);
		request.setPreAuthenticate(false);
		request.setRequestMethod("GET");
		request.setUseDefaultCredentials(this.getUseDefaultCredentials());
		if (!this.getUseDefaultCredentials()) {
			ExchangeCredentials serviceCredentials = this.getCredentials();
			if (null == serviceCredentials) {
				throw new ServiceLocalException(Strings.CredentialsRequired);
			}
			// Make sure that credentials have been authenticated if required
			serviceCredentials.preAuthenticate();

			// Apply credentials to the request
			serviceCredentials.prepareWebRequest(request);
		}
		try {
			request.prepareAsyncConnection();
		} catch (Exception ex) {
			ex.getMessage();
			request = null;
		}

		if (request != null)
		{
			URI redirectUrl;
			OutParam outParam = new OutParam();
			if (this.tryGetRedirectionResponse(request, outParam))
			{
				redirectUrl = (URI) outParam.getParam();
				return redirectUrl;
			}
		}
		try {
			request.close(); 
		} catch (Exception e2) {
			request = null;
		} 
		this.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(new Integer(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().intValue();
         if (urls.size() == 0)
         {
             throw new ServiceValidationException(
			 		Strings.AutodiscoverServiceRequestRequiresDomainOrUrl);
         }

		// 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 = null;

		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().intValue()+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 AutodiscoverLocalException(
								Strings.MaximumRedirectionHopsExceeded);
					}
				case RedirectAddress:
					if (currentHop.getParam() < AutodiscoverMaxRedirections) {
						currentHop.setParam(currentHop.getParam().intValue()+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 AutodiscoverLocalException(
								Strings.MaximumRedirectionHopsExceeded);
					}
				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(
								Strings.AutodiscoverError, settings.getError());
						currentUrlIndex++;
					} else {
						throw new AutodiscoverRemoteException(
								Strings.AutodiscoverError, 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().intValue()+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)) {
				settings = outParam.getParam();
				return settings;
			}
			// If there was an earlier exception, throw it.
			else if (delayedException != null) {
				throw delayedException;
			} else {
				throw new AutodiscoverLocalException(
						Strings.AutodiscoverCouldNotBeLocated);
			}
		}
	}

	/**
	 * 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(
								Strings.AutodiscoverError, 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(new Integer(currentHop));
                        settings.setParam(
								this.internalGetLegacyUserSettings(cls,
                            emailAddress,
                            redirectionEmailAddresses,
                            outParam));
                        currentHop = outParam.getParam().intValue();
						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) {

					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
	 */
    protected GetUserSettingsResponse internalGetLegacyUserSettings(
			String emailAddress, 
			List requestedSettings) throws Exception {
        // Cannot call legacy Autodiscover service with WindowsLive credentials
        if ((this.getCredentials() != null) && (this.getCredentials() instanceof WindowsLiveCredentials)) {
            throw new AutodiscoverLocalException(
					Strings.WLIDCredentialsCannotBeUsedWithLegacyAutodiscover);
        }

        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
	 */
    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());
                    break;

                case NoError:
                default:
                    return response;
            }
        }

        throw new AutodiscoverLocalException(
				Strings.AutodiscoverCouldNotBeLocated);
    }

	/**
	 * 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 (GetUserSettingsResponseCollection) 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, 
			ExchangeVersion, URI, 
			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(
					Strings.AutodiscoverServiceIncompatibleWithRequestVersion,
					MinimumRequestVersionForAutoDiscoverSoapService));
		}

		// If Url is specified, call service directly.
		if (this.url != null) {
			return getSettingsMethod.func(identities, settings, 
					requestedVersion, this.url);

		}
		// 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(
						Strings.AutodiscoverServiceRequestRequiresDomainOrUrl);
			}

			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(
						Strings.AutodiscoverCouldNotBeLocated);
			}
		}
	}

	/**
	 * 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 AutodiscoverLocalException(
				Strings.MaximumRedirectionHopsExceeded);
	}

	/**
	 * 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 (GetDomainSettingsResponseCollection) 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 AutodiscoverLocalException(
				Strings.MaximumRedirectionHopsExceeded);
	}

	/**
	 * 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(
					Strings.NoSoapOrWsSecurityEndpointAvailable);
		}
	}

	/**
	 * 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))) {
				this
				.traceMessage(
						TraceFlags.AutodiscoverConfiguration,
						String
						.format(
								"No Autodiscover endpoints " +
								"are available  for host %s",
								host));

				return false;
			}

			// If we have WLID credentials, 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)));
				}
			}*/

			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 URISyntaxException
	 *             the URI Syntax exception
	 */
    protected List getAutodiscoverServiceUrls(String domainName, 
			OutParam scpHostCount) throws URISyntaxException {
        List urls;

        urls = new ArrayList();

        scpHostCount.setParam(new Integer(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 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 = new HttpClientWebRequest(this.getSimpleHttpConnectionManager());
			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());
			if (!this.getUseDefaultCredentials()) {
				ExchangeCredentials serviceCredentials = this.getCredentials();
				if (null == serviceCredentials) {
					throw new ServiceLocalException(Strings.
							CredentialsRequired);
				}

				// Make sure that credentials have been 
				// authenticated if required
				serviceCredentials.preAuthenticate();

				// Apply credentials to the request
				serviceCredentials.prepareWebRequest(request);
			}
			try {
				request.prepareAsyncConnection();
			} catch (Exception ex) {
				ex.getMessage();
				request = null;
			}

			if (request != null) {
				URI redirectUrl;
				OutParam outParam = new OutParam();
				if (this.tryGetRedirectionResponse(request, outParam)) {
					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;
				}
			} else {
				return false;
			}
			try {
				request.close(); 
			} catch (Exception e2) {
				request = null;
			} 
		}

		this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
				"Maximum number of redirection hops %d exceeded",
				AutodiscoverMaxRedirections));

		throw new AutodiscoverLocalException(
				Strings.MaximumRedirectionHopsExceeded);
	}

	/**
	 * 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);
		}
		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
	 */
	protected void traceResponse(HttpWebRequest request,
			ByteArrayOutputStream memoryStream) throws XMLStreamException,
			IOException, EWSHttpException {
		this.traceHttpResponseHeaders(
				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 URISyntaxException
	 *             the uRI syntax exception
	 */
	protected 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
    protected void processHttpErrorResponse(HttpWebRequest httpWebResponse, 
			Exception webException) throws Exception {
        this.internalProcessHttpErrorResponse(
            httpWebResponse,
            webException,
            TraceFlags.AutodiscoverResponseHttpHeaders,
            TraceFlags.AutodiscoverResponse);
    }
	
	/*
	 * (non-Javadoc)
	 * 
	 * @seemicrosoft.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 
	 * 
	 */
	public AutodiscoverService() throws ArgumentException {
		this(ExchangeVersion.Exchange2010);
	}

	/**
	 * Initializes a new instance of the "AutodiscoverService" class.
	 * 
	 * @param requestedServerVersion
	 *            The requested server version.
	 * @throws ArgumentException 
	 */
	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 
	 */
	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 
	 */
	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 
	 */
	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 
	 */
	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 
	 */
	protected 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 
	 */
	protected 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.	
	 */	
	protected 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.
	 * 
	 * @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
	 *             the exception
	 *             
	 * This method handles will run the entire Autodiscover "discovery" 
	 * algorithm and will follow address and URL redirections.
	 */
	public GetUserSettingsResponse getUserSettings(String userSmtpAddress,
			UserSettingName... userSettingNames) throws Exception {
		List requestedSettings = new ArrayList();
		for (UserSettingName userSettingName : userSettingNames) {
			requestedSettings.add(userSettingName);
		}		

        if (userSmtpAddress == null || userSmtpAddress.isEmpty())
        {
            throw new ServiceValidationException(
					Strings.InvalidAutodiscoverSmtpAddress);
        }

        if (requestedSettings.size() == 0)
        {
            throw new ServiceValidationException(
					Strings.InvalidAutodiscoverSettingsCount);
        }

        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
	 *         responses 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(Strings.AutodiscoverServiceIncompatibleWithRequestVersion, 
				 		MinimumRequestVersionForAutoDiscoverSoapService));
         }
		List smtpAddresses = new ArrayList();
		smtpAddresses.addAll((Collection) userSmtpAddresses);
		List settings = new ArrayList();
		for (UserSettingName userSettingName : userSettingNames) {
			settings.add(userSettingName);
		}
		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();
		for (DomainSettingName domainSettingName : domainSettingNames) {
			settings.add(domainSettingName);
		}
		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
	 *         responses for each individual domain.
	 * @throws Exception
	 *             the exception
	 */
	public GetDomainSettingsResponseCollection getDomainSettings(
			Iterable domains, ExchangeVersion requestedVersion, 
			DomainSettingName... domainSettingNames)
	throws Exception {
		List settings = new ArrayList();
		for (DomainSettingName domainSettingName : domainSettingNames) {
			settings.add(domainSettingName);
		}
		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 
	 */
	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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy