KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jcifs > http > NtlmHttpURLConnection


1 /* jcifs smb client library in Java
2  * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
3  * "Eric Glass" <jcifs at samba dot org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package jcifs.http;
21
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.OutputStream JavaDoc;
26
27 import java.net.Authenticator JavaDoc;
28 import java.net.HttpURLConnection JavaDoc;
29 import java.net.PasswordAuthentication JavaDoc;
30 import java.net.ProtocolException JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.net.URLDecoder JavaDoc;
33
34 import java.security.Permission JavaDoc;
35
36 import java.util.ArrayList JavaDoc;
37 import java.util.Collections JavaDoc;
38 import java.util.HashMap JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Map JavaDoc;
42
43 import jcifs.Config;
44
45 import jcifs.ntlmssp.NtlmFlags;
46 import jcifs.ntlmssp.NtlmMessage;
47 import jcifs.ntlmssp.Type1Message;
48 import jcifs.ntlmssp.Type2Message;
49 import jcifs.ntlmssp.Type3Message;
50
51 import jcifs.util.Base64;
52
53 /**
54  * Wraps an <code>HttpURLConnection</code> to provide NTLM authentication
55  * services.
56  *
57  * Please read <a HREF="../../../httpclient.html">Using jCIFS NTLM Authentication for HTTP Connections</a>.
58  */

