001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.io.dav.http;
013:
014: import java.io.UnsupportedEncodingException;
015: import java.util.ArrayList;
016: import java.util.Collection;
017: import java.util.Collections;
018: import java.util.Comparator;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Map;
022: import java.util.StringTokenizer;
023: import java.util.TreeMap;
024:
025: import org.tmatesoft.svn.core.SVNErrorCode;
026: import org.tmatesoft.svn.core.SVNErrorMessage;
027: import org.tmatesoft.svn.core.SVNException;
028: import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
029: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
030:
031: /**
032: * @version 1.1.1
033: * @author TMate Software Ltd.
034: */
035: abstract class HTTPAuthentication {
036:
037: private Map myChallengeParameters;
038: private String myUserName;
039: private String myPassword;
040:
041: private static final String AUTH_METHODS_PROPERTY = "svnkit.http.methods";
042: private static final String OLD_AUTH_METHODS_PROPERTY = "javasvn.http.methods";
043:
044: protected HTTPAuthentication(SVNPasswordAuthentication credentials) {
045: if (credentials != null) {
046: myUserName = credentials.getUserName();
047: myPassword = credentials.getPassword();
048: }
049: }
050:
051: protected HTTPAuthentication(String name, String password) {
052: myUserName = name;
053: myPassword = password;
054: }
055:
056: protected HTTPAuthentication() {
057: }
058:
059: public void setChallengeParameter(String name, String value) {
060: Map params = getChallengeParameters();
061: params.put(name, value);
062: }
063:
064: public String getChallengeParameter(String name) {
065: if (myChallengeParameters == null) {
066: return null;
067: }
068: return (String) myChallengeParameters.get(name);
069: }
070:
071: protected Map getChallengeParameters() {
072: if (myChallengeParameters == null) {
073: myChallengeParameters = new TreeMap();
074: }
075: return myChallengeParameters;
076: }
077:
078: public void setCredentials(SVNPasswordAuthentication credentials) {
079: if (credentials != null) {
080: myUserName = credentials.getUserName();
081: myPassword = credentials.getPassword();
082: }
083: }
084:
085: public String getUserName() {
086: if (myUserName == null) {
087: myUserName = System.getProperty("user.name", "");
088: }
089: return myUserName;
090: }
091:
092: public String getPassword() {
093: return myPassword;
094: }
095:
096: public void setUserName(String name) {
097: myUserName = name;
098: }
099:
100: public void setPassword(String password) {
101: myPassword = password;
102: }
103:
104: public static HTTPAuthentication parseAuthParameters(
105: Collection authHeaderValues,
106: HTTPAuthentication prevResponse, String charset)
107: throws SVNException {
108: if (authHeaderValues == null) {
109: SVNErrorMessage err = SVNErrorMessage.create(
110: SVNErrorCode.UNSUPPORTED_FEATURE,
111: "Missing HTTP authorization method");
112: SVNErrorManager.error(err);
113: }
114:
115: HTTPAuthentication auth = null;
116: String authHeader = null;
117: // sort auth headers accordingly to priorities.
118: authHeaderValues = sortSchemes(authHeaderValues);
119:
120: for (Iterator authSchemes = authHeaderValues.iterator(); authSchemes
121: .hasNext();) {
122: authHeader = (String) authSchemes.next();
123: String source = authHeader.trim();
124: // parse strings: name="value" or name=value
125: int index = source.indexOf(' ');
126:
127: if (index <= 0) {
128: index = source.length();
129: if (!"NTLM"
130: .equalsIgnoreCase(source.substring(0, index))) {
131: continue;
132: }
133: }
134: String method = source.substring(0, index);
135:
136: source = source.substring(index).trim();
137: if ("Basic".equalsIgnoreCase(method)) {
138: auth = new HTTPBasicAuthentication(charset);
139:
140: if (source.indexOf("realm=") >= 0) {
141: source = source.substring(source.indexOf("realm=")
142: + "realm=".length());
143: source = source.trim();
144: if (source.startsWith("\"")) {
145: source = source.substring(1);
146: }
147: if (source.endsWith("\"")) {
148: source = source.substring(0,
149: source.length() - 1);
150: }
151: //parameters.put("realm", source);
152: auth.setChallengeParameter("realm", source);
153: }
154: break;
155: } else if ("Digest".equalsIgnoreCase(method)) {
156: auth = new HTTPDigestAuthentication();
157:
158: char[] chars = source.toCharArray();
159: int tokenIndex = 0;
160: boolean parsingToken = true;
161: String name = null;
162: String value;
163: int quotesCount = 0;
164:
165: for (int i = 0; i < chars.length; i++) {
166: if (parsingToken) {
167: if (chars[i] == '=') {
168: name = new String(chars, tokenIndex, i
169: - tokenIndex);
170: name = name.trim();
171: tokenIndex = i + 1;
172: parsingToken = false;
173: }
174: } else {
175: if (chars[i] == '\"') {
176: quotesCount = quotesCount > 0 ? 0 : 1;
177: } else if (i + 1 >= chars.length
178: || (chars[i] == ',' && quotesCount == 0)) {
179: value = new String(chars, tokenIndex, i
180: - tokenIndex);
181: value = value.trim();
182: if (value.charAt(0) == '\"'
183: && value.charAt(value.length() - 1) == '\"') {
184: value = value.substring(1);
185: value = value.substring(0, value
186: .length() - 1);
187: }
188: //parameters.put(name, value);
189: auth.setChallengeParameter(name, value);
190: tokenIndex = i + 1;
191: parsingToken = true;
192: }
193: }
194: }
195: HTTPDigestAuthentication digestAuth = (HTTPDigestAuthentication) auth;
196: digestAuth.init();
197:
198: break;
199: } else if ("NTLM".equalsIgnoreCase(method)) {
200: HTTPNTLMAuthentication ntlmAuth = null;
201: if (source.length() == 0) {
202: ntlmAuth = new HTTPNTLMAuthentication(charset);
203: ntlmAuth.setType1State();
204: } else {
205: ntlmAuth = (HTTPNTLMAuthentication) prevResponse;
206: ntlmAuth.parseChallenge(source);
207: ntlmAuth.setType3State();
208: }
209: auth = ntlmAuth;
210: break;
211: }
212: }
213:
214: if (auth == null) {
215: SVNErrorMessage err = SVNErrorMessage
216: .create(
217: SVNErrorCode.UNSUPPORTED_FEATURE,
218: "HTTP authorization method ''{0}'' is not supported",
219: authHeader);
220: SVNErrorManager.error(err);
221: }
222:
223: if (prevResponse != null) {
224: auth.setUserName(prevResponse.getUserName());
225: auth.setPassword(prevResponse.getPassword());
226: }
227:
228: return auth;
229: }
230:
231: public static boolean isSchemeSupportedByServer(String scheme,
232: Collection authHeaderValues) throws SVNException {
233: if (authHeaderValues == null) {
234: SVNErrorMessage err = SVNErrorMessage.create(
235: SVNErrorCode.UNSUPPORTED_FEATURE,
236: "Missing HTTP authorization method");
237: SVNErrorManager.error(err);
238: }
239:
240: String authHeader = null;
241: for (Iterator authSchemes = authHeaderValues.iterator(); authSchemes
242: .hasNext();) {
243: authHeader = (String) authSchemes.next();
244: String source = authHeader.trim();
245: int index = source.indexOf(' ');
246:
247: if (index <= 0) {
248: index = source.length();
249: }
250: String method = source.substring(0, index);
251: if (method.equalsIgnoreCase(scheme)) {
252: return true;
253: }
254: }
255: return false;
256: }
257:
258: private static Collection sortSchemes(Collection authHeaders) {
259: String priorities = System.getProperty(AUTH_METHODS_PROPERTY,
260: System.getProperty(OLD_AUTH_METHODS_PROPERTY));
261: if (priorities == null) {
262: return authHeaders;
263: }
264: final List schemes = new ArrayList();
265: for (StringTokenizer tokens = new StringTokenizer(priorities,
266: " ,"); tokens.hasMoreTokens();) {
267: String scheme = tokens.nextToken();
268: if (!schemes.contains(scheme)) {
269: schemes.add(scheme);
270: }
271: }
272: List ordered = new ArrayList(authHeaders);
273: Collections.sort(ordered, new Comparator() {
274: public int compare(Object o1, Object o2) {
275: String header1 = (String) o1;
276: String header2 = (String) o2;
277:
278: String scheme1 = getSchemeName(header1);
279: String scheme2 = getSchemeName(header2);
280:
281: int index1 = schemes.indexOf(scheme1);
282: int index2 = schemes.indexOf(scheme2);
283:
284: index1 = index1 < 0 ? Integer.MAX_VALUE : index1;
285: index2 = index2 < 0 ? Integer.MAX_VALUE : index2;
286: if (index1 == index2) {
287: return 0;
288: }
289: return index1 > index2 ? 1 : -1;
290: }
291: });
292: return ordered;
293: }
294:
295: private static String getSchemeName(String header) {
296: String source = header.trim();
297: int index = source.indexOf(' ');
298: if (index <= 0) {
299: index = source.length();
300: }
301: return source.substring(0, index);
302: }
303:
304: public abstract String getAuthenticationScheme();
305:
306: public abstract String authenticate() throws SVNException;
307:
308: protected static byte[] getASCIIBytes(final String data) {
309: return getBytes(data, "US-ASCII");
310: }
311:
312: protected static byte[] getBytes(final String data, String charset) {
313: try {
314: return data.getBytes(charset);
315: } catch (UnsupportedEncodingException e) {
316: return data.getBytes();
317: }
318: }
319:
320: }
|