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