59 public class NtlmHttpURLConnection extends HttpURLConnection JavaDoc {
60
61     private static final int MAX_REDIRECTS =
62             Integer.parseInt(System.getProperty("http.maxRedirects", "20"));
63
64     private static final int LM_COMPATIBILITY =
65             Config.getInt("jcifs.smb.lmCompatibility", 0);
66
67     private static final String JavaDoc DEFAULT_DOMAIN;
68
69     private HttpURLConnection JavaDoc connection;
70
71     private Map JavaDoc requestProperties;
72
73     private Map JavaDoc headerFields;
74
75     private ByteArrayOutputStream JavaDoc cachedOutput;
76
77     private String JavaDoc authProperty;
78
79     private String JavaDoc authMethod;
80
81     private boolean handshakeComplete;
82
83     static {
84         String JavaDoc domain = System.getProperty("http.auth.ntlm.domain");
85         if (domain == null) domain = Type3Message.getDefaultDomain();
86         DEFAULT_DOMAIN = domain;
87     }
88
89     public NtlmHttpURLConnection(HttpURLConnection JavaDoc connection) {
90         super(connection.getURL());
91         this.connection = connection;
92         requestProperties = new HashMap JavaDoc();
93     }
94
95     public void connect() throws IOException JavaDoc {
96         if (connected) return;
97         connection.connect();
98         connected = true;
99     }
100
101     private void handshake() throws IOException JavaDoc {
102         if (handshakeComplete) return;
103         doHandshake();
104         handshakeComplete = true;
105     }
106
107     public URL JavaDoc getURL() {
108         return connection.getURL();
109     }
110
111     public int getContentLength() {
112         try {
113             handshake();
114         } catch (IOException JavaDoc ex) { }
115         return connection.getContentLength();
116     }
117
118     public String JavaDoc getContentType() {
119         try {
120             handshake();
121         } catch (IOException JavaDoc ex) { }
122         return connection.getContentType();
123     }
124
125     public String JavaDoc getContentEncoding() {
126         try {
127             handshake();
128         } catch (IOException JavaDoc ex) { }
129         return connection.getContentEncoding();
130     }
131
132     public long getExpiration() {
133         try {
134             handshake();
135         } catch (IOException JavaDoc ex) { }
136         return connection.getExpiration();
137     }
138
139     public long getDate() {
140         try {
141             handshake();
142         } catch (IOException JavaDoc ex) { }
143         return connection.getDate();
144     }
145
146     public long getLastModified() {
147         try {
148             handshake();
149         } catch (IOException JavaDoc ex) { }
150         return connection.getLastModified();
151     }
152
153     public String JavaDoc getHeaderField(String JavaDoc header) {
154         try {
155             handshake();
156         } catch (IOException JavaDoc ex) { }
157         return connection.getHeaderField(header);
158     }
159
160     private Map JavaDoc getHeaderFields0() {
161         if (headerFields != null) return headerFields;
162         Map JavaDoc map = new HashMap JavaDoc();
163         String JavaDoc key = connection.getHeaderFieldKey(0);
164         String JavaDoc value = connection.getHeaderField(0);
165         for (int i = 1; key != null || value != null; i++) {
166             List JavaDoc values = (List JavaDoc) map.get(key);
167             if (values == null) {
168                 values = new ArrayList JavaDoc();
169                 map.put(key, values);
170             }
171             values.add(value);
172             key = connection.getHeaderFieldKey(i);
173             value = connection.getHeaderField(i);
174         }
175         Iterator JavaDoc entries = map.entrySet().iterator();
176         while (entries.hasNext()) {
177             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
178             entry.setValue(Collections.unmodifiableList((List JavaDoc)
179                     entry.getValue()));
180         }
181         return (headerFields = Collections.unmodifiableMap(map));
182     }
183
184     public Map JavaDoc getHeaderFields() {
185         if (headerFields != null) return headerFields;
186         try {
187             handshake();
188         } catch (IOException JavaDoc ex) { }
189         return getHeaderFields0();
190     }
191
192     public int getHeaderFieldInt(String JavaDoc header, int def) {
193         try {
194             handshake();
195         } catch (IOException JavaDoc ex) { }
196         return connection.getHeaderFieldInt(header, def);
197     }
198
199     public long getHeaderFieldDate(String JavaDoc header, long def) {
200         try {
201             handshake();
202         } catch (IOException JavaDoc ex) { }
203         return connection.getHeaderFieldDate(header, def);
204     }
205
206     public String JavaDoc getHeaderFieldKey(int index) {
207         try {
208             handshake();
209         } catch (IOException JavaDoc ex) { }
210         return connection.getHeaderFieldKey(index);
211     }
212
213     public String JavaDoc getHeaderField(int index) {
214         try {
215             handshake();
216         } catch (IOException JavaDoc ex) { }
217         return connection.getHeaderField(index);
218     }
219
220     public Object JavaDoc getContent() throws IOException JavaDoc {
221         try {
222             handshake();
223         } catch (IOException JavaDoc ex) { }
224         return connection.getContent();
225     }
226
227     public Object JavaDoc getContent(Class JavaDoc[] classes) throws IOException JavaDoc {
228         try {
229             handshake();
230         } catch (IOException JavaDoc ex) { }
231         return connection.getContent(classes);
232     }
233
234     public Permission JavaDoc getPermission() throws IOException JavaDoc {
235         return connection.getPermission();
236     }
237
238     public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
239         try {
240             handshake();
241         } catch (IOException JavaDoc ex) { }
242         return connection.getInputStream();
243     }
244
245     public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
246         try {
247             connect();
248         } catch (IOException JavaDoc ex) { }
249         OutputStream JavaDoc output = connection.getOutputStream();
250         cachedOutput = new ByteArrayOutputStream JavaDoc();
251         return new CacheStream(output, cachedOutput);
252     }
253
254     public String JavaDoc toString() {
255         return connection.toString();
256     }
257
258     public void setDoInput(boolean doInput) {
259         connection.setDoInput(doInput);
260         this.doInput = doInput;
261     }
262
263     public boolean getDoInput() {
264         return connection.getDoInput();
265     }
266
267     public void setDoOutput(boolean doOutput) {
268         connection.setDoOutput(doOutput);
269         this.doOutput = doOutput;
270     }
271
272     public boolean getDoOutput() {
273         return connection.getDoOutput();
274     }
275
276     public void setAllowUserInteraction(boolean allowUserInteraction) {
277         connection.setAllowUserInteraction(allowUserInteraction);
278         this.allowUserInteraction = allowUserInteraction;
279     }
280
281     public boolean getAllowUserInteraction() {
282         return connection.getAllowUserInteraction();
283     }
284
285     public void setUseCaches(boolean useCaches) {
286         connection.setUseCaches(useCaches);
287         this.useCaches = useCaches;
288     }
289
290     public boolean getUseCaches() {
291         return connection.getUseCaches();
292     }
293
294     public void setIfModifiedSince(long ifModifiedSince) {
295         connection.setIfModifiedSince(ifModifiedSince);
296         this.ifModifiedSince = ifModifiedSince;
297     }
298
299     public long getIfModifiedSince() {
300         return connection.getIfModifiedSince();
301     }
302
303     public boolean getDefaultUseCaches() {
304         return connection.getDefaultUseCaches();
305     }
306
307     public void setDefaultUseCaches(boolean defaultUseCaches) {
308         connection.setDefaultUseCaches(defaultUseCaches);
309     }
310
311     public void setRequestProperty(String JavaDoc key, String JavaDoc value) {
312         if (key == null) throw new NullPointerException JavaDoc();
313         List JavaDoc values = new ArrayList JavaDoc();
314         values.add(value);
315         boolean found = false;
316         Iterator JavaDoc entries = requestProperties.entrySet().iterator();
317         while (entries.hasNext()) {
318             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
319             if (key.equalsIgnoreCase((String JavaDoc) entry.getKey())) {
320                 entry.setValue(value);
321                 found = true;
322                 break;
323             }
324         }
325         if (!found) requestProperties.put(key, values);
326         connection.setRequestProperty(key, value);
327     }
328
329     public void addRequestProperty(String JavaDoc key, String JavaDoc value) {
330         if (key == null) throw new NullPointerException JavaDoc();
331         List JavaDoc values = null;
332         Iterator JavaDoc entries = requestProperties.entrySet().iterator();
333         while (entries.hasNext()) {
334             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
335             if (key.equalsIgnoreCase((String JavaDoc) entry.getKey())) {
336                 values = (List JavaDoc) entry.getValue();
337                 values.add(value);
338                 break;
339             }
340         }
341         if (values == null) {
342             values = new ArrayList JavaDoc();
343             values.add(value);
344             requestProperties.put(key, values);
345         }
346         // 1.3-compatible.
347
StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
348         Iterator JavaDoc propertyValues = values.iterator();
349         while (propertyValues.hasNext()) {
350             buffer.append(propertyValues.next());
351             if (propertyValues.hasNext()) {
352                 buffer.append(", ");
353             }
354         }
355         connection.setRequestProperty(key, buffer.toString());
356     }
357
358     public String JavaDoc getRequestProperty(String JavaDoc key) {
359         return connection.getRequestProperty(key);
360     }
361
362     public Map JavaDoc getRequestProperties() {
363         Map JavaDoc map = new HashMap JavaDoc();
364         Iterator JavaDoc entries = requestProperties.entrySet().iterator();
365         while (entries.hasNext()) {
366             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
367             map.put(entry.getKey(),
368                     Collections.unmodifiableList((List JavaDoc) entry.getValue()));
369         }
370         return Collections.unmodifiableMap(map);
371     }
372
373     public void setInstanceFollowRedirects(boolean instanceFollowRedirects) {
374         connection.setInstanceFollowRedirects(instanceFollowRedirects);
375     }
376
377     public boolean getInstanceFollowRedirects() {
378         return connection.getInstanceFollowRedirects();
379     }
380
381     public void setRequestMethod(String JavaDoc requestMethod)
382             throws ProtocolException JavaDoc {
383         connection.setRequestMethod(requestMethod);
384         this.method = requestMethod;
385     }
386
387     public String JavaDoc getRequestMethod() {
388         return connection.getRequestMethod();
389     }
390
391     public int getResponseCode() throws IOException JavaDoc {
392         try {
393             handshake();
394         } catch (IOException JavaDoc ex) { }
395         return connection.getResponseCode();
396     }
397
398     public String JavaDoc getResponseMessage() throws IOException JavaDoc {
399         try {
400             handshake();
401         } catch (IOException JavaDoc ex) { }
402         return connection.getResponseMessage();
403     }
404
405     public void disconnect() {
406         connection.disconnect();
407         handshakeComplete = false;
408         connected = false;
409     }
410
411     public boolean usingProxy() {
412         return connection.usingProxy();
413     }
414
415     public InputStream JavaDoc getErrorStream() {
416         try {
417             handshake();
418         } catch (IOException JavaDoc ex) { }
419         return connection.getErrorStream();
420     }
421
422     private int parseResponseCode() throws IOException JavaDoc {
423         try {
424             String JavaDoc response = connection.getHeaderField(0);
425             int index = response.indexOf(' ');
426             while (response.charAt(index) == ' ') index++;
427             return Integer.parseInt(response.substring(index, index + 3));
428         } catch (Exception JavaDoc ex) {
429             throw new IOException JavaDoc(ex.getMessage());
430         }
431     }
432
433     private void doHandshake() throws IOException JavaDoc {
434         connect();
435         try {
436             int response = parseResponseCode();
437             if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) {
438                 return;
439             }
440             Type1Message type1 = (Type1Message) attemptNegotiation(response);
441             if (type1 == null) return; // no NTLM
442
int attempt = 0;
443             while (attempt < MAX_REDIRECTS) {
444                 connection.setRequestProperty(authProperty, authMethod + ' ' +
445                         Base64.encode(type1.toByteArray()));
446                 connection.connect(); // send type 1
447
response = parseResponseCode();
448                 if (response != HTTP_UNAUTHORIZED &&
449                         response != HTTP_PROXY_AUTH) {
450                     return;
451                 }
452                 Type3Message type3 = (Type3Message)
453                         attemptNegotiation(response);
454                 if (type3 == null) return;
455                 connection.setRequestProperty(authProperty, authMethod + ' ' +
456                         Base64.encode(type3.toByteArray()));
457                 connection.connect(); // send type 3
458
if (cachedOutput != null && doOutput) {
459                     OutputStream JavaDoc output = connection.getOutputStream();
460                     cachedOutput.writeTo(output);
461                     output.flush();
462                 }
463                 response = parseResponseCode();
464                 if (response != HTTP_UNAUTHORIZED &&
465                         response != HTTP_PROXY_AUTH) {
466                     return;
467                 }
468                 attempt++;
469                 if (allowUserInteraction && attempt < MAX_REDIRECTS) {
470                     reconnect();
471                 } else {
472                     break;
473                 }
474             }
475             throw new IOException JavaDoc("Unable to negotiate NTLM authentication.");
476         } finally {
477             cachedOutput = null;
478         }
479     }
480
481     private NtlmMessage attemptNegotiation(int response) throws IOException JavaDoc {
482         authProperty = null;
483         authMethod = null;
484         InputStream JavaDoc errorStream = connection.getErrorStream();
485         if (errorStream != null && errorStream.available() != 0) {
486             int count;
487             byte[] buf = new byte[1024];
488             while ((count = errorStream.read(buf, 0, 1024)) != -1);
489         }
490         String JavaDoc authHeader;
491         if (response == HTTP_UNAUTHORIZED) {
492             authHeader = "WWW-Authenticate";
493             authProperty = "Authorization";
494         } else {
495             authHeader = "Proxy-Authenticate";
496             authProperty = "Proxy-Authorization";
497         }
498         String JavaDoc authorization = null;
499         List JavaDoc methods = (List JavaDoc) getHeaderFields0().get(authHeader);
500         if (methods == null) return null;
501         Iterator JavaDoc iterator = methods.iterator();
502         while (iterator.hasNext()) {
503             String JavaDoc currentAuthMethod = (String JavaDoc) iterator.next();
504             if (currentAuthMethod.startsWith("NTLM")) {
505                 if (currentAuthMethod.length() == 4) {
506                     authMethod = "NTLM";
507                     break;
508                 }
509                 if (currentAuthMethod.indexOf(' ') != 4) continue;
510                 authMethod = "NTLM";
511                 authorization = currentAuthMethod.substring(5).trim();
512                 break;
513             } else if (currentAuthMethod.startsWith("Negotiate")) {
514                 if (currentAuthMethod.length() == 9) {
515                     authMethod = "Negotiate";
516                     break;
517                 }
518                 if (currentAuthMethod.indexOf(' ') != 9) continue;
519                 authMethod = "Negotiate";
520                 authorization = currentAuthMethod.substring(10).trim();
521                 break;
522             }
523         }
524         if (authMethod == null) return null;
525         NtlmMessage message = (authorization != null) ?
526                 new Type2Message(Base64.decode(authorization)) : null;
527         reconnect();
528         if (message == null) {
529             message = new Type1Message();
530             if (LM_COMPATIBILITY > 2) {
531                 message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true);
532             }
533         } else {
534             String JavaDoc domain = DEFAULT_DOMAIN;
535             String JavaDoc user = Type3Message.getDefaultUser();
536             String JavaDoc password = Type3Message.getDefaultPassword();
537             String JavaDoc userInfo = url.getUserInfo();
538             if (userInfo != null) {
539                 userInfo = URLDecoder.decode(userInfo);
540                 int index = userInfo.indexOf(':');
541                 user = (index != -1) ? userInfo.substring(0, index) : userInfo;
542                 if (index != -1) password = userInfo.substring(index + 1);
543                 index = user.indexOf('\\');
544                 if (index == -1) index = user.indexOf('/');
545                 domain = (index != -1) ? user.substring(0, index) : domain;
546                 user = (index != -1) ? user.substring(index + 1) : user;
547             }
548             if (user == null) {
549                 if (!allowUserInteraction) return null;
550                 try {
551                     URL JavaDoc url = getURL();
552                     String JavaDoc protocol = url.getProtocol();
553                     int port = url.getPort();
554                     if (port == -1) {
555                         port = "https".equalsIgnoreCase(protocol) ? 443 : 80;
556                     }
557                     PasswordAuthentication JavaDoc auth =
558                             Authenticator.requestPasswordAuthentication(null,
559                                     port, protocol, "", authMethod);
560                     if (auth == null) return null;
561                     user = auth.getUserName();
562                     password = new String JavaDoc(auth.getPassword());
563                 } catch (Exception JavaDoc ex) { }
564             }
565             Type2Message type2 = (Type2Message) message;
566             message = new Type3Message(type2, password, domain, user,
567                     Type3Message.getDefaultWorkstation());
568         }
569         return message;
570     }
571
572     private void reconnect() throws IOException JavaDoc {
573         connection = (HttpURLConnection JavaDoc) connection.getURL().openConnection();
574         connection.setRequestMethod(method);
575         headerFields = null;
576         Iterator JavaDoc properties = requestProperties.entrySet().iterator();
577         while (properties.hasNext()) {
578             Map.Entry JavaDoc property = (Map.Entry JavaDoc) properties.next();
579             String JavaDoc key = (String JavaDoc) property.getKey();
580             StringBuffer JavaDoc value = new StringBuffer JavaDoc();
581             Iterator JavaDoc values = ((List JavaDoc) property.getValue()).iterator();
582             while (values.hasNext()) {
583                 value.append(values.next());
584                 if (values.hasNext()) value.append(", ");
585             }
586             connection.setRequestProperty(key, value.toString());
587         }
588         connection.setAllowUserInteraction(allowUserInteraction);
589         connection.setDoInput(doInput);
590         connection.setDoOutput(doOutput);
591         connection.setIfModifiedSince(ifModifiedSince);
592         connection.setUseCaches(useCaches);
593     }
594
595     private static class CacheStream extends OutputStream JavaDoc {
596
597         private final OutputStream JavaDoc stream;
598
599         private final OutputStream JavaDoc collector;
600
601         public CacheStream(OutputStream JavaDoc stream, OutputStream JavaDoc collector) {
602             this.stream = stream;
603             this.collector = collector;
604         }
605
606         public void close() throws IOException JavaDoc {
607             stream.close();
608             collector.close();
609         }
610
611         public void flush() throws IOException JavaDoc {
612             stream.flush();
613             collector.flush();
614         }
615
616         public void write(byte[] b) throws IOException JavaDoc {
617             stream.write(b);
618             collector.write(b);
619         }
620
621         public void write(byte[] b, int off, int len) throws IOException JavaDoc {
622             stream.write(b, off, len);
623             collector.write(b, off, len);
624         }
625
626         public void write(int b) throws IOException JavaDoc {
627             stream.write(b);
628             collector.write(b);
629         }
630
631     }
632
633 }
634
Popular Tags