• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.http;
18 
19 import java.util.Locale;
20 
21 /**
22  * HttpAuthHeader: a class to store HTTP authentication-header parameters.
23  * For more information, see: RFC 2617: HTTP Authentication.
24  */
25 public class HttpAuthHeader {
26     /**
27      * Possible HTTP-authentication header tokens to search for:
28      */
29     public final static String BASIC_TOKEN = "Basic";
30     public final static String DIGEST_TOKEN = "Digest";
31 
32     private final static String REALM_TOKEN = "realm";
33     private final static String NONCE_TOKEN = "nonce";
34     private final static String STALE_TOKEN = "stale";
35     private final static String OPAQUE_TOKEN = "opaque";
36     private final static String QOP_TOKEN = "qop";
37     private final static String ALGORITHM_TOKEN = "algorithm";
38 
39     /**
40      * An authentication scheme. We currently support two different schemes:
41      * HttpAuthHeader.BASIC  - basic, and
42      * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth").
43      */
44     private int mScheme;
45 
46     public static final int UNKNOWN = 0;
47     public static final int BASIC = 1;
48     public static final int DIGEST = 2;
49 
50     /**
51      * A flag, indicating that the previous request from the client was
52      * rejected because the nonce value was stale. If stale is TRUE
53      * (case-insensitive), the client may wish to simply retry the request
54      * with a new encrypted response, without reprompting the user for a
55      * new username and password.
56      */
57     private boolean mStale;
58 
59     /**
60      * A string to be displayed to users so they know which username and
61      * password to use.
62      */
63     private String mRealm;
64 
65     /**
66      * A server-specified data string which should be uniquely generated
67      * each time a 401 response is made.
68      */
69     private String mNonce;
70 
71     /**
72      * A string of data, specified by the server, which should be returned
73      *  by the client unchanged in the Authorization header of subsequent
74      * requests with URIs in the same protection space.
75      */
76     private String mOpaque;
77 
78     /**
79      * This directive is optional, but is made so only for backward
80      * compatibility with RFC 2069 [6]; it SHOULD be used by all
81      * implementations compliant with this version of the Digest scheme.
82      * If present, it is a quoted string of one or more tokens indicating
83      * the "quality of protection" values supported by the server.  The
84      * value "auth" indicates authentication; the value "auth-int"
85      * indicates authentication with integrity protection.
86      */
87     private String mQop;
88 
89     /**
90      * A string indicating a pair of algorithms used to produce the digest
91      * and a checksum. If this is not present it is assumed to be "MD5".
92      */
93     private String mAlgorithm;
94 
95     /**
96      * Is this authentication request a proxy authentication request?
97      */
98     private boolean mIsProxy;
99 
100     /**
101      * Username string we get from the user.
102      */
103     private String mUsername;
104 
105     /**
106      * Password string we get from the user.
107      */
108     private String mPassword;
109 
110     /**
111      * Creates a new HTTP-authentication header object from the
112      * input header string.
113      * The header string is assumed to contain parameters of at
114      * most one authentication-scheme (ensured by the caller).
115      */
HttpAuthHeader(String header)116     public HttpAuthHeader(String header) {
117         if (header != null) {
118             parseHeader(header);
119         }
120     }
121 
122     /**
123      * @return True iff this is a proxy authentication header.
124      */
isProxy()125     public boolean isProxy() {
126         return mIsProxy;
127     }
128 
129     /**
130      * Marks this header as a proxy authentication header.
131      */
setProxy()132     public void setProxy() {
133         mIsProxy = true;
134     }
135 
136     /**
137      * @return The username string.
138      */
getUsername()139     public String getUsername() {
140         return mUsername;
141     }
142 
143     /**
144      * Sets the username string.
145      */
setUsername(String username)146     public void setUsername(String username) {
147         mUsername = username;
148     }
149 
150     /**
151      * @return The password string.
152      */
getPassword()153     public String getPassword() {
154         return mPassword;
155     }
156 
157     /**
158      * Sets the password string.
159      */
setPassword(String password)160     public void setPassword(String password) {
161         mPassword = password;
162     }
163 
164     /**
165      * @return True iff this is the  BASIC-authentication request.
166      */
isBasic()167     public boolean isBasic () {
168         return mScheme == BASIC;
169     }
170 
171     /**
172      * @return True iff this is the DIGEST-authentication request.
173      */
isDigest()174     public boolean isDigest() {
175         return mScheme == DIGEST;
176     }
177 
178     /**
179      * @return The authentication scheme requested. We currently
180      * support two schemes:
181      * HttpAuthHeader.BASIC  - basic, and
182      * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth").
183      */
getScheme()184     public int getScheme() {
185         return mScheme;
186     }
187 
188     /**
189      * @return True if indicating that the previous request from
190      * the client was rejected because the nonce value was stale.
191      */
getStale()192     public boolean getStale() {
193         return mStale;
194     }
195 
196     /**
197      * @return The realm value or null if there is none.
198      */
getRealm()199     public String getRealm() {
200         return mRealm;
201     }
202 
203     /**
204      * @return The nonce value or null if there is none.
205      */
getNonce()206     public String getNonce() {
207         return mNonce;
208     }
209 
210     /**
211      * @return The opaque value or null if there is none.
212      */
getOpaque()213     public String getOpaque() {
214         return mOpaque;
215     }
216 
217     /**
218      * @return The QOP ("quality-of_protection") value or null if
219      * there is none. The QOP value is always lower-case.
220      */
getQop()221     public String getQop() {
222         return mQop;
223     }
224 
225     /**
226      * @return The name of the algorithm used or null if there is
227      * none. By default, MD5 is used.
228      */
getAlgorithm()229     public String getAlgorithm() {
230         return mAlgorithm;
231     }
232 
233     /**
234      * @return True iff the authentication scheme requested by the
235      * server is supported; currently supported schemes:
236      * BASIC,
237      * DIGEST (only algorithm="md5", no qop or qop="auth).
238      */
isSupportedScheme()239     public boolean isSupportedScheme() {
240         // it is a good idea to enforce non-null realms!
241         if (mRealm != null) {
242             if (mScheme == BASIC) {
243                 return true;
244             } else {
245                 if (mScheme == DIGEST) {
246                     return
247                         mAlgorithm.equals("md5") &&
248                         (mQop == null || mQop.equals("auth"));
249                 }
250             }
251         }
252 
253         return false;
254     }
255 
256     /**
257      * Parses the header scheme name and then scheme parameters if
258      * the scheme is supported.
259      */
parseHeader(String header)260     private void parseHeader(String header) {
261         if (HttpLog.LOGV) {
262             HttpLog.v("HttpAuthHeader.parseHeader(): header: " + header);
263         }
264 
265         if (header != null) {
266             String parameters = parseScheme(header);
267             if (parameters != null) {
268                 // if we have a supported scheme
269                 if (mScheme != UNKNOWN) {
270                     parseParameters(parameters);
271                 }
272             }
273         }
274     }
275 
276     /**
277      * Parses the authentication scheme name. If we have a Digest
278      * scheme, sets the algorithm value to the default of MD5.
279      * @return The authentication scheme parameters string to be
280      * parsed later (if the scheme is supported) or null if failed
281      * to parse the scheme (the header value is null?).
282      */
parseScheme(String header)283     private String parseScheme(String header) {
284         if (header != null) {
285             int i = header.indexOf(' ');
286             if (i >= 0) {
287                 String scheme = header.substring(0, i).trim();
288                 if (scheme.equalsIgnoreCase(DIGEST_TOKEN)) {
289                     mScheme = DIGEST;
290 
291                     // md5 is the default algorithm!!!
292                     mAlgorithm = "md5";
293                 } else {
294                     if (scheme.equalsIgnoreCase(BASIC_TOKEN)) {
295                         mScheme = BASIC;
296                     }
297                 }
298 
299                 return header.substring(i + 1);
300             }
301         }
302 
303         return null;
304     }
305 
306     /**
307      * Parses a comma-separated list of authentification scheme
308      * parameters.
309      */
parseParameters(String parameters)310     private void parseParameters(String parameters) {
311         if (HttpLog.LOGV) {
312             HttpLog.v("HttpAuthHeader.parseParameters():" +
313                       " parameters: " + parameters);
314         }
315 
316         if (parameters != null) {
317             int i;
318             do {
319                 i = parameters.indexOf(',');
320                 if (i < 0) {
321                     // have only one parameter
322                     parseParameter(parameters);
323                 } else {
324                     parseParameter(parameters.substring(0, i));
325                     parameters = parameters.substring(i + 1);
326                 }
327             } while (i >= 0);
328         }
329     }
330 
331     /**
332      * Parses a single authentication scheme parameter. The parameter
333      * string is expected to follow the format: PARAMETER=VALUE.
334      */
parseParameter(String parameter)335     private void parseParameter(String parameter) {
336         if (parameter != null) {
337             // here, we are looking for the 1st occurence of '=' only!!!
338             int i = parameter.indexOf('=');
339             if (i >= 0) {
340                 String token = parameter.substring(0, i).trim();
341                 String value =
342                     trimDoubleQuotesIfAny(parameter.substring(i + 1).trim());
343 
344                 if (HttpLog.LOGV) {
345                     HttpLog.v("HttpAuthHeader.parseParameter():" +
346                               " token: " + token +
347                               " value: " + value);
348                 }
349 
350                 if (token.equalsIgnoreCase(REALM_TOKEN)) {
351                     mRealm = value;
352                 } else {
353                     if (mScheme == DIGEST) {
354                         parseParameter(token, value);
355                     }
356                 }
357             }
358         }
359     }
360 
361     /**
362      * If the token is a known parameter name, parses and initializes
363      * the token value.
364      */
parseParameter(String token, String value)365     private void parseParameter(String token, String value) {
366         if (token != null && value != null) {
367             if (token.equalsIgnoreCase(NONCE_TOKEN)) {
368                 mNonce = value;
369                 return;
370             }
371 
372             if (token.equalsIgnoreCase(STALE_TOKEN)) {
373                 parseStale(value);
374                 return;
375             }
376 
377             if (token.equalsIgnoreCase(OPAQUE_TOKEN)) {
378                 mOpaque = value;
379                 return;
380             }
381 
382             if (token.equalsIgnoreCase(QOP_TOKEN)) {
383                 mQop = value.toLowerCase(Locale.ROOT);
384                 return;
385             }
386 
387             if (token.equalsIgnoreCase(ALGORITHM_TOKEN)) {
388                 mAlgorithm = value.toLowerCase(Locale.ROOT);
389                 return;
390             }
391         }
392     }
393 
394     /**
395      * Parses and initializes the 'stale' paramer value. Any value
396      * different from case-insensitive "true" is considered "false".
397      */
parseStale(String value)398     private void parseStale(String value) {
399         if (value != null) {
400             if (value.equalsIgnoreCase("true")) {
401                 mStale = true;
402             }
403         }
404     }
405 
406     /**
407      * Trims double-quotes around a parameter value if there are any.
408      * @return The string value without the outermost pair of double-
409      * quotes or null if the original value is null.
410      */
trimDoubleQuotesIfAny(String value)411     static private String trimDoubleQuotesIfAny(String value) {
412         if (value != null) {
413             int len = value.length();
414             if (len > 2 &&
415                 value.charAt(0) == '\"' && value.charAt(len - 1) == '\"') {
416                 return value.substring(1, len - 1);
417             }
418         }
419 
420         return value;
421     }
422 }
423