1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.istack.internal.tools; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 import java.io.InputStreamReader; 34 import java.io.UnsupportedEncodingException; 35 import java.lang.reflect.Field; 36 import java.net.Authenticator; 37 import java.net.Authenticator.RequestorType; 38 import java.net.MalformedURLException; 39 import java.net.PasswordAuthentication; 40 import java.net.URL; 41 import java.net.URLDecoder; 42 import java.net.URLEncoder; 43 import java.security.AccessController; 44 import java.security.PrivilegedAction; 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.logging.Level; 48 import java.util.logging.Logger; 49 import java.util.regex.Pattern; 50 import org.xml.sax.Locator; 51 import org.xml.sax.helpers.LocatorImpl; 52 53 /** 54 * @author Vivek Pandey 55 * @author Lukas Jungmann 56 */ 57 public class DefaultAuthenticator extends Authenticator { 58 59 private static DefaultAuthenticator instance; 60 private static Authenticator systemAuthenticator = getCurrentAuthenticator(); 61 private String proxyUser; 62 private String proxyPasswd; 63 private final List<AuthInfo> authInfo = new ArrayList<AuthInfo>(); 64 private static int counter = 0; 65 66 DefaultAuthenticator() { 67 //try undocumented but often used properties 68 if (System.getProperty("http.proxyUser") != null) { 69 proxyUser = System.getProperty("http.proxyUser"); 70 } else { 71 proxyUser = System.getProperty("proxyUser"); 72 } 73 if (System.getProperty("http.proxyPassword") != null) { 74 proxyPasswd = System.getProperty("http.proxyPassword"); 75 } else { 76 proxyPasswd = System.getProperty("proxyPassword"); 77 } 78 } 79 80 public static synchronized DefaultAuthenticator getAuthenticator() { 81 if (instance == null) { 82 instance = new DefaultAuthenticator(); 83 Authenticator.setDefault(instance); 84 } 85 counter++; 86 return instance; 87 } 88 89 public static synchronized void reset() { 90 --counter; 91 if (instance != null && counter == 0) { 92 Authenticator.setDefault(systemAuthenticator); 93 } 94 } 95 96 @Override 97 protected PasswordAuthentication getPasswordAuthentication() { 98 //If user sets proxy user and passwd and the RequestType is from proxy server then create 99 // PasswordAuthentication using proxyUser and proxyPasswd; 100 if ((getRequestorType() == RequestorType.PROXY) && proxyUser != null && proxyPasswd != null) { 101 return new PasswordAuthentication(proxyUser, proxyPasswd.toCharArray()); 102 } 103 for (AuthInfo auth : authInfo) { 104 if (auth.matchingHost(getRequestingURL())) { 105 return new PasswordAuthentication(auth.getUser(), auth.getPassword().toCharArray()); 106 } 107 } 108 return null; 109 } 110 111 /** 112 * Proxy authorization string in form of username:password. 113 * 114 * @param proxyAuth 115 */ 116 public void setProxyAuth(String proxyAuth) { 117 if (proxyAuth == null) { 118 this.proxyUser = null; 119 this.proxyPasswd = null; 120 } else { 121 int i = proxyAuth.indexOf(':'); 122 if (i < 0) { 123 this.proxyUser = proxyAuth; 124 this.proxyPasswd = ""; 125 } else if (i == 0) { 126 this.proxyUser = ""; 127 this.proxyPasswd = proxyAuth.substring(1); 128 } else { 129 this.proxyUser = proxyAuth.substring(0, i); 130 this.proxyPasswd = proxyAuth.substring(i + 1); 131 } 132 } 133 } 134 135 public void setAuth(File f, Receiver l) { 136 Receiver listener = l == null ? new DefaultRImpl() : l; 137 BufferedReader in = null; 138 FileInputStream fi = null; 139 InputStreamReader is = null; 140 try { 141 String text; 142 LocatorImpl locator = new LocatorImpl(); 143 locator.setSystemId(f.getAbsolutePath()); 144 try { 145 fi = new FileInputStream(f); 146 is = new InputStreamReader(fi, "UTF-8"); 147 in = new BufferedReader(is); 148 } catch (UnsupportedEncodingException e) { 149 listener.onError(e, locator); 150 return; 151 } catch (FileNotFoundException e) { 152 listener.onError(e, locator); 153 return; 154 } 155 try { 156 int lineno = 1; 157 locator.setSystemId(f.getCanonicalPath()); 158 while ((text = in.readLine()) != null) { 159 locator.setLineNumber(lineno++); 160 //ignore empty lines and treat those starting with '#' as comments 161 if ("".equals(text.trim()) || text.startsWith("#")) { 162 continue; 163 } 164 try { 165 AuthInfo ai = parseLine(text); 166 authInfo.add(ai); 167 } catch (Exception e) { 168 listener.onParsingError(text, locator); 169 } 170 } 171 } catch (IOException e) { 172 listener.onError(e, locator); 173 Logger.getLogger(DefaultAuthenticator.class.getName()).log(Level.SEVERE, e.getMessage(), e); 174 } 175 } finally { 176 try { 177 if (in != null) { 178 in.close(); 179 } 180 if (is != null) { 181 is.close(); 182 } 183 if (fi != null) { 184 fi.close(); 185 } 186 } catch (IOException ex) { 187 Logger.getLogger(DefaultAuthenticator.class.getName()).log(Level.SEVERE, null, ex); 188 } 189 } 190 } 191 192 private AuthInfo parseLine(String text) throws Exception { 193 URL url; 194 try { 195 url = new URL(text); 196 } catch (MalformedURLException mue) { 197 //possible cause of this can be that password contains 198 //character which has to be encoded in URL, 199 //such as '@', ')', '#' and few others 200 //so try to recreate the URL with encoded string 201 //between 2nd ':' and last '@' 202 int i = text.indexOf(':', text.indexOf(':') + 1) + 1; 203 int j = text.lastIndexOf('@'); 204 String encodedUrl = 205 text.substring(0, i) 206 + URLEncoder.encode(text.substring(i, j), "UTF-8") 207 + text.substring(j); 208 url = new URL(encodedUrl); 209 } 210 211 String authinfo = url.getUserInfo(); 212 213 if (authinfo != null) { 214 int i = authinfo.indexOf(':'); 215 216 if (i >= 0) { 217 String user = authinfo.substring(0, i); 218 String password = authinfo.substring(i + 1); 219 return new AuthInfo( 220 new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()), 221 user, URLDecoder.decode(password, "UTF-8")); 222 } 223 } 224 throw new Exception(); 225 } 226 227 static Authenticator getCurrentAuthenticator() { 228 final Field f = getTheAuthenticator(); 229 if (f == null) { 230 return null; 231 } 232 233 try { 234 AccessController.doPrivileged(new PrivilegedAction<Void>() { 235 @Override 236 public Void run() { 237 f.setAccessible(true); 238 return null; 239 } 240 }); 241 return (Authenticator) f.get(null); 242 } catch (Exception ex) { 243 return null; 244 } finally { 245 AccessController.doPrivileged(new PrivilegedAction<Void>() { 246 @Override 247 public Void run() { 248 f.setAccessible(false); 249 return null; 250 } 251 }); 252 } 253 } 254 255 private static Field getTheAuthenticator() { 256 try { 257 return Authenticator.class.getDeclaredField("theAuthenticator"); 258 } catch (Exception ex) { 259 return null; 260 } 261 } 262 263 public static interface Receiver { 264 265 void onParsingError(String line, Locator loc); 266 267 void onError(Exception e, Locator loc); 268 } 269 270 private static class DefaultRImpl implements Receiver { 271 272 @Override 273 public void onParsingError(String line, Locator loc) { 274 System.err.println(getLocationString(loc) + ": " + line); 275 } 276 277 @Override 278 public void onError(Exception e, Locator loc) { 279 System.err.println(getLocationString(loc) + ": " + e.getMessage()); 280 Logger.getLogger(DefaultAuthenticator.class.getName()).log(Level.SEVERE, e.getMessage(), e); 281 } 282 283 private String getLocationString(Locator l) { 284 return "[" + l.getSystemId() + "#" + l.getLineNumber() + "]"; 285 } 286 } 287 288 /** 289 * Represents authorization information needed by 290 * {@link DefaultAuthenticator} to authenticate access to remote resources. 291 * 292 * @author Vivek Pandey 293 * @author Lukas Jungmann 294 */ 295 final static class AuthInfo { 296 297 private final String user; 298 private final String password; 299 private final Pattern urlPattern; 300 301 public AuthInfo(URL url, String user, String password) { 302 String u = url.toExternalForm().replaceFirst("\\?", "\\\\?"); 303 this.urlPattern = Pattern.compile(u.replace("*", ".*"), Pattern.CASE_INSENSITIVE); 304 this.user = user; 305 this.password = password; 306 } 307 308 public String getUser() { 309 return user; 310 } 311 312 public String getPassword() { 313 return password; 314 } 315 316 /** 317 * Returns if the requesting host and port are associated with this 318 * {@link AuthInfo} 319 */ 320 public boolean matchingHost(URL requestingURL) { 321 return urlPattern.matcher(requestingURL.toExternalForm()).matches(); 322 } 323 } 324 }