001    /*
002     * The MIT License
003     * Copyright (c) 2012 Microsoft Corporation
004     *
005     * Permission is hereby granted, free of charge, to any person obtaining a copy
006     * of this software and associated documentation files (the "Software"), to deal
007     * in the Software without restriction, including without limitation the rights
008     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009     * copies of the Software, and to permit persons to whom the Software is
010     * furnished to do so, subject to the following conditions:
011     *
012     * The above copyright notice and this permission notice shall be included in
013     * all copies or substantial portions of the Software.
014     *
015     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021     * THE SOFTWARE.
022     */
023    
024    package microsoft.exchange.webservices.data.core;
025    
026    import java.io.ByteArrayOutputStream;
027    import java.io.Closeable;
028    import java.io.File;
029    import java.io.IOException;
030    import java.net.MalformedURLException;
031    import java.net.URI;
032    import java.net.URISyntaxException;
033    import java.security.GeneralSecurityException;
034    import java.text.DateFormat;
035    import java.text.SimpleDateFormat;
036    import java.util.Date;
037    import java.util.EnumSet;
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Random;
042    import java.util.TimeZone;
043    
044    import javax.xml.stream.XMLStreamException;
045    import javax.xml.stream.XMLStreamWriter;
046    
047    import microsoft.exchange.webservices.data.EWSConstants;
048    import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
049    import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
050    import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
051    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
052    import microsoft.exchange.webservices.data.core.exception.service.remote.AccountIsLockedException;
053    import microsoft.exchange.webservices.data.core.request.HttpClientWebRequest;
054    import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
055    import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
056    import microsoft.exchange.webservices.data.misc.EwsTraceListener;
057    import microsoft.exchange.webservices.data.misc.ITraceListener;
058    
059    import org.apache.commons.logging.Log;
060    import org.apache.commons.logging.LogFactory;
061    import org.apache.http.client.AuthenticationStrategy;
062    import org.apache.http.client.CookieStore;
063    import org.apache.http.client.protocol.HttpClientContext;
064    import org.apache.http.config.Registry;
065    import org.apache.http.config.RegistryBuilder;
066    import org.apache.http.conn.HttpClientConnectionManager;
067    import org.apache.http.conn.socket.ConnectionSocketFactory;
068    import org.apache.http.conn.socket.PlainConnectionSocketFactory;
069    import org.apache.http.impl.client.BasicCookieStore;
070    import org.apache.http.impl.client.CloseableHttpClient;
071    import org.apache.http.impl.client.HttpClients;
072    import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
073    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
074    
075    /**
076     * Represents an abstract binding to an Exchange Service.
077     */
078    public 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    }