View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.http;
21  
22  import davmail.Settings;
23  import davmail.exception.HttpForbiddenException;
24  import davmail.exception.HttpNotFoundException;
25  import davmail.exception.HttpPreconditionFailedException;
26  import davmail.exception.HttpServerErrorException;
27  import davmail.exception.LoginTimeoutException;
28  import davmail.http.request.ExchangeDavRequest;
29  import davmail.http.request.ExchangeSearchRequest;
30  import davmail.http.request.GetRequest;
31  import davmail.http.request.PostRequest;
32  import davmail.http.request.ResponseWrapper;
33  import davmail.http.request.RestRequest;
34  import org.apache.http.Header;
35  import org.apache.http.HttpResponse;
36  import org.apache.http.HttpStatus;
37  import org.apache.http.StatusLine;
38  import org.apache.http.auth.AuthSchemeProvider;
39  import org.apache.http.auth.AuthScope;
40  import org.apache.http.auth.Credentials;
41  import org.apache.http.auth.NTCredentials;
42  import org.apache.http.client.CredentialsProvider;
43  import org.apache.http.client.HttpResponseException;
44  import org.apache.http.client.config.AuthSchemes;
45  import org.apache.http.client.config.CookieSpecs;
46  import org.apache.http.client.config.RequestConfig;
47  import org.apache.http.client.methods.CloseableHttpResponse;
48  import org.apache.http.client.methods.HttpRequestBase;
49  import org.apache.http.client.protocol.HttpClientContext;
50  import org.apache.http.client.utils.URIUtils;
51  import org.apache.http.config.Registry;
52  import org.apache.http.config.RegistryBuilder;
53  import org.apache.http.conn.HttpClientConnectionManager;
54  import org.apache.http.conn.socket.ConnectionSocketFactory;
55  import org.apache.http.conn.socket.PlainConnectionSocketFactory;
56  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
57  import org.apache.http.cookie.Cookie;
58  import org.apache.http.impl.auth.BasicSchemeFactory;
59  import org.apache.http.impl.auth.DigestSchemeFactory;
60  import org.apache.http.impl.client.BasicCookieStore;
61  import org.apache.http.impl.client.BasicCredentialsProvider;
62  import org.apache.http.impl.client.CloseableHttpClient;
63  import org.apache.http.impl.client.HttpClientBuilder;
64  import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
65  import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
66  import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
67  import org.apache.jackrabbit.webdav.DavException;
68  import org.apache.jackrabbit.webdav.MultiStatus;
69  import org.apache.jackrabbit.webdav.MultiStatusResponse;
70  import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest;
71  import org.apache.jackrabbit.webdav.client.methods.HttpCopy;
72  import org.apache.jackrabbit.webdav.client.methods.HttpMove;
73  import org.apache.log4j.Logger;
74  import org.codehaus.jettison.json.JSONObject;
75  
76  import java.io.Closeable;
77  import java.io.IOException;
78  import java.net.InetAddress;
79  import java.net.InetSocketAddress;
80  import java.net.Proxy;
81  import java.net.ProxySelector;
82  import java.net.URI;
83  import java.security.Principal;
84  import java.security.Security;
85  import java.util.LinkedHashSet;
86  import java.util.List;
87  
88  public class HttpClientAdapter implements Closeable {
89      static final Logger LOGGER = Logger.getLogger("davmail.http.HttpClientAdapter");
90  
91      static final String[] SUPPORTED_PROTOCOLS = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
92      static final Registry<ConnectionSocketFactory> SCHEME_REGISTRY;
93      static String WORKSTATION_NAME = "UNKNOWN";
94      static final int MAX_REDIRECTS = 10;
95  
96      static {
97          // disable Client-initiated TLS renegotiation
98          System.setProperty("jdk.tls.rejectClientInitiatedRenegotiation", "true");
99          // force strong ephemeral Diffie-Hellman parameter
100         System.setProperty("jdk.tls.ephemeralDHKeySize", "2048");
101 
102         Security.setProperty("ssl.SocketFactory.provider", "davmail.http.DavGatewaySSLSocketFactory");
103 
104         // DavMail is Kerberos configuration provider
105         Security.setProperty("login.configuration.provider", "davmail.http.KerberosLoginConfiguration");
106 
107         // re-enable basic proxy authentication on Java >= 1.8.111
108         System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
109 
110         RegistryBuilder<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.create();
111         schemeRegistry.register("http", new PlainConnectionSocketFactory());
112         schemeRegistry.register("https", new SSLConnectionSocketFactory(new DavGatewaySSLSocketFactory(),
113                 SUPPORTED_PROTOCOLS, null,
114                 SSLConnectionSocketFactory.getDefaultHostnameVerifier()));
115 
116         SCHEME_REGISTRY = schemeRegistry.build();
117 
118         try {
119             WORKSTATION_NAME = InetAddress.getLocalHost().getHostName();
120         } catch (Exception e) {
121             // ignore
122         }
123 
124         // set system property *before* calling ProxySelector.getDefault()
125         if (Settings.getBooleanProperty("davmail.useSystemProxies", Boolean.FALSE)) {
126             System.setProperty("java.net.useSystemProxies", "true");
127         }
128         ProxySelector.setDefault(new DavGatewayProxySelector(ProxySelector.getDefault()));
129     }
130 
131     /**
132      * Test if the response is gzip encoded
133      *
134      * @param response http response
135      * @return true if response is gzip encoded
136      */
137     public static boolean isGzipEncoded(HttpResponse response) {
138         Header header = response.getFirstHeader("Content-Encoding");
139         return header != null && "gzip".equals(header.getValue());
140     }
141 
142     HttpClientConnectionManager connectionManager;
143     CloseableHttpClient httpClient;
144     boolean closed = false;
145     CredentialsProvider provider = new BasicCredentialsProvider();
146     BasicCookieStore cookieStore = new BasicCookieStore() {
147         @Override
148         public void addCookie(final Cookie cookie) {
149             //LOGGER.debug("Add cookie " + cookie);
150             super.addCookie(cookie);
151         }
152     };
153     // current URI
154     URI uri;
155     String domain;
156     String userid;
157     String userEmail;
158 
159     public HttpClientAdapter(String url) {
160         this(URI.create(url));
161     }
162 
163     public HttpClientAdapter(String url, String username, String password) {
164         this(URI.create(url), username, password, false);
165     }
166 
167     public HttpClientAdapter(String url, boolean enablePool) {
168         this(URI.create(url), null, null, enablePool);
169     }
170 
171     public HttpClientAdapter(String url, String username, String password, boolean enablePool) {
172         this(URI.create(url), username, password, enablePool);
173     }
174 
175     public HttpClientAdapter(URI uri) {
176         this(uri, null, null, false);
177     }
178 
179     public HttpClientAdapter(URI uri, boolean enablePool) {
180         this(uri, null, null, enablePool);
181     }
182 
183     public HttpClientAdapter(URI uri, String username, String password) {
184         this(uri, username, password, false);
185     }
186 
187     public HttpClientAdapter(URI uri, String username, String password, boolean enablePool) {
188         // init current uri
189         this.uri = uri;
190 
191         if (enablePool) {
192             connectionManager = new PoolingHttpClientConnectionManager(SCHEME_REGISTRY);
193             ((PoolingHttpClientConnectionManager) connectionManager).setDefaultMaxPerRoute(5);
194             startEvictorThread();
195         } else {
196             connectionManager = new BasicHttpClientConnectionManager(SCHEME_REGISTRY);
197         }
198         HttpClientBuilder clientBuilder = HttpClientBuilder.create()
199                 .disableRedirectHandling()
200                 .setDefaultRequestConfig(getRequestConfig())
201                 .setUserAgent(getUserAgent())
202                 .setDefaultAuthSchemeRegistry(getAuthSchemeRegistry())
203                 // httpClient is not shared between clients, do not track connection state
204                 .disableConnectionState()
205                 .setConnectionManager(connectionManager);
206 
207         SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
208         clientBuilder.setRoutePlanner(routePlanner);
209 
210         clientBuilder.setDefaultCookieStore(cookieStore);
211 
212         setCredentials(username, password);
213 
214         boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy");
215         boolean useSystemProxies = Settings.getBooleanProperty("davmail.useSystemProxies", Boolean.FALSE);
216         String proxyHost = null;
217         int proxyPort = 0;
218         String proxyUser = null;
219         String proxyPassword = null;
220 
221         if (useSystemProxies) {
222             // get proxy for url from system settings
223             System.setProperty("java.net.useSystemProxies", "true");
224             List<Proxy> proxyList = getProxyForURI(uri);
225             if (!proxyList.isEmpty() && proxyList.get(0).address() != null) {
226                 InetSocketAddress inetSocketAddress = (InetSocketAddress) proxyList.get(0).address();
227                 proxyHost = inetSocketAddress.getHostName();
228                 proxyPort = inetSocketAddress.getPort();
229 
230                 // we may still need authentication credentials
231                 proxyUser = Settings.getProperty("davmail.proxyUser");
232                 proxyPassword = Settings.getProperty("davmail.proxyPassword");
233             }
234         } else if (isNoProxyFor(uri)) {
235             LOGGER.debug("no proxy for " + uri.getHost());
236         } else if (enableProxy) {
237             proxyHost = Settings.getProperty("davmail.proxyHost");
238             proxyPort = Settings.getIntProperty("davmail.proxyPort");
239             proxyUser = Settings.getProperty("davmail.proxyUser");
240             proxyPassword = Settings.getProperty("davmail.proxyPassword");
241         }
242 
243         if (proxyHost != null && !proxyHost.isEmpty() && (proxyUser != null && !proxyUser.isEmpty())) {
244 
245                 AuthScope authScope = new AuthScope(proxyHost, proxyPort, AuthScope.ANY_REALM);
246                 if (provider == null) {
247                     provider = new BasicCredentialsProvider();
248                 }
249 
250                 // detect ntlm authentication (windows domain name in username)
251                 int backslashIndex = proxyUser.indexOf('\\');
252                 if (backslashIndex > 0) {
253                     provider.setCredentials(authScope, new NTCredentials(proxyUser.substring(backslashIndex + 1),
254                             proxyPassword, WORKSTATION_NAME,
255                             proxyUser.substring(0, backslashIndex)));
256                 } else {
257                     provider.setCredentials(authScope, new NTCredentials(proxyUser, proxyPassword, WORKSTATION_NAME, ""));
258                 }
259 
260         }
261 
262         clientBuilder.setDefaultCredentialsProvider(provider);
263 
264         httpClient = clientBuilder.build();
265     }
266 
267     /**
268      * Get current uri host
269      *
270      * @return current host
271      */
272     public String getHost() {
273         return uri.getHost();
274     }
275 
276     /**
277      * Force current uri.
278      *
279      * @param uri new uri
280      */
281     public void setUri(URI uri) {
282         this.uri = uri;
283     }
284 
285     /**
286      * Current uri.
287      *
288      * @return current uri
289      */
290     public URI getUri() {
291         return uri;
292     }
293 
294     private Registry<AuthSchemeProvider> getAuthSchemeRegistry() {
295         final RegistryBuilder<AuthSchemeProvider> registryBuilder = RegistryBuilder.create();
296         AuthSchemeProvider ntlmSchemeProvider;
297         if (Settings.getBooleanProperty("davmail.enableJcifs", true)) {
298             ntlmSchemeProvider = new JCIFSNTLMSchemeFactory();
299         } else {
300             ntlmSchemeProvider = new DavMailNTLMSchemeFactory();
301         }
302         registryBuilder.register(AuthSchemes.NTLM, ntlmSchemeProvider)
303                 .register(AuthSchemes.BASIC, new BasicSchemeFactory())
304                 .register(AuthSchemes.DIGEST, new DigestSchemeFactory());
305         if (Settings.getBooleanProperty("davmail.enableKerberos")) {
306             registryBuilder.register(AuthSchemes.SPNEGO, new DavMailSPNegoSchemeFactory());
307         }
308 
309         return registryBuilder.build();
310     }
311 
312     private RequestConfig getRequestConfig() {
313         LinkedHashSet<String> authSchemes = new LinkedHashSet<>();
314         if (Settings.getBooleanProperty("davmail.enableKerberos")) {
315             authSchemes.add(AuthSchemes.SPNEGO);
316             authSchemes.add(AuthSchemes.KERBEROS);
317         } else {
318             authSchemes.add(AuthSchemes.NTLM);
319             authSchemes.add(AuthSchemes.BASIC);
320             authSchemes.add(AuthSchemes.DIGEST);
321         }
322         return RequestConfig.custom()
323                 .setCookieSpec(CookieSpecs.STANDARD)
324                 // socket connect timeout
325                 .setConnectTimeout(Settings.getIntProperty("davmail.exchange.connectionTimeout", 10) * 1000)
326                 // inactivity timeout
327                 .setSocketTimeout(Settings.getIntProperty("davmail.exchange.soTimeout", 120) * 1000)
328                 .setTargetPreferredAuthSchemes(authSchemes)
329                 .build();
330     }
331 
332     private void parseUserName(String username) {
333         if (username != null) {
334             int pipeIndex = username.indexOf("|");
335             if (pipeIndex >= 0) {
336                 userid = username.substring(0, pipeIndex);
337                 userEmail = username.substring(pipeIndex + 1);
338             } else {
339                 userid = username;
340                 userEmail = username;
341             }
342             // separate domain name
343             int backSlashIndex = userid.indexOf('\\');
344             if (backSlashIndex >= 0) {
345                 // separate domain from username in credentials
346                 domain = userid.substring(0, backSlashIndex);
347                 userid = userid.substring(backSlashIndex + 1);
348             } else if (userid.contains("@")) {
349                 // no need for domain name with userPrincipalName
350                 domain = "";
351             } else {
352                 domain = Settings.getProperty("davmail.defaultDomain", "");
353             }
354         }
355     }
356 
357     /**
358      * Retrieve Proxy Selector
359      *
360      * @param uri target uri
361      * @return proxy selector
362      */
363     private static List<Proxy> getProxyForURI(java.net.URI uri) {
364         LOGGER.debug("get Default proxy selector");
365         ProxySelector proxySelector = ProxySelector.getDefault();
366         LOGGER.debug("getProxyForURI(" + uri + ')');
367         List<Proxy> proxies = proxySelector.select(uri);
368         LOGGER.debug("got system proxies:" + proxies);
369         return proxies;
370     }
371 
372     protected static boolean isNoProxyFor(java.net.URI uri) {
373         final String noProxyFor = Settings.getProperty("davmail.noProxyFor");
374         if (noProxyFor != null) {
375             final String uriHost = uri.getHost().toLowerCase();
376             final String[] domains = noProxyFor.toLowerCase().split(",\\s*");
377             for (String domain : domains) {
378                 if (uriHost.endsWith(domain)) {
379                     return true;
380                 }
381             }
382         }
383         return false;
384     }
385 
386     public void startEvictorThread() {
387         DavMailIdleConnectionEvictor.addConnectionManager(connectionManager);
388     }
389 
390     @Override
391     public void close() {
392         if (!closed) {
393             closed = true;
394             DavMailIdleConnectionEvictor.removeConnectionManager(connectionManager);
395             try {
396                 httpClient.close();
397             } catch (IOException e) {
398                 LOGGER.warn("Exception closing http client", e);
399             }
400         }
401     }
402 
403     public boolean isClosed() {
404         return closed;
405     }
406 
407     public static void close(HttpClientAdapter httpClientAdapter) {
408         if (httpClientAdapter != null) {
409             httpClientAdapter.close();
410         }
411     }
412 
413     /**
414      * Execute request, do not follow redirects.
415      * if request is an instance of ResponseHandler, process and close response
416      *
417      * @param request Http request
418      * @return Http response
419      * @throws IOException on error
420      */
421     public CloseableHttpResponse execute(HttpRequestBase request) throws IOException {
422         return execute(request, null);
423     }
424 
425     /**
426      * Execute request, do not follow redirects.
427      * if request is an instance of ResponseHandler, process and close response
428      *
429      * @param request Http request
430      * @param context Http request context
431      * @return Http response
432      * @throws IOException on error
433      */
434     public CloseableHttpResponse execute(HttpRequestBase request, HttpClientContext context) throws IOException {
435         // make sure request path is absolute
436         handleURI(request);
437         // execute request and return response
438         return httpClient.execute(request, context);
439     }
440 
441     /**
442      * fix relative uri and update current uri.
443      *
444      * @param request http request
445      */
446     private void handleURI(HttpRequestBase request) {
447         URI requestURI = request.getURI();
448         if (!requestURI.isAbsolute()) {
449             request.setURI(URIUtils.resolve(uri, requestURI));
450         }
451         uri = request.getURI();
452     }
453 
454     public ResponseWrapper executeFollowRedirect(PostRequest request) throws IOException {
455         ResponseWrapper responseWrapper = request;
456         LOGGER.debug(request.getMethod() + " " + request.getURI().toString());
457         LOGGER.debug(request.getParameters());
458 
459         int count = 0;
460         int maxRedirect = Settings.getIntProperty("davmail.httpMaxRedirects", MAX_REDIRECTS);
461 
462         executePostRequest(request);
463         URI redirectLocation = request.getRedirectLocation();
464 
465         while (count++ < maxRedirect && redirectLocation != null) {
466             LOGGER.debug("Redirect " + request.getURI() + " to " + redirectLocation);
467             // replace uri with target location
468             responseWrapper = new GetRequest(redirectLocation);
469             executeGetRequest((GetRequest) responseWrapper);
470             redirectLocation = ((GetRequest) responseWrapper).getRedirectLocation();
471         }
472 
473         return responseWrapper;
474     }
475 
476     public GetRequest executeFollowRedirect(GetRequest request) throws IOException {
477         GetRequest result = request;
478         LOGGER.debug(request.getMethod() + " " + request.getURI().toString());
479 
480         int count = 0;
481         int maxRedirect = Settings.getIntProperty("davmail.httpMaxRedirects", MAX_REDIRECTS);
482 
483         executeGetRequest(request);
484         URI redirectLocation = request.getRedirectLocation();
485 
486         while (count++ < maxRedirect && redirectLocation != null) {
487             LOGGER.debug("Redirect " + request.getURI() + " to " + redirectLocation);
488             // replace uri with target location
489             result = new GetRequest(redirectLocation);
490             executeGetRequest(result);
491             redirectLocation = result.getRedirectLocation();
492         }
493 
494         return result;
495     }
496 
497     /**
498      * Execute get request and return response body as string.
499      *
500      * @param getRequest get request
501      * @return response body
502      * @throws IOException on error
503      */
504     public String executeGetRequest(GetRequest getRequest) throws IOException {
505         handleURI(getRequest);
506         String responseBodyAsString;
507         try (CloseableHttpResponse response = execute(getRequest)) {
508             responseBodyAsString = getRequest.handleResponse(response);
509         }
510         return responseBodyAsString;
511     }
512 
513     /**
514      * Execute post request and return response body as string.
515      *
516      * @param postRequest post request
517      * @return response body
518      * @throws IOException on error
519      */
520     public String executePostRequest(PostRequest postRequest) throws IOException {
521         handleURI(postRequest);
522         String responseBodyAsString;
523         try (CloseableHttpResponse response = execute(postRequest)) {
524             responseBodyAsString = postRequest.handleResponse(response);
525         }
526         return responseBodyAsString;
527     }
528 
529     public JSONObject executeRestRequest(RestRequest restRequest) throws IOException {
530         handleURI(restRequest);
531         JSONObject responseBody;
532         try (CloseableHttpResponse response = execute(restRequest)) {
533             responseBody = restRequest.handleResponse(response);
534         }
535         return responseBody;
536     }
537 
538     /**
539      * Execute WebDav request
540      *
541      * @param request WebDav request
542      * @return multistatus response
543      * @throws IOException on error
544      */
545     public MultiStatus executeDavRequest(BaseDavRequest request) throws IOException {
546         handleURI(request);
547         MultiStatus multiStatus = null;
548         try (CloseableHttpResponse response = execute(request)) {
549             request.checkSuccess(response);
550             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_MULTI_STATUS) {
551                 multiStatus = request.getResponseBodyAsMultiStatus(response);
552             }
553         } catch (DavException e) {
554             LOGGER.error(e.getMessage(), e);
555             throw new IOException(e.getErrorCode() + " " + e.getStatusPhrase(), e);
556         }
557         return multiStatus;
558     }
559 
560     /**
561      * Execute Exchange WebDav request
562      *
563      * @param request WebDav request
564      * @return multistatus response
565      * @throws IOException on error
566      */
567     public MultiStatusResponse[] executeDavRequest(ExchangeDavRequest request) throws IOException {
568         handleURI(request);
569         MultiStatusResponse[] responses;
570         try (CloseableHttpResponse response = execute(request)) {
571             List<MultiStatusResponse> responseList = request.handleResponse(response);
572             // TODO check error handling
573             //request.checkSuccess(response);
574             responses = responseList.toArray(new MultiStatusResponse[0]);
575         }
576         return responses;
577     }
578 
579 
580     /**
581      * Execute webdav search method.
582      *
583      * @param path            <i>encoded</i> searched folder path
584      * @param searchStatement (SQL like) search statement
585      * @param maxCount        max item count
586      * @return Responses enumeration
587      * @throws IOException on error
588      */
589     public MultiStatusResponse[] executeSearchRequest(String path, String searchStatement, int maxCount) throws IOException {
590         ExchangeSearchRequest searchRequest = new ExchangeSearchRequest(path, searchStatement);
591         if (maxCount > 0) {
592             searchRequest.setHeader("Range", "rows=0-" + (maxCount - 1));
593         }
594         return executeDavRequest(searchRequest);
595     }
596 
597     public static boolean isRedirect(HttpResponse response) {
598         return isRedirect(response.getStatusLine().getStatusCode());
599     }
600 
601     /**
602      * Check if status is a redirect (various 30x values).
603      *
604      * @param status Http status
605      * @return true if status is a redirect
606      */
607     public static boolean isRedirect(int status) {
608         return status == HttpStatus.SC_MOVED_PERMANENTLY
609                 || status == HttpStatus.SC_MOVED_TEMPORARILY
610                 || status == HttpStatus.SC_SEE_OTHER
611                 || status == HttpStatus.SC_TEMPORARY_REDIRECT;
612     }
613 
614     /**
615      * Get redirect location from header.
616      *
617      * @param response Http response
618      * @return URI target location
619      */
620     public static URI getRedirectLocation(HttpResponse response) {
621         Header location = response.getFirstHeader("Location");
622         if (isRedirect(response.getStatusLine().getStatusCode()) && location != null) {
623             return URI.create(location.getValue());
624         }
625         return null;
626     }
627 
628     public void setCredentials(String username, String password) {
629         if (Settings.getBooleanProperty("davmail.enableKerberos")) {
630             LOGGER.debug("Kerberos enabled, set empty credentials");
631             provider.setCredentials(new AuthScope(null, -1, null), new Credentials() {
632                 public String getPassword() {
633                     return null;
634                 }
635 
636                 public Principal getUserPrincipal() {
637                     return null;
638                 }
639             });
640         } else {
641             parseUserName(username);
642             if (userid != null && password != null) {
643                 LOGGER.debug("Creating NTCredentials for user " + userid + " workstation " + WORKSTATION_NAME + " domain " + domain);
644                 NTCredentials credentials = new NTCredentials(userid, password, WORKSTATION_NAME, domain);
645                 provider.setCredentials(AuthScope.ANY, credentials);
646             }
647         }
648     }
649 
650     public List<Cookie> getCookies() {
651         return cookieStore.getCookies();
652     }
653 
654     public void addCookie(Cookie cookie) {
655         cookieStore.addCookie(cookie);
656     }
657 
658     public String getUserAgent() {
659         return Settings.getUserAgent();
660     }
661 
662     public static HttpResponseException buildHttpResponseException(HttpRequestBase request, HttpResponse response) {
663         return buildHttpResponseException(request, response.getStatusLine());
664     }
665 
666     /**
667      * Build Http Exception from method status
668      *
669      * @param method Http Method
670      * @return Http Exception
671      */
672     public static HttpResponseException buildHttpResponseException(HttpRequestBase method, StatusLine statusLine) {
673         int status = statusLine.getStatusCode();
674         StringBuilder message = new StringBuilder();
675         message.append(status).append(' ').append(statusLine.getReasonPhrase());
676         message.append(" at ").append(method.getURI());
677         if (method instanceof HttpCopy || method instanceof HttpMove) {
678             message.append(" to ").append(method.getFirstHeader("Destination"));
679         }
680         // 440 means forbidden on Exchange
681         if (status == 440) {
682             return new LoginTimeoutException(message.toString());
683         } else if (status == HttpStatus.SC_FORBIDDEN) {
684             return new HttpForbiddenException(message.toString());
685         } else if (status == HttpStatus.SC_NOT_FOUND) {
686             return new HttpNotFoundException(message.toString());
687         } else if (status == HttpStatus.SC_PRECONDITION_FAILED) {
688             return new HttpPreconditionFailedException(message.toString());
689         } else if (status == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
690             return new HttpServerErrorException(message.toString());
691         } else {
692             return new HttpResponseException(status, message.toString());
693         }
694     }
695 
696 }