001 /*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024 package microsoft.exchange.webservices.data.autodiscover;
025
026 import microsoft.exchange.webservices.data.autodiscover.configuration.ConfigurationSettingsBase;
027 import microsoft.exchange.webservices.data.autodiscover.configuration.outlook.OutlookConfigurationSettings;
028 import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverEndpoints;
029 import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverErrorCode;
030 import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverLocalException;
031 import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverRemoteException;
032 import microsoft.exchange.webservices.data.autodiscover.request.AutodiscoverRequest;
033 import microsoft.exchange.webservices.data.autodiscover.request.GetDomainSettingsRequest;
034 import microsoft.exchange.webservices.data.autodiscover.request.GetUserSettingsRequest;
035 import microsoft.exchange.webservices.data.autodiscover.response.GetDomainSettingsResponse;
036 import microsoft.exchange.webservices.data.autodiscover.response.GetDomainSettingsResponseCollection;
037 import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponse;
038 import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponseCollection;
039 import microsoft.exchange.webservices.data.core.EwsUtilities;
040 import microsoft.exchange.webservices.data.core.EwsXmlReader;
041 import microsoft.exchange.webservices.data.core.ExchangeServiceBase;
042 import microsoft.exchange.webservices.data.core.request.HttpClientWebRequest;
043 import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
044 import microsoft.exchange.webservices.data.credential.WSSecurityBasedCredentials;
045 import microsoft.exchange.webservices.data.autodiscover.enumeration.DomainSettingName;
046 import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
047 import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
048 import microsoft.exchange.webservices.data.autodiscover.enumeration.UserSettingName;
049 import microsoft.exchange.webservices.data.core.exception.misc.ArgumentException;
050 import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
051 import microsoft.exchange.webservices.data.core.exception.misc.FormatException;
052 import microsoft.exchange.webservices.data.autodiscover.exception.MaximumRedirectionHopsExceededException;
053 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
054 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
055 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException;
056 import microsoft.exchange.webservices.data.misc.OutParam;
057 import microsoft.exchange.webservices.data.security.XmlNodeType;
058
059 import javax.xml.stream.XMLStreamException;
060
061 import java.io.ByteArrayInputStream;
062 import java.io.ByteArrayOutputStream;
063 import java.io.IOException;
064 import java.io.InputStream;
065 import java.io.OutputStream;
066 import java.io.PrintWriter;
067 import java.net.MalformedURLException;
068 import java.net.URI;
069 import java.net.URISyntaxException;
070 import java.util.ArrayList;
071 import java.util.Arrays;
072 import java.util.Collection;
073 import java.util.EnumSet;
074 import java.util.List;
075
076 /**
077 * Represents a binding to the Exchange Autodiscover Service.
078 */
079 public class AutodiscoverService extends ExchangeServiceBase
080 implements IAutodiscoverRedirectionUrl, IFunctionDelegate {
081
082 // region Private members
083 /**
084 * The domain.
085 */
086 private String domain;
087
088 /**
089 * The is external.
090 */
091 private Boolean isExternal = true;
092
093 /**
094 * The url.
095 */
096 private URI url;
097
098 /**
099 * The redirection url validation callback.
100 */
101 private IAutodiscoverRedirectionUrl
102 redirectionUrlValidationCallback;
103
104 /**
105 * The dns client.
106 */
107 private AutodiscoverDnsClient dnsClient;
108
109 /**
110 * The dns server address.
111 */
112 private String dnsServerAddress;
113
114 /**
115 * The enable scp lookup.
116 */
117 private boolean enableScpLookup = true;
118
119 // Autodiscover legacy path
120 /**
121 * The Constant AutodiscoverLegacyPath.
122 */
123 private static final String AutodiscoverLegacyPath =
124 "/autodiscover/autodiscover.xml";
125
126 // Autodiscover legacy HTTPS Url
127 /**
128 * The Constant AutodiscoverLegacyHttpsUrl.
129 */
130 private static final String AutodiscoverLegacyHttpsUrl = "https://%s" +
131 AutodiscoverLegacyPath;
132 // Autodiscover legacy HTTP Url
133 /**
134 * The Constant AutodiscoverLegacyHttpUrl.
135 */
136 private static final String AutodiscoverLegacyHttpUrl = "http://%s" +
137 AutodiscoverLegacyPath;
138 // Autodiscover SOAP HTTPS Url
139 /**
140 * The Constant AutodiscoverSoapHttpsUrl.
141 */
142 private static final String AutodiscoverSoapHttpsUrl =
143 "https://%s/autodiscover/autodiscover.svc";
144 // Autodiscover SOAP WS-Security HTTPS Url
145 /**
146 * The Constant AutodiscoverSoapWsSecurityHttpsUrl.
147 */
148 private static final String AutodiscoverSoapWsSecurityHttpsUrl =
149 AutodiscoverSoapHttpsUrl +
150 "/wssecurity";
151
152 /**
153 * Autodiscover SOAP WS-Security symmetrickey HTTPS Url
154 */
155 private static final String AutodiscoverSoapWsSecuritySymmetricKeyHttpsUrl =
156 AutodiscoverSoapHttpsUrl + "/wssecurity/symmetrickey";
157
158 /**
159 * Autodiscover SOAP WS-Security x509cert HTTPS Url
160 */
161 private static final String AutodiscoverSoapWsSecurityX509CertHttpsUrl =
162 AutodiscoverSoapHttpsUrl + "/wssecurity/x509cert";
163
164
165 // Autodiscover request namespace
166 /**
167 * The Constant AutodiscoverRequestNamespace.
168 */
169 private static final String AutodiscoverRequestNamespace =
170 "http://schemas.microsoft.com/exchange/autodiscover/" +
171 "outlook/requestschema/2006";
172 // Maximum number of Url (or address) redirections that will be followed by
173 // an Autodiscover call
174 /**
175 * The Constant AutodiscoverMaxRedirections.
176 */
177 protected static final int AutodiscoverMaxRedirections = 10;
178 // HTTP header indicating that SOAP Autodiscover service is enabled.
179 /**
180 * The Constant AutodiscoverSoapEnabledHeaderName.
181 */
182 private static final String AutodiscoverSoapEnabledHeaderName =
183 "X-SOAP-Enabled";
184 // HTTP header indicating that WS-Security Autodiscover service is enabled.
185 /**
186 * The Constant AutodiscoverWsSecurityEnabledHeaderName.
187 */
188 private static final String AutodiscoverWsSecurityEnabledHeaderName =
189 "X-WSSecurity-Enabled";
190
191
192 /**
193 * HTTP header indicating that WS-Security/SymmetricKey Autodiscover service is enabled.
194 */
195
196 private static final String AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName =
197 "X-WSSecurity-SymmetricKey-Enabled";
198
199
200 /**
201 * HTTP header indicating that WS-Security/X509Cert Autodiscover service is enabled.
202 */
203
204 private static final String AutodiscoverWsSecurityX509CertEnabledHeaderName =
205 "X-WSSecurity-X509Cert-Enabled";
206
207
208 // Minimum request version for Autodiscover SOAP service.
209 /**
210 * The Constant MinimumRequestVersionForAutoDiscoverSoapService.
211 */
212 private static final ExchangeVersion
213 MinimumRequestVersionForAutoDiscoverSoapService =
214 ExchangeVersion.Exchange2010;
215
216 /**
217 * Default implementation of AutodiscoverRedirectionUrlValidationCallback.
218 * Always returns true indicating that the URL can be used.
219 *
220 * @param redirectionUrl the redirection url
221 * @return Returns true.
222 * @throws AutodiscoverLocalException the autodiscover local exception
223 */
224 private boolean defaultAutodiscoverRedirectionUrlValidationCallback(
225 String redirectionUrl) throws AutodiscoverLocalException {
226 throw new AutodiscoverLocalException(String.format(
227 "Autodiscover blocked a potentially insecure redirection to %s. To allow Autodiscover to follow the "
228 + "redirection, use the AutodiscoverUrl(string, AutodiscoverRedirectionUrlValidationCallback) "
229 + "overload.", redirectionUrl));
230 }
231
232 // Legacy Autodiscover
233
234 /**
235 * Calls the Autodiscover service to get configuration settings at the
236 * specified URL.
237 *
238 * @param <TSettings> the generic type
239 * @param cls the cls
240 * @param emailAddress the email address
241 * @param url the url
242 * @return The requested configuration settings. (TSettings The type of the
243 * settings to retrieve)
244 * @throws Exception the exception
245 */
246 private <TSettings extends ConfigurationSettingsBase>
247 TSettings getLegacyUserSettingsAtUrl(
248 Class<TSettings> cls, String emailAddress, URI url)
249 throws Exception {
250 this
251 .traceMessage(TraceFlags.AutodiscoverConfiguration,
252 String.format("Trying to call Autodiscover for %s on %s.", emailAddress, url));
253
254 TSettings settings = cls.newInstance();
255
256 HttpWebRequest request = null;
257 try {
258 request = this.prepareHttpWebRequestForUrl(url);
259
260 this.traceHttpRequestHeaders(
261 TraceFlags.AutodiscoverRequestHttpHeaders,
262 request);
263 // OutputStreamWriter out = new
264 // OutputStreamWriter(request.getOutputStream());
265 OutputStream urlOutStream = request.getOutputStream();
266
267 // If tracing is enabled, we generate the request in-memory so that we
268 // can pass it along to the ITraceListener. Then we copy the stream to
269 // the request stream.
270 if (this.isTraceEnabledFor(TraceFlags.AutodiscoverRequest)) {
271 ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
272
273 PrintWriter writer = new PrintWriter(memoryStream);
274 this.writeLegacyAutodiscoverRequest(emailAddress, settings, writer);
275 writer.flush();
276
277 this.traceXml(TraceFlags.AutodiscoverRequest, memoryStream);
278 // out.write(memoryStream.toString());
279 // out.close();
280 memoryStream.writeTo(urlOutStream);
281 urlOutStream.flush();
282 urlOutStream.close();
283 memoryStream.close();
284 } else {
285 PrintWriter writer = new PrintWriter(urlOutStream);
286 this.writeLegacyAutodiscoverRequest(emailAddress, settings, writer);
287
288 /* Flush Start */
289 writer.flush();
290 urlOutStream.flush();
291 urlOutStream.close();
292 /* Flush End */
293 }
294 request.executeRequest();
295 request.getResponseCode();
296 URI redirectUrl;
297 OutParam<URI> outParam = new OutParam<URI>();
298 if (this.tryGetRedirectionResponse(request, outParam)) {
299 redirectUrl = outParam.getParam();
300 settings.makeRedirectionResponse(redirectUrl);
301 return settings;
302 }
303 InputStream serviceResponseStream = request.getInputStream();
304 // If tracing is enabled, we read the entire response into a
305 // MemoryStream so that we
306 // can pass it along to the ITraceListener. Then we parse the response
307 // from the
308 // MemoryStream.
309 if (this.isTraceEnabledFor(TraceFlags.AutodiscoverResponse)) {
310 ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
311
312 while (true) {
313 int data = serviceResponseStream.read();
314 if (-1 == data) {
315 break;
316 } else {
317 memoryStream.write(data);
318 }
319 }
320 memoryStream.flush();
321
322 this.traceResponse(request, memoryStream);
323 ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(
324 memoryStream.toByteArray());
325 EwsXmlReader reader = new EwsXmlReader(memoryStreamIn);
326 reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
327 settings.loadFromXml(reader);
328
329 } else {
330 EwsXmlReader reader = new EwsXmlReader(serviceResponseStream);
331 reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
332 settings.loadFromXml(reader);
333 }
334
335 serviceResponseStream.close();
336 } finally {
337 if (request != null) {
338 try {
339 request.close();
340 } catch (Exception e2) {
341 // Ignore exception while closing the request.
342 }
343 }
344 }
345
346 return settings;
347 }
348
349 /**
350 * Writes the autodiscover request.
351 *
352 * @param emailAddress the email address
353 * @param settings the settings
354 * @param writer the writer
355 * @throws java.io.IOException Signals that an I/O exception has occurred.
356 */
357 private void writeLegacyAutodiscoverRequest(String emailAddress,
358 ConfigurationSettingsBase settings, PrintWriter writer)
359 throws IOException {
360 writer.write(String.format("<Autodiscover xmlns=\"%s\">", AutodiscoverRequestNamespace));
361 writer.write("<Request>");
362 writer.write(String.format("<EMailAddress>%s</EMailAddress>",
363 emailAddress));
364 writer.write(
365 String.format("<AcceptableResponseSchema>%s</AcceptableResponseSchema>", settings.getNamespace()));
366 writer.write("</Request>");
367 writer.write("</Autodiscover>");
368 }
369
370 /**
371 * Gets a redirection URL to an SSL-enabled Autodiscover service from the
372 * standard non-SSL Autodiscover URL.
373 *
374 * @param domainName the domain name
375 * @return A valid SSL-enabled redirection URL. (May be null)
376 * @throws EWSHttpException the EWS http exception
377 * @throws XMLStreamException the XML stream exception
378 * @throws IOException Signals that an I/O exception has occurred.
379 * @throws ServiceLocalException the service local exception
380 * @throws URISyntaxException the uRI syntax exception
381 */
382 private URI getRedirectUrl(String domainName)
383 throws EWSHttpException, XMLStreamException, IOException, ServiceLocalException, URISyntaxException {
384 String url = String.format(AutodiscoverLegacyHttpUrl, "autodiscover." + domainName);
385
386 traceMessage(TraceFlags.AutodiscoverConfiguration,
387 String.format("Trying to get Autodiscover redirection URL from %s.", url));
388
389 HttpWebRequest request = null;
390
391 try {
392 request = new HttpClientWebRequest(httpClient, httpContext);
393 request.setProxy(getWebProxy());
394
395 try {
396 request.setUrl(URI.create(url).toURL());
397 } catch (MalformedURLException e) {
398 String strErr = String.format("Incorrect format : %s", url);
399 throw new ServiceLocalException(strErr);
400 }
401
402 request.setRequestMethod("GET");
403 request.setAllowAutoRedirect(false);
404
405 // Do NOT allow authentication as this single request will be made over plain HTTP.
406 request.setAllowAuthentication(false);
407
408 prepareCredentials(request);
409
410 request.prepareConnection();
411 try {
412 request.executeRequest();
413 } catch (IOException e) {
414 traceMessage(TraceFlags.AutodiscoverConfiguration, "No Autodiscover redirection URL was returned.");
415 return null;
416 }
417
418 OutParam<URI> outParam = new OutParam<URI>();
419 if (tryGetRedirectionResponse(request, outParam)) {
420 return outParam.getParam();
421 }
422 } finally {
423 if (request != null) {
424 try {
425 request.close();
426 } catch (Exception e) {
427 // Ignore exception when closing the request
428 }
429 }
430 }
431
432 traceMessage(TraceFlags.AutodiscoverConfiguration, "No Autodiscover redirection URL was returned.");
433 return null;
434 }
435
436 /**
437 * Tries the get redirection response.
438 *
439 * @param request the request
440 * @param redirectUrl the redirect URL
441 * @return true if a valid redirection URL was found
442 * @throws XMLStreamException the XML stream exception
443 * @throws IOException signals that an I/O exception has occurred.
444 * @throws EWSHttpException the EWS http exception
445 */
446 private boolean tryGetRedirectionResponse(HttpWebRequest request,
447 OutParam<URI> redirectUrl) throws XMLStreamException, IOException,
448 EWSHttpException {
449 // redirectUrl = null;
450 if (AutodiscoverRequest.isRedirectionResponse(request)) {
451 // Get the redirect location and verify that it's valid.
452 String location = request.getResponseHeaderField("Location");
453
454 if (!(location == null || location.isEmpty())) {
455 try {
456 redirectUrl.setParam(new URI(location));
457
458 // Check if URL is SSL and that the path matches.
459 if ((redirectUrl.getParam().getScheme().toLowerCase()
460 .equals("https")) &&
461 (redirectUrl.getParam().getPath()
462 .equalsIgnoreCase(
463 AutodiscoverLegacyPath))) {
464 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
465 String.format("Redirection URL found: '%s'",
466 redirectUrl.getParam().toString()));
467
468 return true;
469 }
470 } catch (URISyntaxException ex) {
471 this
472 .traceMessage(
473 TraceFlags.AutodiscoverConfiguration,
474 String
475 .format(
476 "Invalid redirection URL " +
477 "was returned: '%s'",
478 location));
479 return false;
480 }
481 }
482 }
483 return false;
484 }
485
486 /**
487 * Calls the legacy Autodiscover service to retrieve configuration settings.
488 *
489 * @param <TSettings> the generic type
490 * @param cls the cls
491 * @param emailAddress The email address to retrieve configuration settings for.
492 * @return The requested configuration settings.
493 * @throws Exception the exception
494 */
495 protected <TSettings extends ConfigurationSettingsBase>
496 TSettings getLegacyUserSettings(
497 Class<TSettings> cls, String emailAddress) throws Exception {
498 /*int currentHop = 1;
499 return this.internalGetConfigurationSettings(cls, emailAddress,
500 currentHop);*/
501
502 // If Url is specified, call service directly.
503 if (this.url != null) {
504 // this.Uri is intended for Autodiscover SOAP service, convert to Legacy endpoint URL.
505 URI autodiscoverUrl = new URI(this.url.toString() + AutodiscoverLegacyPath);
506 return this.getLegacyUserSettingsAtUrl(cls, emailAddress, autodiscoverUrl);
507 }
508
509 // If Domain is specified, figure out the endpoint Url and call service.
510 else if (!(this.domain == null || this.domain.isEmpty())) {
511 URI autodiscoverUrl = new URI(String.format(AutodiscoverLegacyHttpsUrl, this.domain));
512 return this.getLegacyUserSettingsAtUrl(cls,
513 emailAddress, autodiscoverUrl);
514 } else {
515 // No Url or Domain specified, need to
516 //figure out which endpoint to use.
517 int currentHop = 1;
518 OutParam<Integer> outParam = new OutParam<Integer>();
519 outParam.setParam(currentHop);
520 List<String> redirectionEmailAddresses = new ArrayList<String>();
521 return this.internalGetLegacyUserSettings(
522 cls,
523 emailAddress,
524 redirectionEmailAddresses,
525 outParam);
526 }
527 }
528
529 /**
530 * Calls the Autodiscover service to retrieve configuration settings.
531 *
532 * @param <TSettings> the generic type
533 * @param cls the cls
534 * @param emailAddress The email address to retrieve configuration settings for.
535 * @param currentHop Current number of redirection urls/addresses attempted so far.
536 * @return The requested configuration settings.
537 * @throws Exception the exception
538 */
539 private <TSettings extends ConfigurationSettingsBase>
540 TSettings internalGetLegacyUserSettings(
541 Class<TSettings> cls,
542 String emailAddress,
543 List<String> redirectionEmailAddresses,
544 OutParam<Integer> currentHop)
545 throws Exception {
546 String domainName = EwsUtilities.domainFromEmailAddress(emailAddress);
547
548 int scpUrlCount;
549 OutParam<Integer> outParamInt = new OutParam<Integer>();
550 List<URI> urls = this.getAutodiscoverServiceUrls(domainName, outParamInt);
551 scpUrlCount = outParamInt.getParam();
552 if (urls.size() == 0) {
553 throw new ServiceValidationException(
554 "This Autodiscover request requires that either the Domain or Url be specified.");
555 }
556
557 // Assume caller is not inside the Intranet, regardless of whether SCP
558 // Urls
559 // were returned or not. SCP Urls are only relevent if one of them
560 // returns
561 // valid Autodiscover settings.
562 this.isExternal = true;
563
564 int currentUrlIndex = 0;
565
566 // Used to save exception for later reporting.
567 Exception delayedException = null;
568 TSettings settings;
569
570 do {
571 URI autodiscoverUrl = urls.get(currentUrlIndex);
572 boolean isScpUrl = currentUrlIndex < scpUrlCount;
573
574 try {
575 settings = this.getLegacyUserSettingsAtUrl(cls,
576 emailAddress, autodiscoverUrl);
577
578 switch (settings.getResponseType()) {
579 case Success:
580 // Not external if Autodiscover endpoint found via SCP
581 // returned the settings.
582 if (isScpUrl) {
583 this.isExternal = false;
584 }
585 this.url = autodiscoverUrl;
586 return settings;
587 case RedirectUrl:
588 if (currentHop.getParam() < AutodiscoverMaxRedirections) {
589 currentHop.setParam(currentHop.getParam() + 1);
590
591 this
592 .traceMessage(
593 TraceFlags.AutodiscoverResponse,
594 String
595 .format(
596 "Autodiscover " +
597 "service " +
598 "returned " +
599 "redirection URL '%s'.",
600 settings
601 .getRedirectTarget()));
602
603 urls.add(currentUrlIndex, new URI(
604 settings.getRedirectTarget()));
605
606 break;
607 } else {
608 throw new MaximumRedirectionHopsExceededException();
609 }
610 case RedirectAddress:
611 if (currentHop.getParam() < AutodiscoverMaxRedirections) {
612 currentHop.setParam(currentHop.getParam() + 1);
613
614 this
615 .traceMessage(
616 TraceFlags.AutodiscoverResponse,
617 String
618 .format(
619 "Autodiscover " +
620 "service " +
621 "returned " +
622 "redirection email " +
623 "address '%s'.",
624 settings
625 .getRedirectTarget()));
626 // Bug E14:255576 If this email address was already tried, we may have a loop
627 // in SCP lookups. Disable consideration of SCP records.
628 this.disableScpLookupIfDuplicateRedirection(
629 settings.getRedirectTarget(),
630 redirectionEmailAddresses);
631
632 return this.internalGetLegacyUserSettings(cls,
633 settings.getRedirectTarget(),
634 redirectionEmailAddresses,
635 currentHop);
636 } else {
637 throw new MaximumRedirectionHopsExceededException();
638 }
639 case Error:
640 // Don't treat errors from an SCP-based Autodiscover service
641 // to be conclusive.
642 // We'll try the next one and record the error for later.
643 if (isScpUrl) {
644 this
645 .traceMessage(
646 TraceFlags.AutodiscoverConfiguration,
647 "Error returned by " +
648 "Autodiscover service " +
649 "found via SCP, treating " +
650 "as inconclusive.");
651
652 delayedException = new AutodiscoverRemoteException(
653 "The Autodiscover service returned an error.", settings.getError());
654 currentUrlIndex++;
655 } else {
656 throw new AutodiscoverRemoteException("The Autodiscover service returned an error.", settings.getError());
657 }
658 break;
659 default:
660 EwsUtilities
661 .ewsAssert(false, "Autodiscover.GetConfigurationSettings",
662 "An unexpected error has occured. This code path should never be reached.");
663 break;
664 }
665 } catch (XMLStreamException ex) {
666 this.traceMessage(TraceFlags.AutodiscoverConfiguration, String
667 .format("%s failed: XML parsing error: %s", url, ex
668 .getMessage()));
669
670 // The content at the URL wasn't a valid response, let's try the
671 // next.
672 currentUrlIndex++;
673 } catch (IOException ex) {
674 this.traceMessage(
675 TraceFlags.AutodiscoverConfiguration,
676 String.format("%s failed: I/O error: %s",
677 url, ex.getMessage()));
678
679 // The content at the URL wasn't a valid response, let's try the next.
680 currentUrlIndex++;
681 } catch (Exception ex) {
682 HttpWebRequest response = null;
683 URI redirectUrl;
684 OutParam<URI> outParam1 = new OutParam<URI>();
685 if ((response != null) &&
686 this.tryGetRedirectionResponse(response, outParam1)) {
687 redirectUrl = outParam1.getParam();
688 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
689 String.format(
690 "Host returned a redirection to url %s",
691 redirectUrl.toString()));
692
693 currentHop.setParam(currentHop.getParam() + 1);
694 urls.add(currentUrlIndex, redirectUrl);
695 } else {
696 if (response != null) {
697 this.processHttpErrorResponse(response, ex);
698
699 }
700
701 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
702 String.format("%s failed: %s (%s)", url, ex
703 .getClass().getName(), ex.getMessage()));
704
705 // The url did not work, let's try the next.
706 currentUrlIndex++;
707 }
708 }
709 } while (currentUrlIndex < urls.size());
710
711 // If we got this far it's because none of the URLs we tried have
712 // worked. As a next-to-last chance, use GetRedirectUrl to
713 // try to get a redirection URL using an HTTP GET on a non-SSL
714 // Autodiscover endpoint. If successful, use this
715 // redirection URL to get the configuration settings for this email
716 // address. (This will be a common scenario for
717 // DataCenter deployments).
718 URI redirectionUrl = this.getRedirectUrl(domainName);
719 OutParam<TSettings> outParam = new OutParam<TSettings>();
720 if ((redirectionUrl != null)
721 && this.tryLastChanceHostRedirection(cls, emailAddress,
722 redirectionUrl, outParam)) {
723 settings = outParam.getParam();
724 return settings;
725 } else {
726 // Getting a redirection URL from an HTTP GET failed too. As a last
727 // chance, try to get an appropriate SRV Record
728 // using DnsQuery. If successful, use this redirection URL to get
729 // the configuration settings for this email address.
730 redirectionUrl = this.getRedirectionUrlFromDnsSrvRecord(domainName);
731 if ((redirectionUrl != null)
732 && this.tryLastChanceHostRedirection(cls, emailAddress,
733 redirectionUrl, outParam)) {
734 return outParam.getParam();
735 }
736
737 // If there was an earlier exception, throw it.
738 if (delayedException != null) {
739 throw delayedException;
740 }
741
742 throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
743 }
744 }
745
746 /**
747 * Get an autodiscover SRV record in DNS and construct autodiscover URL.
748 *
749 * @param domainName Name of the domain.
750 * @return Autodiscover URL (may be null if lookup failed)
751 * @throws Exception the exception
752 */
753 protected URI getRedirectionUrlFromDnsSrvRecord(String domainName)
754 throws Exception {
755
756 this
757 .traceMessage(
758 TraceFlags.AutodiscoverConfiguration,
759 String
760 .format(
761 "Trying to get Autodiscover host " +
762 "from DNS SRV record for %s.",
763 domainName));
764
765 String hostname = this.dnsClient
766 .findAutodiscoverHostFromSrv(domainName);
767 if (!(hostname == null || hostname.isEmpty())) {
768 this
769 .traceMessage(TraceFlags.AutodiscoverConfiguration,
770 String.format(
771 "Autodiscover host %s was returned.",
772 hostname));
773
774 return new URI(String.format(AutodiscoverLegacyHttpsUrl,
775 hostname));
776 } else {
777 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
778 "No matching Autodiscover DNS SRV records were found.");
779
780 return null;
781 }
782 }
783
784 /**
785 * Tries to get Autodiscover settings using redirection Url.
786 *
787 * @param <TSettings> the generic type
788 * @param cls the cls
789 * @param emailAddress The email address.
790 * @param redirectionUrl Redirection Url.
791 * @param settings The settings.
792 * @return boolean The boolean.
793 * @throws AutodiscoverLocalException the autodiscover local exception
794 * @throws AutodiscoverRemoteException the autodiscover remote exception
795 * @throws Exception the exception
796 */
797 private <TSettings extends ConfigurationSettingsBase> boolean
798 tryLastChanceHostRedirection(
799 Class<TSettings> cls, String emailAddress, URI redirectionUrl,
800 OutParam<TSettings> settings) throws AutodiscoverLocalException,
801 AutodiscoverRemoteException, Exception {
802 List<String> redirectionEmailAddresses = new ArrayList<String>();
803
804 // Bug 60274: Performing a non-SSL HTTP GET to retrieve a redirection
805 // URL is potentially unsafe. We allow the caller
806 // to specify delegate to be called to determine whether we are allowed
807 // to use the redirection URL.
808 if (this
809 .callRedirectionUrlValidationCallback(redirectionUrl.toString())) {
810 for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
811 try {
812 settings.setParam(this.getLegacyUserSettingsAtUrl(cls,
813 emailAddress, redirectionUrl));
814
815 switch (settings.getParam().getResponseType()) {
816 case Success:
817 return true;
818 case Error:
819 throw new AutodiscoverRemoteException("The Autodiscover service returned an error.", settings.getParam()
820 .getError());
821 case RedirectAddress:
822 // If this email address was already tried,
823 //we may have a loop
824 // in SCP lookups. Disable consideration of SCP records.
825 this.disableScpLookupIfDuplicateRedirection(settings.getParam().getRedirectTarget(),
826 redirectionEmailAddresses);
827 OutParam<Integer> outParam = new OutParam<Integer>();
828 outParam.setParam(currentHop);
829 settings.setParam(
830 this.internalGetLegacyUserSettings(cls,
831 emailAddress,
832 redirectionEmailAddresses,
833 outParam));
834 currentHop = outParam.getParam();
835 return true;
836 case RedirectUrl:
837 try {
838 redirectionUrl = new URI(settings.getParam()
839 .getRedirectTarget());
840 } catch (URISyntaxException ex) {
841 this
842 .traceMessage(
843 TraceFlags.
844 AutodiscoverConfiguration,
845 String
846 .format(
847 "Service " +
848 "returned " +
849 "invalid " +
850 "redirection " +
851 "URL %s",
852 settings
853 .getParam()
854 .getRedirectTarget()));
855 return false;
856 }
857 break;
858 default:
859 String failureMessage = String.format(
860 "Autodiscover call at %s failed with error %s, target %s",
861 redirectionUrl,
862 settings.getParam().getResponseType(),
863 settings.getParam().getRedirectTarget());
864 this.traceMessage(
865 TraceFlags.AutodiscoverConfiguration, failureMessage);
866
867 return false;
868 }
869 } catch (XMLStreamException ex) {
870 // If the response is malformed, it wasn't a valid
871 // Autodiscover endpoint.
872 this
873 .traceMessage(TraceFlags.AutodiscoverConfiguration,
874 String.format(
875 "%s failed: XML parsing error: %s",
876 redirectionUrl.toString(), ex
877 .getMessage()));
878 return false;
879 } catch (IOException ex) {
880 this.traceMessage(
881 TraceFlags.AutodiscoverConfiguration,
882 String.format("%s failed: I/O error: %s",
883 redirectionUrl, ex.getMessage()));
884 return false;
885 } catch (Exception ex) {
886 // TODO: BUG response is always null
887 HttpWebRequest response = null;
888 OutParam<URI> outParam = new OutParam<URI>();
889 if ((response != null)
890 && this.tryGetRedirectionResponse(response,
891 outParam)) {
892 redirectionUrl = outParam.getParam();
893 this
894 .traceMessage(
895 TraceFlags.AutodiscoverConfiguration,
896 String
897 .format(
898 "Host returned a " +
899 "redirection" +
900 " to url %s",
901 redirectionUrl));
902
903 } else {
904 if (response != null) {
905 this.processHttpErrorResponse(response, ex);
906 }
907
908 this
909 .traceMessage(
910 TraceFlags.AutodiscoverConfiguration,
911 String.format("%s failed: %s (%s)",
912 url, ex.getClass().getName(),
913 ex.getMessage()));
914 return false;
915 }
916 }
917 }
918 }
919
920 return false;
921 }
922
923 /**
924 * Disables SCP lookup if duplicate email address redirection.
925 *
926 * @param emailAddress The email address to use.
927 * @param redirectionEmailAddresses The list of prior redirection email addresses.
928 */
929 private void disableScpLookupIfDuplicateRedirection(
930 String emailAddress,
931 List<String> redirectionEmailAddresses) {
932 // SMTP addresses are case-insensitive so entries are converted to lower-case.
933 emailAddress = emailAddress.toLowerCase();
934
935 if (redirectionEmailAddresses.contains(emailAddress)) {
936 this.enableScpLookup = false;
937 } else {
938 redirectionEmailAddresses.add(emailAddress);
939 }
940 }
941
942 /**
943 * Gets user settings from Autodiscover legacy endpoint.
944 *
945 * @param emailAddress The email address to use.
946 * @param requestedSettings The requested settings.
947 * @return GetUserSettingsResponse
948 * @throws Exception on error
949 */
950 protected GetUserSettingsResponse internalGetLegacyUserSettings(
951 String emailAddress,
952 List<UserSettingName> requestedSettings) throws Exception {
953 // Cannot call legacy Autodiscover service with WindowsLive and other WSSecurity-based credential
954 if ((this.getCredentials() != null) && (this.getCredentials() instanceof WSSecurityBasedCredentials)) {
955 throw new AutodiscoverLocalException(
956 "WindowsLiveCredentials can't be used with this Autodiscover endpoint.");
957 }
958
959 OutlookConfigurationSettings settings = this.getLegacyUserSettings(
960 OutlookConfigurationSettings.class,
961 emailAddress);
962
963
964
965 return settings.convertSettings(emailAddress, requestedSettings);
966 }
967
968 /**
969 * Calls the SOAP Autodiscover service
970 * for user settings for a single SMTP address.
971 *
972 * @param smtpAddress SMTP address.
973 * @param requestedSettings The requested settings.
974 * @return GetUserSettingsResponse
975 * @throws Exception on error
976 */
977 protected GetUserSettingsResponse internalGetSoapUserSettings(
978 String smtpAddress,
979 List<UserSettingName> requestedSettings) throws Exception {
980 List<String> smtpAddresses = new ArrayList<String>();
981 smtpAddresses.add(smtpAddress);
982
983 List<String> redirectionEmailAddresses = new ArrayList<String>();
984 redirectionEmailAddresses.add(smtpAddress.toLowerCase());
985
986 for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
987 GetUserSettingsResponse response = this.getUserSettings(smtpAddresses,
988 requestedSettings).getTResponseAtIndex(0);
989
990 switch (response.getErrorCode()) {
991 case RedirectAddress:
992 this.traceMessage(
993 TraceFlags.AutodiscoverResponse,
994 String.format("Autodiscover service returned redirection email address '%s'.",
995 response.getRedirectTarget()));
996
997 smtpAddresses.clear();
998 smtpAddresses.add(response.getRedirectTarget().
999 toLowerCase());
1000 this.url = null;
1001 this.domain = null;
1002
1003 // If this email address was already tried,
1004 //we may have a loop
1005 // in SCP lookups. Disable consideration of SCP records.
1006 this.disableScpLookupIfDuplicateRedirection(response.getRedirectTarget(),
1007 redirectionEmailAddresses);
1008 break;
1009
1010 case RedirectUrl:
1011 this.traceMessage(
1012 TraceFlags.AutodiscoverResponse,
1013 String.format("Autodiscover service returned redirection URL '%s'.",
1014 response.getRedirectTarget()));
1015
1016 //this.url = new URI(response.getRedirectTarget());
1017 this.url = this.getCredentials().adjustUrl(new URI(response.getRedirectTarget()));
1018 break;
1019
1020 case NoError:
1021 default:
1022 return response;
1023 }
1024 }
1025
1026 throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
1027 }
1028
1029 /**
1030 * Gets the user settings using Autodiscover SOAP service.
1031 *
1032 * @param smtpAddresses The SMTP addresses of the users.
1033 * @param settings The settings.
1034 * @return GetUserSettingsResponseCollection Object.
1035 * @throws Exception the exception
1036 */
1037 protected GetUserSettingsResponseCollection getUserSettings(
1038 final List<String> smtpAddresses, List<UserSettingName> settings)
1039 throws Exception {
1040 EwsUtilities.validateParam(smtpAddresses, "smtpAddresses");
1041 EwsUtilities.validateParam(settings, "settings");
1042
1043 return this.getSettings(
1044 GetUserSettingsResponseCollection.class, UserSettingName.class,
1045 smtpAddresses, settings, null, this,
1046 new IFuncDelegate<String>() {
1047 public String func() throws FormatException {
1048 return EwsUtilities
1049 .domainFromEmailAddress(smtpAddresses.get(0));
1050 }
1051 });
1052 }
1053
1054 /**
1055 * Gets user or domain settings using Autodiscover SOAP service.
1056 *
1057 * @param <TGetSettingsResponseCollection> the generic type
1058 * @param <TSettingName> the generic type
1059 * @param cls the cls
1060 * @param cls1 the cls1
1061 * @param identities Either the domains or the SMTP addresses of the users.
1062 * @param settings The settings.
1063 * @param requestedVersion Requested version of the Exchange service.
1064 * @param getSettingsMethod The method to use.
1065 * @param getDomainMethod The method to calculate the domain value.
1066 * @return TGetSettingsResponse Collection.
1067 * @throws Exception the exception
1068 */
1069 private <TGetSettingsResponseCollection, TSettingName>
1070 TGetSettingsResponseCollection getSettings(
1071 Class<TGetSettingsResponseCollection> cls,
1072 Class<TSettingName> cls1,
1073 List<String> identities,
1074 List<TSettingName> settings,
1075 ExchangeVersion requestedVersion,
1076 IFunctionDelegate<List<String>, List<TSettingName>,
1077 TGetSettingsResponseCollection> getSettingsMethod,
1078 IFuncDelegate<String> getDomainMethod) throws Exception {
1079 TGetSettingsResponseCollection response;
1080
1081 // Autodiscover service only exists in E14 or later.
1082 if (this.getRequestedServerVersion().compareTo(
1083 MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
1084 throw new ServiceVersionException(String.format(
1085 "The Autodiscover service only supports %s or a later version.",
1086 MinimumRequestVersionForAutoDiscoverSoapService));
1087 }
1088
1089 // If Url is specified, call service directly.
1090 if (this.url != null) {
1091 URI autodiscoverUrl = this.url;
1092 response = getSettingsMethod.func(identities, settings,
1093 requestedVersion, this.url);
1094 this.url = autodiscoverUrl;
1095 return response;
1096 }
1097 // If Domain is specified, determine endpoint Url and call service.
1098 else if (!(this.domain == null || this.domain.isEmpty())) {
1099 URI autodiscoverUrl = this.getAutodiscoverEndpointUrl(this.domain);
1100 response = getSettingsMethod.func(identities, settings,
1101 requestedVersion,
1102 autodiscoverUrl);
1103
1104 // If we got this far, response was successful, set Url.
1105 this.url = autodiscoverUrl;
1106 return response;
1107 }
1108 // No Url or Domain specified, need to figure out which endpoint(s) to
1109 // try.
1110 else {
1111 // Assume caller is not inside the Intranet, regardless of whether
1112 // SCP Urls
1113 // were returned or not. SCP Urls are only relevent if one of them
1114 // returns
1115 // valid Autodiscover settings.
1116 this.isExternal = true;
1117
1118 URI autodiscoverUrl;
1119
1120 String domainName = getDomainMethod.func();
1121 int scpHostCount;
1122 OutParam<Integer> outParam = new OutParam<Integer>();
1123 List<String> hosts = this.getAutodiscoverServiceHosts(domainName,
1124 outParam);
1125 scpHostCount = outParam.getParam();
1126 if (hosts.size() == 0) {
1127 throw new ServiceValidationException(
1128 "This Autodiscover request requires that either the Domain or Url be specified.");
1129 }
1130
1131 for (int currentHostIndex = 0; currentHostIndex < hosts.size(); currentHostIndex++) {
1132 String host = hosts.get(currentHostIndex);
1133 boolean isScpHost = currentHostIndex < scpHostCount;
1134 OutParam<URI> outParams = new OutParam<URI>();
1135 if (this.tryGetAutodiscoverEndpointUrl(host, outParams)) {
1136 autodiscoverUrl = outParams.getParam();
1137 response = getSettingsMethod.func(identities, settings,
1138 requestedVersion,
1139 autodiscoverUrl);
1140
1141 // If we got this far, the response was successful, set Url.
1142 this.url = autodiscoverUrl;
1143
1144 // Not external if Autodiscover endpoint found via SCP
1145 // returned the settings.
1146 if (isScpHost) {
1147 this.isExternal = false;
1148 }
1149
1150 return response;
1151 }
1152 }
1153
1154 // Next-to-last chance: try unauthenticated GET over HTTP to be
1155 // redirected to appropriate service endpoint.
1156 autodiscoverUrl = this.getRedirectUrl(domainName);
1157 OutParam<URI> outParamUrl = new OutParam<URI>();
1158 if ((autodiscoverUrl != null) &&
1159 this
1160 .callRedirectionUrlValidationCallback(
1161 autodiscoverUrl.toString()) &&
1162 this.tryGetAutodiscoverEndpointUrl(autodiscoverUrl
1163 .getHost(), outParamUrl)) {
1164 autodiscoverUrl = outParamUrl.getParam();
1165 response = getSettingsMethod.func(identities, settings,
1166 requestedVersion,
1167 autodiscoverUrl);
1168
1169 // If we got this far, the response was successful, set Url.
1170 this.url = autodiscoverUrl;
1171
1172 return response;
1173 }
1174
1175 // Last Chance: try to read autodiscover SRV Record from DNS. If we
1176 // find one, use
1177 // the hostname returned to construct an Autodiscover endpoint URL.
1178 autodiscoverUrl = this
1179 .getRedirectionUrlFromDnsSrvRecord(domainName);
1180 if ((autodiscoverUrl != null) &&
1181 this
1182 .callRedirectionUrlValidationCallback(
1183 autodiscoverUrl.toString()) &&
1184 this.tryGetAutodiscoverEndpointUrl(autodiscoverUrl
1185 .getHost(), outParamUrl)) {
1186 autodiscoverUrl = outParamUrl.getParam();
1187 response = getSettingsMethod.func(identities, settings,
1188 requestedVersion,
1189 autodiscoverUrl);
1190
1191 // If we got this far, the response was successful, set Url.
1192 this.url = autodiscoverUrl;
1193
1194 return response;
1195 } else {
1196 throw new AutodiscoverLocalException("The Autodiscover service couldn't be located.");
1197 }
1198 }
1199 }
1200
1201 /**
1202 * Gets settings for one or more users.
1203 *
1204 * @param smtpAddresses The SMTP addresses of the users.
1205 * @param settings The settings.
1206 * @param requestedVersion Requested version of the Exchange service.
1207 * @param autodiscoverUrl The autodiscover URL.
1208 * @return GetUserSettingsResponse collection.
1209 * @throws ServiceLocalException the service local exception
1210 * @throws Exception the exception
1211 */
1212 private GetUserSettingsResponseCollection internalGetUserSettings(
1213 List<String> smtpAddresses, List<UserSettingName> settings,
1214 ExchangeVersion requestedVersion,
1215 URI autodiscoverUrl) throws ServiceLocalException, Exception {
1216 // The response to GetUserSettings can be a redirection. Execute
1217 // GetUserSettings until we get back
1218 // a valid response or we've followed too many redirections.
1219 for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
1220 GetUserSettingsRequest request = new GetUserSettingsRequest(this,
1221 autodiscoverUrl);
1222 request.setSmtpAddresses(smtpAddresses);
1223 request.setSettings(settings);
1224 GetUserSettingsResponseCollection response = request.execute();
1225
1226 // Did we get redirected?
1227 if (response.getErrorCode() == AutodiscoverErrorCode.RedirectUrl
1228 && response.getRedirectionUrl() != null) {
1229 this.traceMessage(
1230 TraceFlags.AutodiscoverConfiguration,
1231 String.format("Request to %s returned redirection to %s",
1232 autodiscoverUrl.toString(), response.getRedirectionUrl()));
1233
1234 autodiscoverUrl = response.getRedirectionUrl();
1235 } else {
1236 return response;
1237 }
1238 }
1239
1240 this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
1241 "Maximum number of redirection hops %d exceeded",
1242 AutodiscoverMaxRedirections));
1243
1244 throw new MaximumRedirectionHopsExceededException();
1245 }
1246
1247 /**
1248 * Gets the domain settings using Autodiscover SOAP service.
1249 *
1250 * @param domains The domains.
1251 * @param settings The settings.
1252 * @param requestedVersion Requested version of the Exchange service.
1253 * @return GetDomainSettingsResponse collection.
1254 * @throws Exception the exception
1255 */
1256 protected GetDomainSettingsResponseCollection getDomainSettings(
1257 final List<String> domains, List<DomainSettingName> settings,
1258 ExchangeVersion requestedVersion)
1259 throws Exception {
1260 EwsUtilities.validateParam(domains, "domains");
1261 EwsUtilities.validateParam(settings, "settings");
1262
1263 return this.getSettings(
1264 GetDomainSettingsResponseCollection.class,
1265 DomainSettingName.class, domains, settings,
1266 requestedVersion, this,
1267 new IFuncDelegate<String>() {
1268 public String func() {
1269 return domains.get(0);
1270 }
1271 });
1272 }
1273
1274 /**
1275 * Gets settings for one or more domains.
1276 *
1277 * @param domains The domains.
1278 * @param settings The settings.
1279 * @param requestedVersion Requested version of the Exchange service.
1280 * @param autodiscoverUrl The autodiscover URL.
1281 * @return GetDomainSettingsResponse Collection.
1282 * @throws ServiceLocalException the service local exception
1283 * @throws Exception the exception
1284 */
1285 private GetDomainSettingsResponseCollection internalGetDomainSettings(
1286 List<String> domains, List<DomainSettingName> settings,
1287 ExchangeVersion requestedVersion,
1288 URI autodiscoverUrl) throws ServiceLocalException, Exception {
1289 // The response to GetDomainSettings can be a redirection. Execute
1290 // GetDomainSettings until we get back
1291 // a valid response or we've followed too many redirections.
1292 for (int currentHop = 0; currentHop < AutodiscoverService.AutodiscoverMaxRedirections; currentHop++) {
1293 GetDomainSettingsRequest request = new GetDomainSettingsRequest(
1294 this, autodiscoverUrl);
1295 request.setDomains(domains);
1296 request.setSettings(settings);
1297 request.setRequestedVersion(requestedVersion);
1298 GetDomainSettingsResponseCollection response = request.execute();
1299
1300 // Did we get redirected?
1301 if (response.getErrorCode() == AutodiscoverErrorCode.RedirectUrl
1302 && response.getRedirectionUrl() != null) {
1303 autodiscoverUrl = response.getRedirectionUrl();
1304 } else {
1305 return response;
1306 }
1307 }
1308
1309 this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
1310 "Maximum number of redirection hops %d exceeded",
1311 AutodiscoverMaxRedirections));
1312
1313 throw new MaximumRedirectionHopsExceededException();
1314 }
1315
1316 /**
1317 * Gets the autodiscover endpoint URL.
1318 *
1319 * @param host The host.
1320 * @return URI The URI.
1321 * @throws Exception the exception
1322 */
1323 private URI getAutodiscoverEndpointUrl(String host) throws Exception {
1324 URI autodiscoverUrl = null;
1325 OutParam<URI> outParam = new OutParam<URI>();
1326 if (this.tryGetAutodiscoverEndpointUrl(host, outParam)) {
1327 return autodiscoverUrl;
1328 } else {
1329 throw new AutodiscoverLocalException(
1330 "No appropriate Autodiscover SOAP or WS-Security endpoint is available.");
1331 }
1332 }
1333
1334 /**
1335 * Tries the get Autodiscover Service endpoint URL.
1336 *
1337 * @param host The host.
1338 * @param url the url
1339 * @return boolean The boolean.
1340 * @throws Exception the exception
1341 */
1342 private boolean tryGetAutodiscoverEndpointUrl(String host,
1343 OutParam<URI> url)
1344 throws Exception {
1345 EnumSet<AutodiscoverEndpoints> endpoints;
1346 OutParam<EnumSet<AutodiscoverEndpoints>> outParam =
1347 new OutParam<EnumSet<AutodiscoverEndpoints>>();
1348 if (this.tryGetEnabledEndpointsForHost(host, outParam)) {
1349 endpoints = outParam.getParam();
1350 url
1351 .setParam(new URI(String.format(AutodiscoverSoapHttpsUrl,
1352 host)));
1353
1354 // Make sure that at least one of the non-legacy endpoints is
1355 // available.
1356 if ((!endpoints.contains(AutodiscoverEndpoints.Soap)) &&
1357 (!endpoints.contains(
1358 AutodiscoverEndpoints.WsSecurity))
1359 // (endpoints .contains( AutodiscoverEndpoints.WSSecuritySymmetricKey) ) &&
1360 //(endpoints .contains( AutodiscoverEndpoints.WSSecurityX509Cert))
1361 ) {
1362 this
1363 .traceMessage(
1364 TraceFlags.AutodiscoverConfiguration,
1365 String
1366 .format(
1367 "No Autodiscover endpoints " +
1368 "are available for host %s",
1369 host));
1370
1371 return false;
1372 }
1373
1374 // If we have WLID credential, make sure that we have a WS-Security
1375 // endpoint
1376 /*
1377 if (this.getCredentials() instanceof WindowsLiveCredentials) {
1378 if (endpoints.contains(AutodiscoverEndpoints.WsSecurity)) {
1379 this
1380 .traceMessage(
1381 TraceFlags.AutodiscoverConfiguration,
1382 String
1383 .format(
1384 "No Autodiscover " +
1385 "WS-Security " +
1386 "endpoint is available" +
1387 " for host %s",
1388 host));
1389
1390 return false;
1391 } else {
1392 url.setParam(new URI(String.format(
1393 AutodiscoverSoapWsSecurityHttpsUrl, host)));
1394 }
1395 }
1396 else if (this.getCredentials() instanceof PartnerTokenCredentials)
1397 {
1398 if (endpoints.contains( AutodiscoverEndpoints.WSSecuritySymmetricKey))
1399 {
1400 this.traceMessage(
1401 TraceFlags.AutodiscoverConfiguration,
1402 String.format("No Autodiscover WS-Security/SymmetricKey endpoint is available for host {0}", host));
1403
1404 return false;
1405 }
1406 else
1407 {
1408 url.setParam( new URI(String.format(AutodiscoverSoapWsSecuritySymmetricKeyHttpsUrl, host)));
1409 }
1410 }
1411 else if (this.getCredentials()instanceof X509CertificateCredentials)
1412 {
1413 if ((endpoints.contains(AutodiscoverEndpoints.WSSecurityX509Cert))
1414 {
1415 this.traceMessage(
1416 TraceFlags.AutodiscoverConfiguration,
1417 String.format("No Autodiscover WS-Security/X509Cert endpoint is available for host {0}", host));
1418
1419 return false;
1420 }
1421 else
1422 {
1423 url.setParam( new URI(String.format(AutodiscoverSoapWsSecurityX509CertHttpsUrl, host)));
1424 }
1425 }
1426 */
1427 return true;
1428
1429
1430 } else {
1431 this
1432 .traceMessage(
1433 TraceFlags.AutodiscoverConfiguration,
1434 String
1435 .format(
1436 "No Autodiscover endpoints " +
1437 "are available for host %s",
1438 host));
1439
1440 return false;
1441 }
1442 }
1443
1444 /**
1445 * Gets the list of autodiscover service URLs.
1446 *
1447 * @param domainName Domain name.
1448 * @param scpHostCount Count of hosts found via SCP lookup.
1449 * @return List of Autodiscover URLs.
1450 * @throws java.net.URISyntaxException the URI Syntax exception
1451 */
1452 protected List<URI> getAutodiscoverServiceUrls(String domainName,
1453 OutParam<Integer> scpHostCount) throws URISyntaxException {
1454 List<URI> urls;
1455
1456 urls = new ArrayList<URI>();
1457
1458 scpHostCount.setParam(urls.size());
1459
1460 // As a fallback, add autodiscover URLs base on the domain name.
1461 urls.add(new URI(String.format(AutodiscoverLegacyHttpsUrl,
1462 domainName)));
1463 urls.add(new URI(String.format(AutodiscoverLegacyHttpsUrl,
1464 "autodiscover." + domainName)));
1465
1466 return urls;
1467 }
1468
1469 /**
1470 * Gets the list of autodiscover service hosts.
1471 *
1472 * @param domainName Domain name.
1473 * @param outParam the out param
1474 * @return List of hosts.
1475 * @throws java.net.URISyntaxException the uRI syntax exception
1476 * @throws ClassNotFoundException the class not found exception
1477 */
1478 protected List<String> getAutodiscoverServiceHosts(String domainName,
1479 OutParam<Integer> outParam) throws URISyntaxException,
1480 ClassNotFoundException {
1481
1482 List<URI> urls = this.getAutodiscoverServiceUrls(domainName, outParam);
1483 List<String> lst = new ArrayList<String>();
1484 for (URI url : urls) {
1485 lst.add(url.getHost());
1486 }
1487 return lst;
1488 }
1489
1490 /**
1491 * Gets the enabled autodiscover endpoints on a specific host.
1492 *
1493 * @param host The host.
1494 * @param endpoints Endpoints found for host.
1495 * @return Flags indicating which endpoints are enabled.
1496 * @throws Exception the exception
1497 */
1498 private boolean tryGetEnabledEndpointsForHost(String host,
1499 OutParam<EnumSet<AutodiscoverEndpoints>> endpoints) throws Exception {
1500 this.traceMessage(TraceFlags.AutodiscoverConfiguration, String.format(
1501 "Determining which endpoints are enabled for host %s", host));
1502
1503 // We may get redirected to another host. And therefore need to limit the number of redirections we'll
1504 // tolerate.
1505 for (int currentHop = 0; currentHop < AutodiscoverMaxRedirections; currentHop++) {
1506 URI autoDiscoverUrl = new URI(String.format(AutodiscoverLegacyHttpsUrl, host));
1507
1508 endpoints.setParam(EnumSet.of(AutodiscoverEndpoints.None));
1509
1510 HttpWebRequest request = null;
1511 try {
1512 request = new HttpClientWebRequest(httpClient, httpContext);
1513 request.setProxy(getWebProxy());
1514
1515 try {
1516 request.setUrl(autoDiscoverUrl.toURL());
1517 } catch (MalformedURLException e) {
1518 String strErr = String.format("Incorrect format : %s", url);
1519 throw new ServiceLocalException(strErr);
1520 }
1521
1522 request.setRequestMethod("GET");
1523 request.setAllowAutoRedirect(false);
1524 request.setPreAuthenticate(false);
1525 request.setUseDefaultCredentials(this.getUseDefaultCredentials());
1526
1527 prepareCredentials(request);
1528
1529 request.prepareConnection();
1530 try {
1531 request.executeRequest();
1532 } catch (IOException e) {
1533 return false;
1534 }
1535
1536 OutParam<URI> outParam = new OutParam<URI>();
1537 if (this.tryGetRedirectionResponse(request, outParam)) {
1538 URI redirectUrl = outParam.getParam();
1539 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
1540 String.format("Host returned redirection to host '%s'", redirectUrl.getHost()));
1541
1542 host = redirectUrl.getHost();
1543 } else {
1544 endpoints.setParam(this.getEndpointsFromHttpWebResponse(request));
1545
1546 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
1547 String.format("Host returned enabled endpoint flags: %s", endpoints.getParam().toString()));
1548
1549 return true;
1550 }
1551 } finally {
1552 if (request != null) {
1553 try {
1554 request.close();
1555 } catch (Exception e) {
1556 // Connection can't be closed. We'll ignore this...
1557 }
1558 }
1559 }
1560 }
1561
1562 this.traceMessage(TraceFlags.AutodiscoverConfiguration,
1563 String.format("Maximum number of redirection hops %d exceeded", AutodiscoverMaxRedirections));
1564
1565 throw new MaximumRedirectionHopsExceededException();
1566 }
1567
1568 /**
1569 * Gets the endpoints from HTTP web response.
1570 *
1571 * @param request the request
1572 * @return Endpoints enabled.
1573 * @throws EWSHttpException the EWS http exception
1574 */
1575 private EnumSet<AutodiscoverEndpoints> getEndpointsFromHttpWebResponse(
1576 HttpWebRequest request) throws EWSHttpException {
1577 EnumSet<AutodiscoverEndpoints> endpoints = EnumSet
1578 .noneOf(AutodiscoverEndpoints.class);
1579 endpoints.add(AutodiscoverEndpoints.Legacy);
1580
1581 if (!(request.getResponseHeaders().get(
1582 AutodiscoverSoapEnabledHeaderName) == null || request
1583 .getResponseHeaders().get(AutodiscoverSoapEnabledHeaderName)
1584 .isEmpty())) {
1585 endpoints.add(AutodiscoverEndpoints.Soap);
1586 }
1587 if (!(request.getResponseHeaders().get(
1588 AutodiscoverWsSecurityEnabledHeaderName) == null || request
1589 .getResponseHeaders().get(
1590 AutodiscoverWsSecurityEnabledHeaderName).isEmpty())) {
1591 endpoints.add(AutodiscoverEndpoints.WsSecurity);
1592 }
1593
1594 /* if (! (request.getResponseHeaders().get(
1595 AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName) !=null || request
1596 .getResponseHeaders().get(
1597 AutodiscoverWsSecuritySymmetricKeyEnabledHeaderName).isEmpty()))
1598 {
1599 endpoints .add( AutodiscoverEndpoints.WSSecuritySymmetricKey);
1600 }
1601 if (!(request.getResponseHeaders().get(
1602 AutodiscoverWsSecurityX509CertEnabledHeaderName)!=null ||
1603 request.getResponseHeaders().get(
1604 AutodiscoverWsSecurityX509CertEnabledHeaderName).isEmpty()))
1605
1606 {
1607 endpoints .add(AutodiscoverEndpoints.WSSecurityX509Cert);
1608 }*/
1609
1610 return endpoints;
1611 }
1612
1613 /**
1614 * Traces the response.
1615 *
1616 * @param request the request
1617 * @param memoryStream the memory stream
1618 * @throws XMLStreamException the XML stream exception
1619 * @throws IOException signals that an I/O exception has occurred.
1620 * @throws EWSHttpException the EWS http exception
1621 */
1622 public void traceResponse(HttpWebRequest request, ByteArrayOutputStream memoryStream) throws XMLStreamException,
1623 IOException, EWSHttpException {
1624 this.processHttpResponseHeaders(
1625 TraceFlags.AutodiscoverResponseHttpHeaders, request);
1626 String contentType = request.getResponseContentType();
1627 if (!(contentType == null || contentType.isEmpty())) {
1628 contentType = contentType.toLowerCase();
1629 if (contentType.toLowerCase().startsWith("text/") ||
1630 contentType.toLowerCase().
1631 startsWith("application/soap")) {
1632 this.traceXml(TraceFlags.AutodiscoverResponse, memoryStream);
1633 } else {
1634 this.traceMessage(TraceFlags.AutodiscoverResponse,
1635 "Non-textual response");
1636 }
1637 }
1638 }
1639
1640 /**
1641 * Creates an HttpWebRequest instance and initializes it with the
1642 * appropriate parameters, based on the configuration of this service
1643 * object.
1644 *
1645 * @param url The URL that the HttpWebRequest should target
1646 * @return HttpWebRequest The HttpWebRequest
1647 * @throws ServiceLocalException the service local exception
1648 * @throws java.net.URISyntaxException the uRI syntax exception
1649 */
1650 public HttpWebRequest prepareHttpWebRequestForUrl(URI url)
1651 throws ServiceLocalException, URISyntaxException {
1652 return this.prepareHttpWebRequestForUrl(url, false,
1653 // acceptGzipEncoding
1654 false); // allowAutoRedirect
1655 }
1656
1657 /**
1658 * Calls the redirection URL validation callback. If the redirection URL
1659 * validation callback is null, use the default callback which does not
1660 * allow following any redirections.
1661 *
1662 * @param redirectionUrl The redirection URL.
1663 * @return True if redirection should be followed.
1664 * @throws AutodiscoverLocalException the autodiscover local exception
1665 */
1666 private boolean callRedirectionUrlValidationCallback(String redirectionUrl)
1667 throws AutodiscoverLocalException {
1668 IAutodiscoverRedirectionUrl callback =
1669 (this.redirectionUrlValidationCallback == null) ? this
1670 : this.redirectionUrlValidationCallback;
1671 return callback
1672 .autodiscoverRedirectionUrlValidationCallback(redirectionUrl);
1673 }
1674
1675 /**
1676 * Processes an HTTP error response.
1677 *
1678 * @param httpWebResponse The HTTP web response.
1679 * @throws Exception the exception
1680 */
1681 @Override public void processHttpErrorResponse(HttpWebRequest httpWebResponse, Exception webException) throws Exception {
1682 this.internalProcessHttpErrorResponse(
1683 httpWebResponse,
1684 webException,
1685 TraceFlags.AutodiscoverResponseHttpHeaders,
1686 TraceFlags.AutodiscoverResponse);
1687 }
1688
1689 /*
1690 * (non-Javadoc)
1691 *
1692 * @see microsoft.exchange.webservices.AutodiscoverRedirectionUrlInterface#
1693 * autodiscoverRedirectionUrlValidationCallback(java.lang.String)
1694 */
1695 public boolean autodiscoverRedirectionUrlValidationCallback(
1696 String redirectionUrl) throws AutodiscoverLocalException {
1697 return defaultAutodiscoverRedirectionUrlValidationCallback(
1698 redirectionUrl);
1699 }
1700
1701 /**
1702 * Initializes a new instance of the "AutodiscoverService" class.
1703 *
1704 * @throws ArgumentException on validation error
1705 */
1706 public AutodiscoverService() throws ArgumentException {
1707 this(ExchangeVersion.Exchange2010);
1708 }
1709
1710 /**
1711 * Initializes a new instance of the "AutodiscoverService" class.
1712 *
1713 * @param requestedServerVersion The requested server version
1714 * @throws ArgumentException on validation error
1715 */
1716 public AutodiscoverService(ExchangeVersion requestedServerVersion)
1717 throws ArgumentException {
1718 this(null, null, requestedServerVersion);
1719 }
1720
1721 /**
1722 * Initializes a new instance of the "AutodiscoverService" class.
1723 *
1724 * @param domain The domain that will be used to determine the URL of the service
1725 * @throws ArgumentException on validation error
1726 */
1727 public AutodiscoverService(String domain) throws ArgumentException {
1728 this(null, domain);
1729 }
1730
1731 /**
1732 * Initializes a new instance of the "AutodiscoverService" class.
1733 *
1734 * @param domain The domain that will be used to determine the URL of the service
1735 * @param requestedServerVersion The requested server version
1736 * @throws ArgumentException on validation error
1737 */
1738 public AutodiscoverService(String domain,
1739 ExchangeVersion requestedServerVersion) throws ArgumentException {
1740 this(null, domain, requestedServerVersion);
1741 }
1742
1743 /**
1744 * Initializes a new instance of the "AutodiscoverService" class.
1745 *
1746 * @param url The URL of the service
1747 * @throws ArgumentException on validation error
1748 */
1749 public AutodiscoverService(URI url) throws ArgumentException {
1750 this(url, url.getHost());
1751 }
1752
1753 /**
1754 * Initializes a new instance of the "AutodiscoverService" class.
1755 *
1756 * @param url The URL of the service
1757 * @param requestedServerVersion The requested server version
1758 * @throws ArgumentException on validation error
1759 */
1760 public AutodiscoverService(URI url,
1761 ExchangeVersion requestedServerVersion) throws ArgumentException {
1762 this(url, url.getHost(), requestedServerVersion);
1763 }
1764
1765 /**
1766 * Initializes a new instance of the "AutodiscoverService" class.
1767 *
1768 * @param url The URL of the service
1769 * @param domain The domain that will be used to determine the URL of the service
1770 * @throws ArgumentException on validation error
1771 */
1772 public AutodiscoverService(URI url, String domain)
1773 throws ArgumentException {
1774 super();
1775 EwsUtilities.validateDomainNameAllowNull(domain, "domain");
1776 this.url = url;
1777 this.domain = domain;
1778 this.dnsClient = new AutodiscoverDnsClient(this);
1779 }
1780
1781 /**
1782 * Initializes a new instance of the "AutodiscoverService" class.
1783 *
1784 * @param url The URL of the service.
1785 * @param domain The domain that will be used to determine the URL of the
1786 * service.
1787 * @param requestedServerVersion The requested server version.
1788 * @throws ArgumentException on validation error
1789 */
1790 public AutodiscoverService(URI url, String domain,
1791 ExchangeVersion requestedServerVersion) throws ArgumentException {
1792 super(requestedServerVersion);
1793 EwsUtilities.validateDomainNameAllowNull(domain, "domain");
1794
1795 this.url = url;
1796 this.domain = domain;
1797 this.dnsClient = new AutodiscoverDnsClient(this);
1798 }
1799
1800 /**
1801 * Initializes a new instance of the AutodiscoverService class.
1802 *
1803 * @param service The other service.
1804 * @param requestedServerVersion The requested server version.
1805 */
1806 public AutodiscoverService(ExchangeServiceBase service,
1807 ExchangeVersion requestedServerVersion) {
1808 super(service, requestedServerVersion);
1809 this.dnsClient = new AutodiscoverDnsClient(this);
1810 }
1811
1812 /**
1813 * Initializes a new instance of the "AutodiscoverService" class.
1814 *
1815 * @param service The service.
1816 */
1817 public AutodiscoverService(ExchangeServiceBase service) {
1818 super(service, service.getRequestedServerVersion());
1819 }
1820
1821 /**
1822 * Retrieves the specified settings for single SMTP address.
1823 * <p>This method will run the entire Autodiscover "discovery"
1824 * algorithm and will follow address and URL redirections.</p>
1825
1826 * @param userSmtpAddress The SMTP addresses of the user.
1827 * @param userSettingNames The user setting names.
1828 * @return A UserResponse object containing the requested settings for the
1829 * specified user.
1830 * @throws Exception on error
1831 */
1832 public GetUserSettingsResponse getUserSettings(String userSmtpAddress,
1833 UserSettingName... userSettingNames) throws Exception {
1834 List<UserSettingName> requestedSettings = new ArrayList<UserSettingName>();
1835 requestedSettings.addAll(Arrays.asList(userSettingNames));
1836
1837 if (userSmtpAddress == null || userSmtpAddress.isEmpty()) {
1838 throw new ServiceValidationException("A valid SMTP address must be specified.");
1839 }
1840
1841 if (requestedSettings.size() == 0) {
1842 throw new ServiceValidationException("At least one setting must be requested.");
1843 }
1844
1845 if (this.getRequestedServerVersion().compareTo(MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
1846 return this.internalGetLegacyUserSettings(userSmtpAddress,
1847 requestedSettings);
1848 } else {
1849 return this.internalGetSoapUserSettings(userSmtpAddress,
1850 requestedSettings);
1851 }
1852
1853 }
1854
1855 /**
1856 * Retrieves the specified settings for a set of users.
1857 *
1858 * @param userSmtpAddresses the user smtp addresses
1859 * @param userSettingNames The user setting names.
1860 * @return A GetUserSettingsResponseCollection object containing the
1861 * response for each individual user.
1862 * @throws Exception the exception
1863 */
1864 public GetUserSettingsResponseCollection getUsersSettings(
1865 Iterable<String> userSmtpAddresses,
1866 UserSettingName... userSettingNames) throws Exception {
1867 if (this.getRequestedServerVersion().compareTo(MinimumRequestVersionForAutoDiscoverSoapService) < 0) {
1868 throw new ServiceVersionException(
1869 String.format("The Autodiscover service only supports %s or a later version.",
1870 MinimumRequestVersionForAutoDiscoverSoapService));
1871 }
1872 List<String> smtpAddresses = new ArrayList<String>();
1873 smtpAddresses.addAll((Collection<? extends String>) userSmtpAddresses);
1874 List<UserSettingName> settings = new ArrayList<UserSettingName>();
1875 settings.addAll(Arrays.asList(userSettingNames));
1876 return this.getUserSettings(smtpAddresses, settings);
1877 }
1878
1879 /**
1880 * Retrieves the specified settings for a domain.
1881 *
1882 * @param domain The domain.
1883 * @param requestedVersion Requested version of the Exchange service.
1884 * @param domainSettingNames The domain setting names.
1885 * @return A DomainResponse object containing the requested settings for the
1886 * specified domain.
1887 * @throws Exception the exception
1888 */
1889 public GetDomainSettingsResponse getDomainSettings(String domain,
1890 ExchangeVersion requestedVersion,
1891 DomainSettingName... domainSettingNames) throws Exception {
1892 List<String> domains = new ArrayList<String>(1);
1893 domains.add(domain);
1894
1895 List<DomainSettingName> settings = new ArrayList<DomainSettingName>();
1896 settings.addAll(Arrays.asList(domainSettingNames));
1897
1898 return this.getDomainSettings(domains, settings, requestedVersion).
1899 getTResponseAtIndex(0);
1900 }
1901
1902 /**
1903 * Retrieves the specified settings for a set of domains.
1904 *
1905 * @param domains the domains
1906 * @param requestedVersion Requested version of the Exchange service.
1907 * @param domainSettingNames The domain setting names.
1908 * @return A GetDomainSettingsResponseCollection object containing the
1909 * response for each individual domain.
1910 * @throws Exception the exception
1911 */
1912 public GetDomainSettingsResponseCollection getDomainSettings(
1913 Iterable<String> domains, ExchangeVersion requestedVersion,
1914 DomainSettingName... domainSettingNames)
1915 throws Exception {
1916 List<DomainSettingName> settings = new ArrayList<DomainSettingName>();
1917 settings.addAll(Arrays.asList(domainSettingNames));
1918
1919 List<String> domainslst = new ArrayList<String>();
1920 domainslst.addAll((Collection<? extends String>) domains);
1921
1922 return this.getDomainSettings(domainslst, settings, requestedVersion);
1923 }
1924
1925 /**
1926 * Gets the domain this service is bound to. When this property is
1927 * set, the domain name is used to automatically determine the Autodiscover service URL.
1928 *
1929 * @return the domain
1930 */
1931 public String getDomain() {
1932 return this.domain;
1933 }
1934
1935 /**
1936 * Sets the domain this service is bound to. When this property is
1937 * set, the domain
1938 * name is used to automatically determine the Autodiscover service URL.
1939 *
1940 * @param value the new domain
1941 * @throws ArgumentException on validation error
1942 */
1943 public void setDomain(String value) throws ArgumentException {
1944 EwsUtilities.validateDomainNameAllowNull(value, "Domain");
1945
1946 // If Domain property is set to non-null value, Url property is nulled.
1947 if (value != null) {
1948 this.url = null;
1949 }
1950 this.domain = value;
1951 }
1952
1953 /**
1954 * Gets the url this service is bound to.
1955 *
1956 * @return the url
1957 */
1958 public URI getUrl() {
1959 return this.url;
1960 }
1961
1962 /**
1963 * Sets the url this service is bound to.
1964 *
1965 * @param value the new url
1966 */
1967 public void setUrl(URI value) {
1968 // If Url property is set to non-null value, Domain property is set to
1969 // host portion of Url.
1970 if (value != null) {
1971 this.domain = value.getHost();
1972 }
1973 this.url = value;
1974 }
1975
1976 public Boolean isExternal() {
1977 return this.isExternal;
1978 }
1979
1980 protected void setIsExternal(Boolean value) {
1981 this.isExternal = value;
1982 }
1983
1984
1985 /**
1986 * Gets the redirection url validation callback.
1987 *
1988 * @return the redirection url validation callback
1989 */
1990 public IAutodiscoverRedirectionUrl
1991 getRedirectionUrlValidationCallback() {
1992 return this.redirectionUrlValidationCallback;
1993 }
1994
1995 /**
1996 * Sets the redirection url validation callback.
1997 *
1998 * @param value the new redirection url validation callback
1999 */
2000 public void setRedirectionUrlValidationCallback(
2001 IAutodiscoverRedirectionUrl value) {
2002 this.redirectionUrlValidationCallback = value;
2003 }
2004
2005 /**
2006 * Gets the dns server address.
2007 *
2008 * @return the dns server address
2009 */
2010 protected String getDnsServerAddress() {
2011 return this.dnsServerAddress;
2012 }
2013
2014 /**
2015 * Sets the dns server address.
2016 *
2017 * @param value the new dns server address
2018 */
2019 protected void setDnsServerAddress(String value) {
2020 this.dnsServerAddress = value;
2021 }
2022
2023 /**
2024 * Gets a value indicating whether the AutodiscoverService should
2025 * perform SCP (ServiceConnectionPoint) record lookup when determining
2026 * the Autodiscover service URL.
2027 *
2028 * @return the enable scp lookup
2029 */
2030 public boolean getEnableScpLookup() {
2031 return this.enableScpLookup;
2032 }
2033
2034 /**
2035 * Sets the enable scp lookup.
2036 *
2037 * @param value the new enable scp lookup
2038 */
2039 public void setEnableScpLookup(boolean value) {
2040 this.enableScpLookup = value;
2041 }
2042
2043 /*
2044 * (non-Javadoc)
2045 *
2046 * @see
2047 * microsoft.exchange.webservices.FuncDelegateInterface#func(java.util.List,
2048 * java.util.List, java.net.URI)
2049 */
2050 @Override
2051 public Object func(List arg1, List arg2, ExchangeVersion arg3, URI arg4)
2052 throws ServiceLocalException, Exception {
2053 if (arg2.get(0).getClass().equals(DomainSettingName.class)) {
2054 return internalGetDomainSettings(arg1, arg2, arg3, arg4);
2055 } else if (arg2.get(0).getClass().equals(UserSettingName.class)) {
2056 return internalGetUserSettings(arg1, arg2, arg3, arg4);
2057 } else {
2058 return null;
2059 }
2060 }
2061
2062 }