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.core;
025
026import java.io.ByteArrayOutputStream;
027import java.io.Closeable;
028import java.io.File;
029import java.io.IOException;
030import java.net.MalformedURLException;
031import java.net.URI;
032import java.net.URISyntaxException;
033import java.security.GeneralSecurityException;
034import java.text.DateFormat;
035import java.text.SimpleDateFormat;
036import java.util.Date;
037import java.util.EnumSet;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041import java.util.Random;
042import java.util.TimeZone;
043
044import javax.xml.stream.XMLStreamException;
045import javax.xml.stream.XMLStreamWriter;
046
047import microsoft.exchange.webservices.data.EWSConstants;
048import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
049import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
050import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
051import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
052import microsoft.exchange.webservices.data.core.exception.service.remote.AccountIsLockedException;
053import microsoft.exchange.webservices.data.core.request.HttpClientWebRequest;
054import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
055import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
056import microsoft.exchange.webservices.data.misc.EwsTraceListener;
057import microsoft.exchange.webservices.data.misc.ITraceListener;
058
059import org.apache.commons.logging.Log;
060import org.apache.commons.logging.LogFactory;
061import org.apache.http.client.AuthenticationStrategy;
062import org.apache.http.client.CookieStore;
063import org.apache.http.client.protocol.HttpClientContext;
064import org.apache.http.config.Registry;
065import org.apache.http.config.RegistryBuilder;
066import org.apache.http.conn.HttpClientConnectionManager;
067import org.apache.http.conn.socket.ConnectionSocketFactory;
068import org.apache.http.conn.socket.PlainConnectionSocketFactory;
069import org.apache.http.impl.client.BasicCookieStore;
070import org.apache.http.impl.client.CloseableHttpClient;
071import org.apache.http.impl.client.HttpClients;
072import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
073import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
074
075/**
076 * Represents an abstract binding to an Exchange Service.
077 */
078public abstract class ExchangeServiceBase implements Closeable {
079  
080  private static final Log LOG = LogFactory.getLog(ExchangeService.class);
081
082  /**
083   * The credential.
084   */
085  private ExchangeCredentials credentials;
086
087  /**
088   * The use default credential.
089   */
090  private boolean useDefaultCredentials;
091
092  /**
093   * The binary secret.
094   */
095  private static byte[] binarySecret;
096
097  /**
098   * The timeout.
099   */
100  private int timeout = 100000;
101
102  /**
103   * The trace enabled.
104   */
105  private boolean traceEnabled;
106
107  /**
108   * The trace flags.
109   */
110  private EnumSet<TraceFlags> traceFlags = EnumSet.allOf(TraceFlags.class);
111
112  /**
113   * The trace listener.
114   */
115  private ITraceListener traceListener = new EwsTraceListener();
116
117  /**
118   * The pre authenticate.
119   */
120  private boolean preAuthenticate;
121
122  /**
123   * The user agent.
124   */
125  private String userAgent = ExchangeServiceBase.defaultUserAgent;
126
127  /**
128   * The accept gzip encoding.
129   */
130  private boolean acceptGzipEncoding = true;
131
132  /**
133   * The requested server version.
134   */
135  private ExchangeVersion requestedServerVersion = ExchangeVersion.Exchange2010_SP2;
136
137  /**
138   * The server info.
139   */
140  private ExchangeServerInfo serverInfo;
141
142  private Map<String, String> httpHeaders = new HashMap<String, String>();
143
144  private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
145
146  private WebProxy webProxy;
147
148  protected CloseableHttpClient httpClient;
149
150  protected HttpClientContext httpContext;
151
152  protected CloseableHttpClient httpPoolingClient;
153  
154  private int maximumPoolingConnections = 10;
155
156
157//  protected HttpClientWebRequest request = null;
158
159  // protected static HttpStatusCode AccountIsLocked = (HttpStatusCode)456;
160
161  /**
162   * Default UserAgent.
163   */
164  private static String defaultUserAgent = "ExchangeServicesClient/" + EwsUtilities.getBuildVersion();
165
166  /**
167   * Initializes a new instance.
168   *
169   * This constructor performs the initialization of the HTTP connection manager, so it should be called by
170   * every other constructor.
171   */
172  protected ExchangeServiceBase() {
173    setUseDefaultCredentials(true);
174    initializeHttpClient();
175    initializeHttpContext();
176  }
177
178  protected ExchangeServiceBase(ExchangeVersion requestedServerVersion) {
179    this();
180    this.requestedServerVersion = requestedServerVersion;
181  }
182
183  protected ExchangeServiceBase(ExchangeServiceBase service, ExchangeVersion requestedServerVersion) {
184    this(requestedServerVersion);
185    this.useDefaultCredentials = service.getUseDefaultCredentials();
186    this.credentials = service.getCredentials();
187    this.traceEnabled = service.isTraceEnabled();
188    this.traceListener = service.getTraceListener();
189    this.traceFlags = service.getTraceFlags();
190    this.timeout = service.getTimeout();
191    this.preAuthenticate = service.isPreAuthenticate();
192    this.userAgent = service.getUserAgent();
193    this.acceptGzipEncoding = service.getAcceptGzipEncoding();
194    this.httpHeaders = service.getHttpHeaders();
195  }
196
197  private void initializeHttpClient() {
198    Registry<ConnectionSocketFactory> registry = createConnectionSocketFactoryRegistry();
199    HttpClientConnectionManager httpConnectionManager = new BasicHttpClientConnectionManager(registry);
200    AuthenticationStrategy authStrategy = new CookieProcessingTargetAuthenticationStrategy();
201
202    httpClient = HttpClients.custom()
203      .setConnectionManager(httpConnectionManager)
204      .setTargetAuthenticationStrategy(authStrategy)
205      .build();
206  }
207
208  private void initializeHttpPoolingClient() {
209    Registry<ConnectionSocketFactory> registry = createConnectionSocketFactoryRegistry();
210    PoolingHttpClientConnectionManager httpConnectionManager = new PoolingHttpClientConnectionManager(registry);
211    httpConnectionManager.setMaxTotal(maximumPoolingConnections);
212    httpConnectionManager.setDefaultMaxPerRoute(maximumPoolingConnections);
213    AuthenticationStrategy authStrategy = new CookieProcessingTargetAuthenticationStrategy();
214
215    httpPoolingClient = HttpClients.custom()
216        .setConnectionManager(httpConnectionManager)
217        .setTargetAuthenticationStrategy(authStrategy)
218        .build();
219  }
220
221  /**
222   * Sets the maximum number of connections for the pooling connection manager which is used for
223   * subscriptions.
224   * <p>
225   * Default is 10.
226   * </p>
227   * 
228   * @param maximumPoolingConnections Maximum number of pooling connections
229   */
230  public void setMaximumPoolingConnections(int maximumPoolingConnections) {
231    if (maximumPoolingConnections < 1)
232      throw new IllegalArgumentException("maximumPoolingConnections must be 1 or greater");
233    this.maximumPoolingConnections = maximumPoolingConnections;
234  }
235
236  /**
237   * Create registry with configured {@link ConnectionSocketFactory} instances.
238   * Override this method to change how to work with different schemas.
239   *
240   * @return registry object
241   */
242  protected Registry<ConnectionSocketFactory> createConnectionSocketFactoryRegistry() {
243    try {
244      return RegistryBuilder.<ConnectionSocketFactory>create()
245        .register(EWSConstants.HTTP_SCHEME, new PlainConnectionSocketFactory())
246        .register(EWSConstants.HTTPS_SCHEME, EwsSSLProtocolSocketFactory.build(null))
247        .build();
248    } catch (GeneralSecurityException e) {
249      throw new RuntimeException(
250        "Could not initialize ConnectionSocketFactory instances for HttpClientConnectionManager", e
251      );
252    }
253  }
254
255  /**
256   * (Re)initializes the HttpContext object. This removes any existing state (mainly cookies). Use an own
257   * cookie store, instead of the httpClient's global store, so cookies get reset on reinitialization
258   */
259  private void initializeHttpContext() {
260    CookieStore cookieStore = new BasicCookieStore();
261    httpContext = HttpClientContext.create();
262    httpContext.setCookieStore(cookieStore);
263  }
264
265  @Override
266  public void close() {
267    try {
268      httpClient.close();
269    } catch (IOException e) {
270      LOG.debug(e);
271    }
272
273    if (httpPoolingClient != null) {
274      try {
275        httpPoolingClient.close();
276      } catch (IOException e) {
277        LOG.debug(e);
278      }
279    }
280  }
281
282  // Event handlers
283
284  /**
285   * Calls the custom SOAP header serialisation event handlers, if defined.
286   *
287   * @param writer The XmlWriter to which to write the custom SOAP headers.
288   */
289  public void doOnSerializeCustomSoapHeaders(XMLStreamWriter writer) {
290    EwsUtilities
291        .ewsAssert(writer != null, "ExchangeService.DoOnSerializeCustomSoapHeaders", "writer is null");
292
293    if (null != getOnSerializeCustomSoapHeaders() &&
294        !getOnSerializeCustomSoapHeaders().isEmpty()) {
295      for (ICustomXmlSerialization customSerialization : getOnSerializeCustomSoapHeaders()) {
296        customSerialization.CustomXmlSerialization(writer);
297      }
298    }
299  }
300
301  // Utilities
302
303  /**
304   * Creates an HttpWebRequest instance and initialises it with the
305   * appropriate parameters, based on the configuration of this service
306   * object.
307   *
308   * @param url                The URL that the HttpWebRequest should target.
309   * @param acceptGzipEncoding If true, ask server for GZip compressed content.
310   * @param allowAutoRedirect  If true, redirection response will be automatically followed.
311   * @return An initialised instance of HttpWebRequest.
312   * @throws ServiceLocalException       the service local exception
313   * @throws java.net.URISyntaxException the uRI syntax exception
314   */
315  protected HttpWebRequest prepareHttpWebRequestForUrl(URI url, boolean acceptGzipEncoding,
316      boolean allowAutoRedirect) throws ServiceLocalException, URISyntaxException {
317    // Verify that the protocol is something that we can handle
318    String scheme = url.getScheme();
319    if (!scheme.equalsIgnoreCase(EWSConstants.HTTP_SCHEME)
320      && !scheme.equalsIgnoreCase(EWSConstants.HTTPS_SCHEME)) {
321      String strErr = String.format("Protocol %s isn't supported for service request.", scheme);
322      throw new ServiceLocalException(strErr);
323    }
324
325    HttpClientWebRequest request = new HttpClientWebRequest(httpClient, httpContext);
326    prepareHttpWebRequestForUrl(url, acceptGzipEncoding, allowAutoRedirect, request);
327
328    return request;
329  }
330
331  /**
332   * Creates an HttpWebRequest instance from a pooling connection manager and initialises it with
333   * the appropriate parameters, based on the configuration of this service object.
334   * <p>
335   * This is used for subscriptions.
336   * </p>
337   *
338   * @param url The URL that the HttpWebRequest should target.
339   * @param acceptGzipEncoding If true, ask server for GZip compressed content.
340   * @param allowAutoRedirect If true, redirection response will be automatically followed.
341   * @return An initialised instance of HttpWebRequest.
342   * @throws ServiceLocalException the service local exception
343   * @throws java.net.URISyntaxException the uRI syntax exception
344   */
345  protected HttpWebRequest prepareHttpPoolingWebRequestForUrl(URI url, boolean acceptGzipEncoding,
346      boolean allowAutoRedirect) throws ServiceLocalException, URISyntaxException {
347    // Verify that the protocol is something that we can handle
348    String scheme = url.getScheme();
349    if (!scheme.equalsIgnoreCase(EWSConstants.HTTP_SCHEME)
350        && !scheme.equalsIgnoreCase(EWSConstants.HTTPS_SCHEME)) {
351      String strErr = String.format("Protocol %s isn't supported for service request.", scheme);
352      throw new ServiceLocalException(strErr);
353    }
354
355    if (httpPoolingClient == null) {
356      initializeHttpPoolingClient();
357    }
358
359    HttpClientWebRequest request = new HttpClientWebRequest(httpPoolingClient, httpContext);
360    prepareHttpWebRequestForUrl(url, acceptGzipEncoding, allowAutoRedirect, request);
361
362    return request;
363  }
364
365  private void prepareHttpWebRequestForUrl(URI url, boolean acceptGzipEncoding, boolean allowAutoRedirect,
366      HttpClientWebRequest request) throws ServiceLocalException, URISyntaxException {
367    try {
368      request.setUrl(url.toURL());
369    } catch (MalformedURLException e) {
370      String strErr = String.format("Incorrect format : %s", url);
371      throw new ServiceLocalException(strErr);
372    }
373
374    request.setPreAuthenticate(preAuthenticate);
375    request.setTimeout(timeout);
376    request.setContentType("text/xml; charset=utf-8");
377    request.setAccept("text/xml");
378    request.setUserAgent(userAgent);
379    request.setAllowAutoRedirect(allowAutoRedirect);
380    request.setAcceptGzipEncoding(acceptGzipEncoding);
381    request.setHeaders(getHttpHeaders());
382    request.setProxy(getWebProxy());
383    prepareCredentials(request);
384
385    request.prepareConnection();
386
387    httpResponseHeaders.clear();
388  }
389
390  protected void prepareCredentials(HttpWebRequest request) throws ServiceLocalException, URISyntaxException {
391    request.setUseDefaultCredentials(useDefaultCredentials);
392    if (!useDefaultCredentials) {
393      if (credentials == null) {
394        throw new ServiceLocalException("Credentials are required to make a service request.");
395      }
396
397      // Make sure that credential have been authenticated if required
398      credentials.preAuthenticate();
399
400      // Apply credential to the request
401      credentials.prepareWebRequest(request);
402    }
403  }
404
405  /**
406   * This method doesn't handle 500 ISE errors. This is handled by the caller since
407   * 500 ISE typically indicates that a SOAP fault has occurred and the handling of
408   * a SOAP fault is currently service specific.
409   *
410   * @param httpWebResponse HTTP web response
411   * @param webException web exception
412   * @param responseHeadersTraceFlag trace flag for response headers
413   * @param responseTraceFlag trace flag for respone
414   * @throws Exception on error
415   */
416  protected void internalProcessHttpErrorResponse(HttpWebRequest httpWebResponse, Exception webException,
417      TraceFlags responseHeadersTraceFlag, TraceFlags responseTraceFlag) throws Exception {
418    EwsUtilities.ewsAssert(500 != httpWebResponse.getResponseCode(),
419        "ExchangeServiceBase.InternalProcessHttpErrorResponse",
420        "InternalProcessHttpErrorResponse does not handle 500 ISE errors, the caller is supposed to handle this.");
421
422    this.processHttpResponseHeaders(responseHeadersTraceFlag, httpWebResponse);
423
424    // E14:321785 -- Deal with new HTTP error code indicating that account is locked.
425    // The "unlock" URL is returned as the status description in the response.
426    if (httpWebResponse.getResponseCode() == 456) {
427      String location = httpWebResponse.getResponseContentType();
428
429      URI accountUnlockUrl = null;
430      if (checkURIPath(location)) {
431        accountUnlockUrl = new URI(location);
432      }
433
434      final String message = String.format("This account is locked. Visit %s to unlock it.", accountUnlockUrl);
435      this.traceMessage(responseTraceFlag, message);
436      throw new AccountIsLockedException(message, accountUnlockUrl, webException);
437    }
438  }
439
440  /**
441   * @param location file path
442   * @return false if location is null,true if this abstract pathname is absolute
443   */
444  public static boolean checkURIPath(String location) {
445    if (location == null) {
446      return false;
447    }
448    final File file = new File(location);
449    return file.isAbsolute();
450  }
451
452  /**
453   * @param httpWebResponse HTTP web response
454   * @param webException web exception
455   * @throws Exception on error
456   */
457  protected abstract void processHttpErrorResponse(HttpWebRequest httpWebResponse, Exception webException)
458      throws Exception;
459
460  /**
461   * Determines whether tracing is enabled for specified trace flag(s).
462   *
463   * @param traceFlags The trace flags.
464   * @return True if tracing is enabled for specified trace flag(s).
465   */
466  public boolean isTraceEnabledFor(TraceFlags traceFlags) {
467    return this.isTraceEnabled() && this.traceFlags.contains(traceFlags);
468  }
469
470  /**
471   * Logs the specified string to the TraceListener if tracing is enabled.
472   *
473   * @param traceType kind of trace entry
474   * @param logEntry the entry to log
475   * @throws XMLStreamException the XML stream exception
476   * @throws IOException signals that an I/O exception has occurred
477   */
478  public void traceMessage(TraceFlags traceType, String logEntry) throws XMLStreamException, IOException {
479    if (this.isTraceEnabledFor(traceType)) {
480      String traceTypeStr = traceType.toString();
481      String logMessage = EwsUtilities.formatLogMessage(traceTypeStr, logEntry);
482      this.traceListener.trace(traceTypeStr, logMessage);
483    }
484  }
485
486  /**
487   * Logs the specified XML to the TraceListener if tracing is enabled.
488   *
489   * @param traceType Kind of trace entry.
490   * @param stream    The stream containing XML.
491   */
492  public void traceXml(TraceFlags traceType, ByteArrayOutputStream stream) {
493    if (this.isTraceEnabledFor(traceType)) {
494      String traceTypeStr = traceType.toString();
495      String logMessage = EwsUtilities.formatLogMessageWithXmlContent(traceTypeStr, stream);
496      this.traceListener.trace(traceTypeStr, logMessage);
497    }
498  }
499
500  /**
501   * Traces the HTTP request headers.
502   *
503   * @param traceType Kind of trace entry.
504   * @param request   The request
505   * @throws EWSHttpException EWS http exception
506   * @throws URISyntaxException URI syntax error
507   * @throws IOException signals that an I/O exception has occurred
508   * @throws XMLStreamException the XML stream exception
509   */
510  public void traceHttpRequestHeaders(TraceFlags traceType, HttpWebRequest request)
511      throws URISyntaxException, EWSHttpException, XMLStreamException, IOException {
512    if (this.isTraceEnabledFor(traceType)) {
513      String traceTypeStr = traceType.toString();
514      String headersAsString = EwsUtilities.formatHttpRequestHeaders(request);
515      String logMessage = EwsUtilities.formatLogMessage(traceTypeStr, headersAsString);
516      this.traceListener.trace(traceTypeStr, logMessage);
517    }
518  }
519
520  /**
521   * Traces the HTTP response headers.
522   *
523   * @param traceType kind of trace entry
524   * @param request the HttpRequest object
525   * @throws XMLStreamException the XML stream exception
526   * @throws IOException signals that an I/O exception has occurred
527   * @throws EWSHttpException the EWS http exception
528   */
529  private void traceHttpResponseHeaders(TraceFlags traceType, HttpWebRequest request)
530      throws XMLStreamException, IOException, EWSHttpException {
531    if (this.isTraceEnabledFor(traceType)) {
532      String traceTypeStr = traceType.toString();
533      String headersAsString = EwsUtilities.formatHttpResponseHeaders(request);
534      String logMessage = EwsUtilities.formatLogMessage(traceTypeStr, headersAsString);
535      this.traceListener.trace(traceTypeStr, logMessage);
536    }
537  }
538
539  /**
540   * Converts the date time to universal date time string.
541   *
542   * @param dt the date
543   * @return String representation of DateTime in yyyy-MM-ddTHH:mm:ssZ format.
544   */
545  public String convertDateTimeToUniversalDateTimeString(Date dt) {
546    String utcPattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";
547    DateFormat utcFormatter = new SimpleDateFormat(utcPattern);
548    utcFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
549    return utcFormatter.format(dt);
550  }
551
552  /**
553   * Sets the user agent to a custom value
554   *
555   * @param userAgent User agent string to set on the service
556   */
557  protected void setCustomUserAgent(String userAgent) {
558    this.userAgent = userAgent;
559  }
560
561  /**
562   * Validates this instance.
563   *
564   * @throws ServiceLocalException the service local exception
565   */
566  public void validate() throws ServiceLocalException {
567  }
568
569  /**
570   * Gets a value indicating whether tracing is enabled.
571   *
572   * @return True is tracing is enabled
573   */
574  public boolean isTraceEnabled() {
575    return this.traceEnabled;
576  }
577
578  /**
579   * Sets a value indicating whether tracing is enabled.
580   *
581   * @param traceEnabled true to enable tracing
582   */
583  public void setTraceEnabled(boolean traceEnabled) {
584    this.traceEnabled = traceEnabled;
585    if (this.traceEnabled && (this.traceListener == null)) {
586      this.traceListener = new EwsTraceListener();
587    }
588  }
589
590  /**
591   * Gets the trace flags.
592   *
593   * @return Set of trace flags.
594   */
595  public EnumSet<TraceFlags> getTraceFlags() {
596    return traceFlags;
597  }
598
599  /**
600   * Sets the trace flags.
601   *
602   * @param traceFlags A set of trace flags
603   */
604  public void setTraceFlags(EnumSet<TraceFlags> traceFlags) {
605    this.traceFlags = traceFlags;
606  }
607
608  /**
609   * Gets the trace listener.
610   *
611   * @return The trace listener.
612   */
613  public ITraceListener getTraceListener() {
614    return traceListener;
615  }
616
617  /**
618   * Sets the trace listener.
619   *
620   * @param traceListener the trace listener.
621   */
622  public void setTraceListener(ITraceListener traceListener) {
623    this.traceListener = traceListener;
624    this.traceEnabled = (traceListener != null);
625  }
626
627  /**
628   * Gets the credential used to authenticate with the Exchange Web Services.
629   *
630   * @return credential
631   */
632  public ExchangeCredentials getCredentials() {
633    return this.credentials;
634  }
635
636  /**
637   * Sets the credential used to authenticate with the Exchange Web Services.
638   * Setting the Credentials property automatically sets the
639   * UseDefaultCredentials to false.
640   *
641   * @param credentials Exchange credential.
642   */
643  public void setCredentials(ExchangeCredentials credentials) {
644    this.credentials = credentials;
645    this.useDefaultCredentials = false;
646
647    // Reset the httpContext, to remove any existing authentication cookies from subsequent request
648    initializeHttpContext();
649  }
650
651  /**
652   * Gets a value indicating whether the credential of the user currently
653   * logged into Windows should be used to authenticate with the Exchange Web
654   * Services.
655   *
656   * @return true if credential of the user currently logged in are used
657   */
658  public boolean getUseDefaultCredentials() {
659    return this.useDefaultCredentials;
660  }
661
662  /**
663   * Sets a value indicating whether the credential of the user currently
664   * logged into Windows should be used to authenticate with the Exchange Web
665   * Services. Setting UseDefaultCredentials to true automatically sets the
666   * Credentials property to null.
667   *
668   * @param value the new use default credential
669   */
670  public void setUseDefaultCredentials(boolean value) {
671    this.useDefaultCredentials = value;
672    if (value) {
673      this.credentials = null;
674    }
675
676    // Reset the httpContext, to remove any existing authentication cookies from subsequent request
677    initializeHttpContext();
678  }
679  
680  /**
681   * Gets the timeout used when sending HTTP request and when receiving HTTP
682   * response, in milliseconds.
683   *
684   * @return timeout in milliseconds
685   */
686  public int getTimeout() {
687    return timeout;
688  }
689
690  /**
691   * Sets the timeout used when sending HTTP request and when receiving HTTP
692   * respones, in milliseconds. Defaults to 100000.
693   *
694   * @param timeout timeout in milliseconds
695   */
696  public void setTimeout(int timeout) {
697    if (timeout < 1) {
698      throw new IllegalArgumentException("Timeout must be greater than zero.");
699    }
700    this.timeout = timeout;
701  }
702
703  /**
704   * Gets a value that indicates whether HTTP pre-authentication should be
705   * performed.
706   *
707   * @return true indicates pre-authentication is set
708   */
709  public boolean isPreAuthenticate() {
710    return preAuthenticate;
711  }
712
713  /**
714   * Sets a value that indicates whether HTTP pre-authentication should be
715   * performed.
716   *
717   * @param preAuthenticate true to enable pre-authentication
718   */
719  public void setPreAuthenticate(boolean preAuthenticate) {
720    this.preAuthenticate = preAuthenticate;
721  }
722
723  /**
724   * Gets a value indicating whether GZip compression encoding should be
725   * accepted. This value will tell the server that the client is able to
726   * handle GZip compression encoding. The server will only send Gzip
727   * compressed content if it has been configured to do so.
728   *
729   * @return true if compression is used
730   */
731  public boolean getAcceptGzipEncoding() {
732    return acceptGzipEncoding;
733  }
734
735  /**
736   * Gets a value indicating whether GZip compression encoding should
737   * be accepted. This value will tell the server that the client is able to
738   * handle GZip compression encoding. The server will only send Gzip
739   * compressed content if it has been configured to do so.
740   *
741   * @param acceptGzipEncoding true to enable compression
742   */
743  public void setAcceptGzipEncoding(boolean acceptGzipEncoding) {
744    this.acceptGzipEncoding = acceptGzipEncoding;
745  }
746
747  /**
748   * Gets the requested server version.
749   *
750   * @return The requested server version.
751   */
752  public ExchangeVersion getRequestedServerVersion() {
753    return this.requestedServerVersion;
754  }
755
756  /**
757   * Gets the user agent.
758   *
759   * @return The user agent.
760   */
761  public String getUserAgent() {
762    return this.userAgent;
763  }
764
765  /**
766   * Sets the user agent.
767   *
768   * @param userAgent The user agent
769   */
770  public void setUserAgent(String userAgent) {
771    this.userAgent = userAgent + " (" + ExchangeServiceBase.defaultUserAgent + ")";
772  }
773
774  /**
775   * Gets information associated with the server that processed the last
776   * request. Will be null if no request have been processed.
777   *
778   * @return the server info
779   */
780  public ExchangeServerInfo getServerInfo() {
781    return serverInfo;
782  }
783
784  /**
785   * Sets information associated with the server that processed the last
786   * request.
787   *
788   * @param serverInfo Server Information
789   */
790  public void setServerInfo(ExchangeServerInfo serverInfo) {
791    this.serverInfo = serverInfo;
792  }
793
794  /**
795   * Gets the web proxy that should be used when sending request to EWS.
796   *
797   * @return Proxy
798   * the Proxy Information
799   */
800  public WebProxy getWebProxy() {
801    return this.webProxy;
802  }
803
804  /**
805   * Sets the web proxy that should be used when sending request to EWS.
806   * Set this property to null to use the default web proxy.
807   *
808   * @param value the Proxy Information
809   */
810  public void setWebProxy(WebProxy value) {
811    this.webProxy = value;
812  }
813
814  /**
815   * Gets a collection of HTTP headers that will be sent with each request to
816   * EWS.
817   *
818   * @return httpHeaders
819   */
820  public Map<String, String> getHttpHeaders() {
821    return this.httpHeaders;
822  }
823
824  // Events
825
826  /**
827   * Provides an event that applications can implement to emit custom SOAP
828   * headers in request that are sent to Exchange.
829   */
830  private List<ICustomXmlSerialization> OnSerializeCustomSoapHeaders;
831
832  /**
833   * Gets the on serialize custom soap headers.
834   *
835   * @return the on serialize custom soap headers
836   */
837  public List<ICustomXmlSerialization> getOnSerializeCustomSoapHeaders() {
838    return OnSerializeCustomSoapHeaders;
839  }
840
841  /**
842   * Sets the on serialize custom soap headers.
843   *
844   * @param onSerializeCustomSoapHeaders the new on serialize custom soap headers
845   */
846  public void setOnSerializeCustomSoapHeaders(List<ICustomXmlSerialization> onSerializeCustomSoapHeaders) {
847    OnSerializeCustomSoapHeaders = onSerializeCustomSoapHeaders;
848  }
849
850  /**
851   * Traces the HTTP response headers.
852   *
853   * @param traceType kind of trace entry
854   * @param request   The request
855   * @throws EWSHttpException EWS http exception
856   * @throws IOException signals that an I/O exception has occurred
857   * @throws XMLStreamException the XML stream exception
858   */
859  public void processHttpResponseHeaders(TraceFlags traceType, HttpWebRequest request)
860      throws XMLStreamException, IOException, EWSHttpException {
861    this.traceHttpResponseHeaders(traceType, request);
862    this.saveHttpResponseHeaders(request.getResponseHeaders());
863  }
864
865  /**
866   * Save the HTTP response headers.
867   *
868   * @param headers The response headers
869   */
870  private void saveHttpResponseHeaders(Map<String, String> headers) {
871    this.httpResponseHeaders.clear();
872
873    for (String key : headers.keySet()) {
874      this.httpResponseHeaders.put(key, headers.get(key));
875    }
876  }
877
878  /**
879   * Gets a collection of HTTP headers from the last response.
880   * @return HTTP response headers
881   */
882  public Map<String, String> getHttpResponseHeaders() {
883    return this.httpResponseHeaders;
884  }
885
886  /**
887   * Gets the session key.
888   * @return session key
889   */
890  public static byte[] getSessionKey() {
891    // this has to be computed only once.
892    synchronized (ExchangeServiceBase.class) {
893      if (ExchangeServiceBase.binarySecret == null) {
894        Random randomNumberGenerator = new Random();
895        ExchangeServiceBase.binarySecret = new byte[256 / 8];
896        randomNumberGenerator.nextBytes(binarySecret);
897      }
898
899      return ExchangeServiceBase.binarySecret;
900    }
901  }
902}