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