0001: /*
0002: * @(#)DefaultAuthHandler.java 0.3-2 18/06/1999
0003: *
0004: * This file is part of the HTTPClient package
0005: * Copyright (C) 1996-1999 Ronald Tschalär
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free
0019: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
0020: * MA 02111-1307, USA
0021: *
0022: * For questions, suggestions, bug-reports, enhancement-requests etc.
0023: * I may be contacted at:
0024: *
0025: * ronald@innovation.ch
0026: *
0027: */
0028:
0029: package HTTPClient;
0030:
0031: import java.io.IOException;
0032: import java.io.FileInputStream;
0033: import java.io.DataInputStream;
0034: import java.util.Vector;
0035: import java.util.StringTokenizer;
0036:
0037: import java.awt.Frame;
0038: import java.awt.Panel;
0039: import java.awt.Label;
0040: import java.awt.Button;
0041: import java.awt.Dimension;
0042: import java.awt.TextField;
0043: import java.awt.GridLayout;
0044: import java.awt.BorderLayout;
0045: import java.awt.GridBagLayout;
0046: import java.awt.GridBagConstraints;
0047: import java.awt.event.ActionEvent;
0048: import java.awt.event.ActionListener;
0049: import java.awt.event.WindowEvent;
0050: import java.awt.event.WindowAdapter;
0051:
0052: /**
0053: * A simple authorization handler that throws up a message box requesting
0054: * both a username and password. This is default authorization handler.
0055: * Currently only handles the authentication types "Basic", "Digest" and
0056: * "SOCKS5" (used for the SocksClient and not part of HTTP per se).
0057: *
0058: * @version 0.3-2 18/06/1999
0059: * @author Ronald Tschalär
0060: * @since V0.2
0061: */
0062:
0063: class DefaultAuthHandler implements AuthorizationHandler,
0064: GlobalConstants {
0065: private static final byte[] NUL = new byte[0];
0066:
0067: private static final int DI_A1 = 0;
0068: private static final int DI_A1S = 1;
0069: private static final int DI_QOP = 2;
0070:
0071: private static byte[] digest_secret = null;
0072:
0073: private BasicAuthBox inp = null;
0074:
0075: /**
0076: * For Digest authentication we need to set the uri, response and
0077: * opaque parameters. For "Basic" and "SOCKS5" nothing is done.
0078: */
0079: public AuthorizationInfo fixupAuthInfo(AuthorizationInfo info,
0080: RoRequest req, AuthorizationInfo challenge, RoResponse resp)
0081: throws AuthSchemeNotImplException {
0082: // nothing to do for Basic and SOCKS5 schemes
0083:
0084: if (info.getScheme().equalsIgnoreCase("Basic")
0085: || info.getScheme().equalsIgnoreCase("SOCKS5"))
0086: return info;
0087: else if (!info.getScheme().equalsIgnoreCase("Digest"))
0088: throw new AuthSchemeNotImplException(info.getScheme());
0089:
0090: if (DebugAuth)
0091: System.err
0092: .println("Auth: fixing up Authorization for host "
0093: + info.getHost() + ":" + info.getPort()
0094: + "; scheme: " + info.getScheme()
0095: + "; realm: " + info.getRealm());
0096:
0097: return digest_fixup(info, req, challenge, resp);
0098: }
0099:
0100: /**
0101: * returns the requested authorization, or null if none was given.
0102: *
0103: * @param challenge the parsed challenge from the server.
0104: * @param req the request which solicited this response
0105: * @param resp the full response received
0106: * @return a structure containing the necessary authorization info,
0107: * or null
0108: * @exception AuthSchemeNotImplException if the authentication scheme
0109: * in the challenge cannot be handled.
0110: */
0111: public AuthorizationInfo getAuthorization(
0112: AuthorizationInfo challenge, RoRequest req, RoResponse resp)
0113: throws AuthSchemeNotImplException {
0114: AuthorizationInfo cred;
0115:
0116: if (DebugAuth)
0117: System.err
0118: .println("Auth: Requesting Authorization for host "
0119: + challenge.getHost()
0120: + ":"
0121: + challenge.getPort()
0122: + "; scheme: "
0123: + challenge.getScheme()
0124: + "; realm: "
0125: + challenge.getRealm());
0126:
0127: // we only handle Basic, Digest and SOCKS5 authentication
0128:
0129: if (!challenge.getScheme().equalsIgnoreCase("Basic")
0130: && !challenge.getScheme().equalsIgnoreCase("Digest")
0131: && !challenge.getScheme().equalsIgnoreCase("SOCKS5"))
0132: throw new AuthSchemeNotImplException(challenge.getScheme());
0133:
0134: // For digest authentication, check if stale is set
0135:
0136: if (challenge.getScheme().equalsIgnoreCase("Digest")) {
0137: cred = digest_check_stale(challenge, req, resp);
0138: if (cred != null)
0139: return cred;
0140: }
0141:
0142: // Ask the user for username/password
0143:
0144: if (!req.allowUI())
0145: return null;
0146:
0147: if (inp == null) {
0148: synchronized (getClass()) {
0149: if (inp == null)
0150: inp = new BasicAuthBox();
0151: }
0152: }
0153:
0154: NVPair answer;
0155: if (challenge.getScheme().equalsIgnoreCase("basic")
0156: || challenge.getScheme().equalsIgnoreCase("Digest")) {
0157: answer = inp.getInput(
0158: "Enter username and password for realm `"
0159: + challenge.getRealm() + "'", "on host "
0160: + challenge.getHost() + ":"
0161: + challenge.getPort(),
0162: "Authentication Scheme: " + challenge.getScheme());
0163: } else {
0164: answer = inp.getInput(
0165: "Enter username and password for SOCKS "
0166: + "server on host ", challenge.getHost(),
0167: "Authentication Method: username/password");
0168: }
0169:
0170: if (answer == null)
0171: return null;
0172:
0173: // Now process the username/password
0174:
0175: if (challenge.getScheme().equalsIgnoreCase("basic")) {
0176: cred = new AuthorizationInfo(challenge.getHost(), challenge
0177: .getPort(), challenge.getScheme(), challenge
0178: .getRealm(), Codecs.base64Encode(answer.getName()
0179: + ":" + answer.getValue()));
0180: } else if (challenge.getScheme().equalsIgnoreCase("Digest")) {
0181: cred = digest_gen_auth_info(challenge.getHost(), challenge
0182: .getPort(), challenge.getRealm(), answer.getName(),
0183: answer.getValue(), req.getConnection().getContext());
0184: cred = digest_fixup(cred, req, challenge, null);
0185: } else // SOCKS5
0186: {
0187: NVPair[] upwd = { answer };
0188: cred = new AuthorizationInfo(challenge.getHost(), challenge
0189: .getPort(), challenge.getScheme(), challenge
0190: .getRealm(), upwd, null);
0191: }
0192:
0193: // try to get rid of any unencoded passwords in memory
0194:
0195: answer = null;
0196: System.gc();
0197:
0198: // Done
0199:
0200: if (DebugAuth)
0201: System.err.println("Auth: Got Authorization");
0202:
0203: return cred;
0204: }
0205:
0206: /**
0207: * We handle the "Authentication-Info" and "Proxy-Authentication-Info"
0208: * headers here.
0209: */
0210: public void handleAuthHeaders(Response resp, RoRequest req,
0211: AuthorizationInfo prev, AuthorizationInfo prxy)
0212: throws IOException {
0213: String auth_info = resp.getHeader("Authentication-Info");
0214: String prxy_info = resp.getHeader("Proxy-Authentication-Info");
0215:
0216: if (auth_info == null && prev != null
0217: && hasParam(prev.getParams(), "qop", "auth-int"))
0218: auth_info = "";
0219:
0220: if (prxy_info == null && prxy != null
0221: && hasParam(prxy.getParams(), "qop", "auth-int"))
0222: prxy_info = "";
0223:
0224: try {
0225: handleAuthInfo(auth_info, "Authentication-Info", prev,
0226: resp, req, true);
0227: handleAuthInfo(prxy_info, "Proxy-Authentication-Info",
0228: prxy, resp, req, true);
0229: } catch (ParseException pe) {
0230: throw new IOException(pe.toString());
0231: }
0232: }
0233:
0234: /**
0235: * We handle the "Authentication-Info" and "Proxy-Authentication-Info"
0236: * trailers here.
0237: */
0238: public void handleAuthTrailers(Response resp, RoRequest req,
0239: AuthorizationInfo prev, AuthorizationInfo prxy)
0240: throws IOException {
0241: String auth_info = resp.getTrailer("Authentication-Info");
0242: String prxy_info = resp.getTrailer("Proxy-Authentication-Info");
0243:
0244: try {
0245: handleAuthInfo(auth_info, "Authentication-Info", prev,
0246: resp, req, false);
0247: handleAuthInfo(prxy_info, "Proxy-Authentication-Info",
0248: prxy, resp, req, false);
0249: } catch (ParseException pe) {
0250: throw new IOException(pe.toString());
0251: }
0252: }
0253:
0254: private static void handleAuthInfo(String auth_info,
0255: String hdr_name, AuthorizationInfo prev, Response resp,
0256: RoRequest req, boolean in_headers) throws ParseException,
0257: IOException {
0258: if (auth_info == null)
0259: return;
0260:
0261: Vector pai = Util.parseHeader(auth_info);
0262: HttpHeaderElement elem;
0263:
0264: if (handle_nextnonce(prev, req, elem = Util.getElement(pai,
0265: "nextnonce")))
0266: pai.removeElement(elem);
0267: if (handle_discard(prev, req, elem = Util.getElement(pai,
0268: "discard")))
0269: pai.removeElement(elem);
0270:
0271: if (in_headers) {
0272: HttpHeaderElement qop = null;
0273:
0274: if (pai != null
0275: && (qop = Util.getElement(pai, "qop")) != null
0276: && qop.getValue() != null) {
0277: handle_rspauth(prev, resp, req, pai, hdr_name);
0278: } else if (prev != null
0279: && (Util.hasToken(resp.getHeader("Trailer"),
0280: hdr_name)
0281: && hasParam(prev.getParams(), "qop", null) || hasParam(
0282: prev.getParams(), "qop", "auth-int"))) {
0283: handle_rspauth(prev, resp, req, null, hdr_name);
0284: }
0285:
0286: else if ((pai != null && qop == null && pai
0287: .contains(new HttpHeaderElement("digest")))
0288: || (Util.hasToken(resp.getHeader("Trailer"),
0289: hdr_name)
0290: && prev != null && !hasParam(prev
0291: .getParams(), "qop", null))) {
0292: handle_digest(prev, resp, req, hdr_name);
0293: }
0294: }
0295:
0296: if (pai.size() > 0)
0297: resp.setHeader(hdr_name, Util.assembleHeader(pai));
0298: else
0299: resp.deleteHeader(hdr_name);
0300: }
0301:
0302: private static final boolean hasParam(NVPair[] params, String name,
0303: String val) {
0304: for (int idx = 0; idx < params.length; idx++)
0305: if (params[idx].getName().equalsIgnoreCase(name)
0306: && (val == null || params[idx].getValue()
0307: .equalsIgnoreCase(val)))
0308: return true;
0309:
0310: return false;
0311: }
0312:
0313: /*
0314: * Here are all the Digest specific methods
0315: */
0316:
0317: private static AuthorizationInfo digest_gen_auth_info(String host,
0318: int port, String realm, String user, String pass,
0319: Object context) {
0320: String A1 = user + ":" + realm + ":" + pass;
0321: String[] info = { new MD5(A1).asHex(), null, null };
0322:
0323: AuthorizationInfo prev = AuthorizationInfo.getAuthorization(
0324: host, port, "Digest", realm, context);
0325: NVPair[] params;
0326: if (prev == null) {
0327: params = new NVPair[4];
0328: params[0] = new NVPair("username", user);
0329: params[1] = new NVPair("uri", "");
0330: params[2] = new NVPair("nonce", "");
0331: params[3] = new NVPair("response", "");
0332: } else {
0333: params = prev.getParams();
0334: for (int idx = 0; idx < params.length; idx++) {
0335: if (params[idx].getName().equalsIgnoreCase("username")) {
0336: params[idx] = new NVPair("username", user);
0337: break;
0338: }
0339: }
0340: }
0341:
0342: return new AuthorizationInfo(host, port, "Digest", realm,
0343: params, info);
0344: }
0345:
0346: /**
0347: * The fixup handler
0348: */
0349: private static AuthorizationInfo digest_fixup(
0350: AuthorizationInfo info, RoRequest req,
0351: AuthorizationInfo challenge, RoResponse resp)
0352: throws AuthSchemeNotImplException {
0353: // get various parameters from challenge
0354:
0355: int ch_domain = -1, ch_nonce = -1, ch_alg = -1, ch_opaque = -1, ch_stale = -1, ch_dreq = -1, ch_qop = -1;
0356: NVPair[] ch_params = null;
0357: if (challenge != null) {
0358: ch_params = challenge.getParams();
0359:
0360: for (int idx = 0; idx < ch_params.length; idx++) {
0361: String name = ch_params[idx].getName().toLowerCase();
0362: if (name.equals("domain"))
0363: ch_domain = idx;
0364: else if (name.equals("nonce"))
0365: ch_nonce = idx;
0366: else if (name.equals("opaque"))
0367: ch_opaque = idx;
0368: else if (name.equals("algorithm"))
0369: ch_alg = idx;
0370: else if (name.equals("stale"))
0371: ch_stale = idx;
0372: else if (name.equals("digest-required"))
0373: ch_dreq = idx;
0374: else if (name.equals("qop"))
0375: ch_qop = idx;
0376: }
0377: }
0378:
0379: // get various parameters from info
0380:
0381: int uri = -1, user = -1, alg = -1, response = -1, nonce = -1, cnonce = -1, nc = -1, opaque = -1, digest = -1, dreq = -1, qop = -1;
0382: NVPair[] params;
0383: String[] extra;
0384:
0385: synchronized (info) // we need to juggle nonce, nc, etc
0386: {
0387: params = info.getParams();
0388:
0389: for (int idx = 0; idx < params.length; idx++) {
0390: String name = params[idx].getName().toLowerCase();
0391: if (name.equals("uri"))
0392: uri = idx;
0393: else if (name.equals("username"))
0394: user = idx;
0395: else if (name.equals("algorithm"))
0396: alg = idx;
0397: else if (name.equals("nonce"))
0398: nonce = idx;
0399: else if (name.equals("cnonce"))
0400: cnonce = idx;
0401: else if (name.equals("nc"))
0402: nc = idx;
0403: else if (name.equals("response"))
0404: response = idx;
0405: else if (name.equals("opaque"))
0406: opaque = idx;
0407: else if (name.equals("digest"))
0408: digest = idx;
0409: else if (name.equals("digest-required"))
0410: dreq = idx;
0411: else if (name.equals("qop"))
0412: qop = idx;
0413: }
0414:
0415: extra = (String[]) info.getExtraInfo();
0416:
0417: // currently only MD5 hash (and "MD5-sess") is supported
0418:
0419: if (alg != -1
0420: && !params[alg].getValue().equalsIgnoreCase("MD5")
0421: && !params[alg].getValue().equalsIgnoreCase(
0422: "MD5-sess"))
0423: throw new AuthSchemeNotImplException(
0424: "Digest auth scheme: " + "Algorithm "
0425: + params[alg].getValue()
0426: + " not implemented");
0427:
0428: if (ch_alg != -1
0429: && !ch_params[ch_alg].getValue().equalsIgnoreCase(
0430: "MD5")
0431: && !ch_params[ch_alg].getValue().equalsIgnoreCase(
0432: "MD5-sess"))
0433: throw new AuthSchemeNotImplException(
0434: "Digest auth scheme: " + "Algorithm "
0435: + ch_params[ch_alg].getValue()
0436: + " not implemented");
0437:
0438: // fix up uri and nonce
0439:
0440: params[uri] = new NVPair("uri", req.getRequestURI());
0441: String old_nonce = params[nonce].getValue();
0442: if (ch_nonce != -1
0443: && !old_nonce
0444: .equals(ch_params[ch_nonce].getValue()))
0445: params[nonce] = ch_params[ch_nonce];
0446:
0447: // update or add optional attributes (opaque, algorithm, cnonce,
0448: // nonce-count, and qop
0449:
0450: if (ch_opaque != -1) {
0451: if (opaque == -1) {
0452: params = Util
0453: .resizeArray(params, params.length + 1);
0454: opaque = params.length - 1;
0455: }
0456: params[opaque] = ch_params[ch_opaque];
0457: }
0458:
0459: if (ch_alg != -1) {
0460: if (alg == -1) {
0461: params = Util
0462: .resizeArray(params, params.length + 1);
0463: alg = params.length - 1;
0464: }
0465: params[alg] = ch_params[ch_alg];
0466: }
0467:
0468: if (ch_qop != -1
0469: || (ch_alg != -1 && ch_params[ch_alg].getValue()
0470: .equalsIgnoreCase("MD5-sess"))) {
0471: if (cnonce == -1) {
0472: params = Util
0473: .resizeArray(params, params.length + 1);
0474: cnonce = params.length - 1;
0475: }
0476:
0477: if (digest_secret == null)
0478: digest_secret = gen_random_bytes(20);
0479:
0480: long l_time = System.currentTimeMillis();
0481: byte[] time = new byte[8];
0482: time[0] = (byte) (l_time & 0xFF);
0483: time[1] = (byte) ((l_time >> 8) & 0xFF);
0484: time[2] = (byte) ((l_time >> 16) & 0xFF);
0485: time[3] = (byte) ((l_time >> 24) & 0xFF);
0486: time[4] = (byte) ((l_time >> 32) & 0xFF);
0487: time[5] = (byte) ((l_time >> 40) & 0xFF);
0488: time[6] = (byte) ((l_time >> 48) & 0xFF);
0489: time[7] = (byte) ((l_time >> 56) & 0xFF);
0490:
0491: MD5 hash = new MD5(digest_secret);
0492: hash.Update(time);
0493: params[cnonce] = new NVPair("cnonce", hash.asHex());
0494: }
0495:
0496: // select qop option
0497:
0498: if (ch_qop != -1) {
0499: if (qop == -1) {
0500: params = Util
0501: .resizeArray(params, params.length + 1);
0502: qop = params.length - 1;
0503: }
0504: extra[DI_QOP] = ch_params[ch_qop].getValue();
0505:
0506: // select qop option
0507:
0508: String[] qops = splitList(extra[DI_QOP], ",");
0509: String p = null;
0510: for (int idx = 0; idx < qops.length; idx++) {
0511: if (qops[idx].equalsIgnoreCase("auth-int")
0512: && (req.getStream() == null || req
0513: .getConnection().ServProtVersKnown
0514: && req.getConnection().ServerProtocolVersion >= HTTP_1_1)) {
0515: p = "auth-int";
0516: break;
0517: }
0518: if (qops[idx].equalsIgnoreCase("auth"))
0519: p = "auth";
0520: }
0521: if (p == null) {
0522: for (int idx = 0; idx < qops.length; idx++)
0523: if (qops[idx].equalsIgnoreCase("auth-int"))
0524: throw new AuthSchemeNotImplException(
0525: "Digest auth scheme: Can't comply with qop "
0526: + "option 'auth-int' because an HttpOutputStream "
0527: + "is being used and the server doesn't speak "
0528: + "HTTP/1.1");
0529:
0530: throw new AuthSchemeNotImplException(
0531: "Digest auth scheme: "
0532: + "None of the available qop options '"
0533: + ch_params[ch_qop].getValue()
0534: + "' implemented");
0535: }
0536: params[qop] = new NVPair("qop", p);
0537: }
0538:
0539: // increment nonce-count.
0540:
0541: if (qop != -1) {
0542: /* Note: we should actually be serializing all requests through
0543: * here so that the server sees the nonce-count in a
0544: * strictly increasing order. However, this would be a
0545: * *major* hassle to do, so we're just winging it. Most
0546: * of the time the requests will go over the wire in the
0547: * same order as they pass through here, but in MT apps
0548: * it's possible for one request to "overtake" another
0549: * between here and the synchronized block in
0550: * sendRequest().
0551: */
0552: if (nc == -1) {
0553: params = Util
0554: .resizeArray(params, params.length + 1);
0555: nc = params.length - 1;
0556: params[nc] = new NVPair("nc", "00000001");
0557: } else if (old_nonce.equals(params[nonce].getValue())) {
0558: String c = Long.toHexString(Long.parseLong(
0559: params[nc].getValue(), 16) + 1);
0560: params[nc] = new NVPair("nc", "00000000"
0561: .substring(c.length())
0562: + c);
0563: } else
0564: params[nc] = new NVPair("nc", "00000001");
0565: }
0566:
0567: // calc new session key if necessary
0568:
0569: if (challenge != null
0570: && (ch_stale == -1 || !ch_params[ch_stale]
0571: .getValue().equalsIgnoreCase("true"))
0572: && alg != -1
0573: && params[alg].getValue().equalsIgnoreCase(
0574: "MD5-sess")) {
0575: extra[DI_A1S] = new MD5(extra[DI_A1] + ":"
0576: + params[nonce].getValue() + ":"
0577: + params[cnonce].getValue()).asHex();
0578: }
0579:
0580: // update parameters for next auth cycle
0581:
0582: info.setParams(params);
0583: info.setExtraInfo(extra);
0584: }
0585:
0586: // calc "response" attribute
0587:
0588: String hash = null;
0589: if (qop != -1
0590: && params[qop].getValue().equalsIgnoreCase("auth-int")
0591: && req.getStream() == null) {
0592: MD5 entity_hash = new MD5();
0593: entity_hash.Update(req.getData() == null ? NUL : req
0594: .getData());
0595: hash = entity_hash.asHex();
0596: }
0597:
0598: if (req.getStream() == null)
0599: params[response] = new NVPair("response", calcResponseAttr(
0600: hash, extra, params, alg, uri, qop, nonce, nc,
0601: cnonce, req.getMethod()));
0602:
0603: // calc digest if necessary
0604:
0605: AuthorizationInfo new_info;
0606:
0607: boolean ch_dreq_val = false;
0608: if (ch_dreq != -1
0609: && (ch_params[ch_dreq].getValue() == null || ch_params[ch_dreq]
0610: .getValue().equalsIgnoreCase("true")))
0611: ch_dreq_val = true;
0612:
0613: if ((ch_dreq_val || digest != -1) && req.getStream() == null) {
0614: NVPair[] d_params;
0615: if (digest == -1) {
0616: d_params = Util.resizeArray(params, params.length + 1);
0617: digest = params.length;
0618: } else
0619: d_params = params;
0620: d_params[digest] = new NVPair("digest", calc_digest(req,
0621: extra[DI_A1], params[nonce].getValue()));
0622:
0623: if (dreq == -1) // if server requires digest, then so do we...
0624: {
0625: dreq = d_params.length;
0626: d_params = Util.resizeArray(d_params,
0627: d_params.length + 1);
0628: d_params[dreq] = new NVPair("digest-required", "true");
0629: }
0630:
0631: new_info = new AuthorizationInfo(info.getHost(), info
0632: .getPort(), info.getScheme(), info.getRealm(),
0633: d_params, extra);
0634: } else if (ch_dreq_val)
0635: new_info = null;
0636: else
0637: new_info = new AuthorizationInfo(info.getHost(), info
0638: .getPort(), info.getScheme(), info.getRealm(),
0639: params, extra);
0640:
0641: // add info for other domains, if listed
0642:
0643: boolean from_server = (challenge != null)
0644: && challenge.getHost().equalsIgnoreCase(
0645: req.getConnection().getHost());
0646: if (ch_domain != -1) {
0647: URI base = null;
0648: try {
0649: base = new URI(req.getConnection().getProtocol(), req
0650: .getConnection().getHost(), req.getConnection()
0651: .getPort(), req.getRequestURI());
0652: } catch (ParseException pe) {
0653: }
0654:
0655: StringTokenizer tok = new StringTokenizer(
0656: ch_params[ch_domain].getValue());
0657: while (tok.hasMoreTokens()) {
0658: URI Uri;
0659: try {
0660: Uri = new URI(base, tok.nextToken());
0661: } catch (ParseException pe) {
0662: continue;
0663: }
0664:
0665: AuthorizationInfo tmp = AuthorizationInfo
0666: .getAuthorization(Uri.getHost(), Uri.getPort(),
0667: info.getScheme(), info.getRealm(), req
0668: .getConnection().getContext());
0669: if (tmp == null) {
0670: params[uri] = new NVPair("uri", Uri.getPath());
0671: tmp = new AuthorizationInfo(Uri.getHost(), Uri
0672: .getPort(), info.getScheme(), info
0673: .getRealm(), params, extra);
0674: AuthorizationInfo.addAuthorization(tmp);
0675: }
0676: if (from_server)
0677: tmp.addPath(Uri.getPath());
0678: }
0679: } else if (from_server && challenge != null) {
0680: // Spec says that if no domain attribute is present then the
0681: // whole server should be considered being in the same space
0682: AuthorizationInfo tmp = AuthorizationInfo.getAuthorization(
0683: challenge.getHost(), challenge.getPort(), info
0684: .getScheme(), info.getRealm(), req
0685: .getConnection().getContext());
0686: if (tmp != null)
0687: tmp.addPath("/");
0688: }
0689:
0690: // now return the one to use
0691:
0692: return new_info;
0693: }
0694:
0695: /**
0696: * @return the fixed info is stale=true; null otherwise
0697: */
0698: private static AuthorizationInfo digest_check_stale(
0699: AuthorizationInfo challenge, RoRequest req, RoResponse resp)
0700: throws AuthSchemeNotImplException {
0701: AuthorizationInfo cred = null;
0702:
0703: NVPair[] params = challenge.getParams();
0704: for (int idx = 0; idx < params.length; idx++) {
0705: String name = params[idx].getName();
0706: if (name.equalsIgnoreCase("stale")
0707: && params[idx].getValue().equalsIgnoreCase("true")) {
0708: cred = AuthorizationInfo.getAuthorization(challenge,
0709: req, resp, false);
0710: if (cred != null) // should always be the case
0711: return digest_fixup(cred, req, challenge, resp);
0712: break; // should never be reached
0713: }
0714: }
0715:
0716: return cred;
0717: }
0718:
0719: /**
0720: * Handle nextnonce field.
0721: */
0722: private static boolean handle_nextnonce(AuthorizationInfo prev,
0723: RoRequest req, HttpHeaderElement nextnonce) {
0724: if (prev == null || nextnonce == null
0725: || nextnonce.getValue() == null)
0726: return false;
0727:
0728: AuthorizationInfo ai;
0729: try {
0730: ai = AuthorizationInfo.getAuthorization(prev, req, null,
0731: false);
0732: } catch (AuthSchemeNotImplException asnie) {
0733: ai = prev; /* shouldn't happen */
0734: }
0735: synchronized (ai) {
0736: NVPair[] params = ai.getParams();
0737: params = setValue(params, "nonce", nextnonce.getValue());
0738: params = setValue(params, "nc", "00000000");
0739: ai.setParams(params);
0740: }
0741:
0742: return true;
0743: }
0744:
0745: /**
0746: * Handle digest field of the Authentication-Info response header.
0747: */
0748: private static boolean handle_digest(AuthorizationInfo prev,
0749: Response resp, RoRequest req, String hdr_name)
0750: throws IOException {
0751: if (prev == null)
0752: return false;
0753:
0754: NVPair[] params = prev.getParams();
0755: VerifyDigest verifier = new VerifyDigest(((String[]) prev
0756: .getExtraInfo())[0], getValue(params, "nonce"), req
0757: .getMethod(), getValue(params, "uri"), hdr_name, resp);
0758:
0759: if (resp.hasEntity()) {
0760: if (DebugAuth)
0761: System.err
0762: .println("Auth: pushing md5-check-stream to verify "
0763: + "digest from " + hdr_name);
0764: resp.inp_stream = new MD5InputStream(resp.inp_stream,
0765: verifier);
0766: } else {
0767: if (DebugAuth)
0768: System.err.println("Auth: verifying digest from "
0769: + hdr_name);
0770: verifier.verifyHash(new MD5().Final(), 0);
0771: }
0772:
0773: return true;
0774: }
0775:
0776: /**
0777: * Handle rspauth field of the Authentication-Info response header.
0778: */
0779: private static boolean handle_rspauth(AuthorizationInfo prev,
0780: Response resp, RoRequest req, Vector auth_info,
0781: String hdr_name) throws IOException {
0782: if (prev == null)
0783: return false;
0784:
0785: // get the parameters we sent
0786:
0787: NVPair[] params = prev.getParams();
0788: int uri = -1, alg = -1, nonce = -1, cnonce = -1, nc = -1;
0789: for (int idx = 0; idx < params.length; idx++) {
0790: String name = params[idx].getName().toLowerCase();
0791: if (name.equals("uri"))
0792: uri = idx;
0793: else if (name.equals("algorithm"))
0794: alg = idx;
0795: else if (name.equals("nonce"))
0796: nonce = idx;
0797: else if (name.equals("cnonce"))
0798: cnonce = idx;
0799: else if (name.equals("nc"))
0800: nc = idx;
0801: }
0802:
0803: // create hash verifier to verify rspauth
0804:
0805: VerifyRspAuth verifier = new VerifyRspAuth(params[uri]
0806: .getValue(), ((String[]) prev.getExtraInfo())[0],
0807: (alg == -1 ? null : params[alg].getValue()),
0808: params[nonce].getValue(), (cnonce == -1 ? ""
0809: : params[cnonce].getValue()), (nc == -1 ? ""
0810: : params[nc].getValue()), hdr_name, resp);
0811:
0812: // if Authentication-Info in header and qop=auth then verify immediately
0813:
0814: HttpHeaderElement qop = null;
0815: if (auth_info != null
0816: && (qop = Util.getElement(auth_info, "qop")) != null
0817: && qop.getValue() != null
0818: && (qop.getValue().equalsIgnoreCase("auth") || !resp
0819: .hasEntity()
0820: && qop.getValue().equalsIgnoreCase("auth-int"))) {
0821: if (DebugAuth)
0822: System.err.println("Auth: verifying rspauth from "
0823: + hdr_name);
0824: verifier.verifyHash(new MD5().Final(), 0);
0825: } else {
0826: // else push md5 stream and verify after body
0827:
0828: if (DebugAuth)
0829: System.err
0830: .println("Auth: pushing md5-check-stream to verify "
0831: + "rspauth from " + hdr_name);
0832: resp.inp_stream = new MD5InputStream(resp.inp_stream,
0833: verifier);
0834: }
0835:
0836: return true;
0837: }
0838:
0839: /**
0840: * Calc "response" attribute for a request.
0841: */
0842: private static String calcResponseAttr(String hash, String[] extra,
0843: NVPair[] params, int alg, int uri, int qop, int nonce,
0844: int nc, int cnonce, String method) {
0845: String A1, A2, resp_val;
0846:
0847: if (alg != -1
0848: && params[alg].getValue().equalsIgnoreCase("MD5-sess"))
0849: A1 = extra[DI_A1S];
0850: else
0851: A1 = extra[DI_A1];
0852:
0853: A2 = method + ":" + params[uri].getValue();
0854: if (qop != -1
0855: && params[qop].getValue().equalsIgnoreCase("auth-int")) {
0856: A2 += ":" + hash;
0857: }
0858: A2 = new MD5(A2).asHex();
0859:
0860: if (qop == -1)
0861: resp_val = new MD5(A1 + ":" + params[nonce].getValue()
0862: + ":" + A2).asHex();
0863: else
0864: resp_val = new MD5(A1 + ":" + params[nonce].getValue()
0865: + ":" + params[nc].getValue() + ":"
0866: + params[cnonce].getValue() + ":"
0867: + params[qop].getValue() + ":" + A2).asHex();
0868:
0869: return resp_val;
0870: }
0871:
0872: /**
0873: * Calculates the digest of the request body. This was in RFC-2069
0874: * and draft-ietf-http-authentication-00.txt, but has subsequently
0875: * been removed. Here for backwards compatibility.
0876: */
0877: private static String calc_digest(RoRequest req, String A1_hash,
0878: String nonce) {
0879: if (req.getStream() != null)
0880: return "";
0881:
0882: int ct = -1, ce = -1, lm = -1, ex = -1, dt = -1;
0883: for (int idx = 0; idx < req.getHeaders().length; idx++) {
0884: String name = req.getHeaders()[idx].getName();
0885: if (name.equalsIgnoreCase("Content-type"))
0886: ct = idx;
0887: else if (name.equalsIgnoreCase("Content-Encoding"))
0888: ce = idx;
0889: else if (name.equalsIgnoreCase("Last-Modified"))
0890: lm = idx;
0891: else if (name.equalsIgnoreCase("Expires"))
0892: ex = idx;
0893: else if (name.equalsIgnoreCase("Date"))
0894: dt = idx;
0895: }
0896:
0897: NVPair[] hdrs = req.getHeaders();
0898: byte[] entity_body = (req.getData() == null ? NUL : req
0899: .getData());
0900: MD5 entity_hash = new MD5();
0901: entity_hash.Update(entity_body);
0902:
0903: String entity_info = new MD5(req.getRequestURI() + ":"
0904: + (ct == -1 ? "" : hdrs[ct].getValue()) + ":"
0905: + entity_body.length + ":"
0906: + (ce == -1 ? "" : hdrs[ce].getValue()) + ":"
0907: + (lm == -1 ? "" : hdrs[lm].getValue()) + ":"
0908: + (ex == -1 ? "" : hdrs[ex].getValue())).asHex();
0909: String entity_digest = A1_hash + ":" + nonce + ":"
0910: + req.getMethod() + ":"
0911: + (dt == -1 ? "" : hdrs[dt].getValue()) + ":"
0912: + entity_info + ":" + entity_hash.asHex();
0913:
0914: if (DebugAuth) {
0915: System.err.println("Auth: Entity-Info: '"
0916: + req.getRequestURI() + ":"
0917: + (ct == -1 ? "" : hdrs[ct].getValue()) + ":"
0918: + entity_body.length + ":"
0919: + (ce == -1 ? "" : hdrs[ce].getValue()) + ":"
0920: + (lm == -1 ? "" : hdrs[lm].getValue()) + ":"
0921: + (ex == -1 ? "" : hdrs[ex].getValue()) + "'");
0922: System.err.println("Auth: Entity-Body: '"
0923: + entity_hash.asHex() + "'");
0924: System.err.println("Auth: Entity-Digest: '"
0925: + entity_digest + "'");
0926: }
0927:
0928: return new MD5(entity_digest).asHex();
0929: }
0930:
0931: /**
0932: * Handle discard token
0933: */
0934: private static boolean handle_discard(AuthorizationInfo prev,
0935: RoRequest req, HttpHeaderElement discard) {
0936: if (discard != null && prev != null) {
0937: AuthorizationInfo.removeAuthorization(prev, req
0938: .getConnection().getContext());
0939: return true;
0940: }
0941:
0942: return false;
0943: }
0944:
0945: /**
0946: * Generate <var>num</var> bytes of random data.
0947: *
0948: * @param num the number of bytes to generate
0949: * @return a byte array of random data
0950: */
0951: private static byte[] gen_random_bytes(int num) {
0952: // first try /dev/random
0953: try {
0954: FileInputStream rnd = new FileInputStream("/dev/random");
0955: DataInputStream din = new DataInputStream(rnd);
0956: byte[] data = new byte[num];
0957: din.readFully(data);
0958: try {
0959: din.close();
0960: } catch (IOException ioe) {
0961: }
0962: return data;
0963: } catch (Throwable t) {
0964: }
0965:
0966: /* This is probably a much better generator, but it can be awfully
0967: * slow (~ 6 secs / byte on my old LX)
0968: */
0969: //return new java.security.SecureRandom().getSeed(num);
0970: /* this is faster, but needs to be done better... */
0971: byte[] data = new byte[num];
0972: try {
0973: long fm = Runtime.getRuntime().freeMemory();
0974: data[0] = (byte) (fm & 0xFF);
0975: data[1] = (byte) ((fm >> 8) & 0xFF);
0976:
0977: int h = data.hashCode();
0978: data[2] = (byte) (h & 0xFF);
0979: data[3] = (byte) ((h >> 8) & 0xFF);
0980: data[4] = (byte) ((h >> 16) & 0xFF);
0981: data[5] = (byte) ((h >> 24) & 0xFF);
0982:
0983: long time = System.currentTimeMillis();
0984: data[6] = (byte) (time & 0xFF);
0985: data[7] = (byte) ((time >> 8) & 0xFF);
0986: } catch (ArrayIndexOutOfBoundsException aioobe) {
0987: }
0988:
0989: return data;
0990: }
0991:
0992: /**
0993: * Return the value of the first NVPair whose name matches the key
0994: * using a case-insensitive search.
0995: *
0996: * @param list an array of NVPair's
0997: * @param key the key to search for
0998: * @return the value of the NVPair with that key, or null if not
0999: * found.
1000: */
1001: private final static String getValue(NVPair[] list, String key) {
1002: int len = list.length;
1003:
1004: for (int idx = 0; idx < len; idx++)
1005: if (list[idx].getName().equalsIgnoreCase(key))
1006: return list[idx].getValue();
1007:
1008: return null;
1009: }
1010:
1011: /**
1012: * Return the index of the first NVPair whose name matches the key
1013: * using a case-insensitive search.
1014: *
1015: * @param list an array of NVPair's
1016: * @param key the key to search for
1017: * @return the index of the NVPair with that key, or -1 if not
1018: * found.
1019: */
1020: private final static int getIndex(NVPair[] list, String key) {
1021: int len = list.length;
1022:
1023: for (int idx = 0; idx < len; idx++)
1024: if (list[idx].getName().equalsIgnoreCase(key))
1025: return idx;
1026:
1027: return -1;
1028: }
1029:
1030: /**
1031: * Sets the value of the NVPair with the name that matches the key
1032: * (case-insensitive). If no name matches, a new entry is created.
1033: *
1034: * @param list an array of NVPair's
1035: * @param key the name of the NVPair
1036: * @param val the value of the new NVPair
1037: * @return the (possibly) new list
1038: */
1039: private final static NVPair[] setValue(NVPair[] list, String key,
1040: String val) {
1041: int idx = getIndex(list, key);
1042: if (idx == -1) {
1043: idx = list.length;
1044: list = Util.resizeArray(list, list.length + 1);
1045: }
1046:
1047: list[idx] = new NVPair(key, val);
1048: return list;
1049: }
1050:
1051: /**
1052: * Split a list into an array of Strings, using sep as the
1053: * separator and removing whitespace around the separator.
1054: */
1055: private static String[] splitList(String str, String sep) {
1056: if (str == null)
1057: return new String[0];
1058:
1059: StringTokenizer tok = new StringTokenizer(str, sep);
1060: String[] list = new String[tok.countTokens()];
1061: for (int idx = 0; idx < list.length; idx++)
1062: list[idx] = tok.nextToken().trim();
1063:
1064: return list;
1065: }
1066:
1067: /**
1068: * Produce a string of the form "A5:22:F1:0B:53"
1069: */
1070: static String hex(byte[] buf) {
1071: StringBuffer str = new StringBuffer(buf.length * 3);
1072: for (int idx = 0; idx < buf.length; idx++) {
1073: str.append(Character.forDigit(buf[idx] >>> 4, 16));
1074: str.append(Character.forDigit(buf[idx] & 16, 16));
1075: str.append(':');
1076: }
1077: str.setLength(str.length() - 1);
1078:
1079: return str.toString();
1080: }
1081:
1082: static final byte[] unHex(String hex) {
1083: byte[] digest = new byte[hex.length() / 2];
1084:
1085: for (int idx = 0; idx < digest.length; idx++) {
1086: digest[idx] = (byte) (0xFF & Integer.parseInt(hex
1087: .substring(2 * idx, 2 * (idx + 1)), 16));
1088: }
1089:
1090: return digest;
1091: }
1092: }
1093:
1094: /**
1095: * This verifies the "rspauth" from draft-ietf-http-authentication-03
1096: */
1097: class VerifyRspAuth implements HashVerifier, GlobalConstants {
1098: private String uri;
1099: private String HA1;
1100: private String alg;
1101: private String nonce;
1102: private String cnonce;
1103: private String nc;
1104: private String hdr;
1105: private RoResponse resp;
1106:
1107: public VerifyRspAuth(String uri, String HA1, String alg,
1108: String nonce, String cnonce, String nc, String hdr,
1109: RoResponse resp) {
1110: this .uri = uri;
1111: this .HA1 = HA1;
1112: this .alg = alg;
1113: this .nonce = nonce;
1114: this .cnonce = cnonce;
1115: this .nc = nc;
1116: this .hdr = hdr;
1117: this .resp = resp;
1118: }
1119:
1120: public void verifyHash(byte[] hash, long len) throws IOException {
1121: String auth_info = resp.getHeader(hdr);
1122: if (auth_info == null)
1123: auth_info = resp.getTrailer(hdr);
1124: if (auth_info == null)
1125: return;
1126:
1127: Vector pai;
1128: try {
1129: pai = Util.parseHeader(auth_info);
1130: } catch (ParseException pe) {
1131: throw new IOException(pe.toString());
1132: }
1133:
1134: String qop;
1135: HttpHeaderElement elem = Util.getElement(pai, "qop");
1136: if (elem == null
1137: || (qop = elem.getValue()) == null
1138: || (!qop.equalsIgnoreCase("auth") && !qop
1139: .equalsIgnoreCase("auth-int")))
1140: return;
1141:
1142: elem = Util.getElement(pai, "rspauth");
1143: if (elem == null || elem.getValue() == null)
1144: return;
1145: byte[] digest = DefaultAuthHandler.unHex(elem.getValue());
1146:
1147: elem = Util.getElement(pai, "cnonce");
1148: if (elem != null && elem.getValue() != null
1149: && !elem.getValue().equals(cnonce))
1150: throw new IOException("Digest auth scheme: received wrong "
1151: + "client-nonce '" + elem.getValue()
1152: + "' - expected '" + cnonce + "'");
1153:
1154: elem = Util.getElement(pai, "nc");
1155: if (elem != null && elem.getValue() != null
1156: && !elem.getValue().equals(nc))
1157: throw new IOException("Digest auth scheme: received wrong "
1158: + "nonce-count '" + elem.getValue()
1159: + "' - expected '" + nc + "'");
1160:
1161: String A1, A2;
1162: if (alg != null && alg.equalsIgnoreCase("MD5-sess"))
1163: A1 = new MD5(HA1 + ":" + nonce + ":" + cnonce).asHex();
1164: else
1165: A1 = HA1;
1166:
1167: // draft-01 was: A2 = resp.getStatusCode() + ":" + uri;
1168: A2 = ":" + uri;
1169: if (qop.equalsIgnoreCase("auth-int"))
1170: A2 += ":" + MD5.asHex(hash);
1171: A2 = new MD5(A2).asHex();
1172:
1173: hash = new MD5(A1 + ":" + nonce + ":" + nc + ":" + cnonce + ":"
1174: + qop + ":" + A2).Final();
1175:
1176: for (int idx = 0; idx < hash.length; idx++) {
1177: if (hash[idx] != digest[idx])
1178: throw new IOException("MD5-Digest mismatch: expected "
1179: + DefaultAuthHandler.hex(digest)
1180: + " but calculated "
1181: + DefaultAuthHandler.hex(hash));
1182: }
1183:
1184: if (DebugAuth)
1185: System.err.println("Auth: rspauth from " + hdr
1186: + " successfully verified");
1187: }
1188: }
1189:
1190: /**
1191: * This verifies the "digest" from rfc-2069
1192: */
1193: class VerifyDigest implements HashVerifier, GlobalConstants {
1194: private String HA1;
1195: private String nonce;
1196: private String method;
1197: private String uri;
1198: private String hdr;
1199: private RoResponse resp;
1200:
1201: public VerifyDigest(String HA1, String nonce, String method,
1202: String uri, String hdr, RoResponse resp) {
1203: this .HA1 = HA1;
1204: this .nonce = nonce;
1205: this .method = method;
1206: this .uri = uri;
1207: this .hdr = hdr;
1208: this .resp = resp;
1209: }
1210:
1211: public void verifyHash(byte[] hash, long len) throws IOException {
1212: String auth_info = resp.getHeader(hdr);
1213: if (auth_info == null)
1214: auth_info = resp.getTrailer(hdr);
1215: if (auth_info == null)
1216: return;
1217:
1218: Vector pai;
1219: try {
1220: pai = Util.parseHeader(auth_info);
1221: } catch (ParseException pe) {
1222: throw new IOException(pe.toString());
1223: }
1224: HttpHeaderElement elem = Util.getElement(pai, "digest");
1225: if (elem == null || elem.getValue() == null)
1226: return;
1227:
1228: byte[] digest = DefaultAuthHandler.unHex(elem.getValue());
1229:
1230: String entity_info = new MD5(uri + ":"
1231: + header_val("Content-Type", resp) + ":"
1232: + header_val("Content-Length", resp) + ":"
1233: + header_val("Content-Encoding", resp) + ":"
1234: + header_val("Last-Modified", resp) + ":"
1235: + header_val("Expires", resp)).asHex();
1236: hash = new MD5(HA1 + ":" + nonce + ":" + method + ":"
1237: + header_val("Date", resp) + ":" + entity_info + ":"
1238: + MD5.asHex(hash)).Final();
1239:
1240: for (int idx = 0; idx < hash.length; idx++) {
1241: if (hash[idx] != digest[idx])
1242: throw new IOException("MD5-Digest mismatch: expected "
1243: + DefaultAuthHandler.hex(digest)
1244: + " but calculated "
1245: + DefaultAuthHandler.hex(hash));
1246: }
1247:
1248: if (DebugAuth)
1249: System.err.println("Auth: digest from " + hdr
1250: + " successfully verified");
1251: }
1252:
1253: private static final String header_val(String hdr_name,
1254: RoResponse resp) throws IOException {
1255: String hdr = resp.getHeader(hdr_name);
1256: String tlr = resp.getTrailer(hdr_name);
1257: return (hdr != null ? hdr : (tlr != null ? tlr : ""));
1258: }
1259: }
1260:
1261: /**
1262: * This class implements a simple popup that request username and password
1263: * used for the "basic" and "digest" authentication schemes.
1264: *
1265: * @version 0.3-2 18/06/1999
1266: * @author Ronald Tschalär
1267: */
1268: class BasicAuthBox extends Frame {
1269: private final static String title = "Authorization Request";
1270: private Dimension screen;
1271: private Label line1, line2, line3;
1272: private TextField user, pass;
1273: private int done;
1274: private final static int OK = 1, CANCEL = 0;
1275:
1276: /**
1277: * Constructs the popup with two lines of text above the input fields
1278: */
1279: BasicAuthBox() {
1280: super (title);
1281:
1282: screen = getToolkit().getScreenSize();
1283:
1284: addNotify();
1285: addWindowListener(new Close());
1286: setLayout(new BorderLayout());
1287:
1288: Panel p = new Panel(new GridLayout(3, 1));
1289: p.add(line1 = new Label());
1290: p.add(line2 = new Label());
1291: p.add(line3 = new Label());
1292: add("North", p);
1293:
1294: p = new Panel(new GridLayout(2, 1));
1295: p.add(new Label("Username:"));
1296: p.add(new Label("Password:"));
1297: add("West", p);
1298: p = new Panel(new GridLayout(2, 1));
1299: p.add(user = new TextField(30));
1300: p.add(pass = new TextField(30));
1301: pass.addActionListener(new Ok());
1302: pass.setEchoChar('*');
1303: add("East", p);
1304:
1305: GridBagLayout gb = new GridBagLayout();
1306: p = new Panel(gb);
1307: GridBagConstraints constr = new GridBagConstraints();
1308: Panel pp = new Panel();
1309: p.add(pp);
1310: constr.gridwidth = GridBagConstraints.REMAINDER;
1311: gb.setConstraints(pp, constr);
1312: constr.gridwidth = 1;
1313: constr.weightx = 1.0;
1314: Button b;
1315: p.add(b = new Button(" OK "));
1316: b.addActionListener(new Ok());
1317: constr.weightx = 1.0;
1318: gb.setConstraints(b, constr);
1319: p.add(b = new Button("Clear"));
1320: b.addActionListener(new Clear());
1321: constr.weightx = 2.0;
1322: gb.setConstraints(b, constr);
1323: p.add(b = new Button("Cancel"));
1324: b.addActionListener(new Cancel());
1325: constr.weightx = 1.0;
1326: gb.setConstraints(b, constr);
1327: add("South", p);
1328:
1329: pack();
1330: setResizable(false);
1331: }
1332:
1333: /**
1334: * our event handlers
1335: */
1336: private class Ok implements ActionListener {
1337: public void actionPerformed(ActionEvent ae) {
1338: done = OK;
1339: synchronized (BasicAuthBox.this ) {
1340: BasicAuthBox.this .notifyAll();
1341: }
1342: }
1343: }
1344:
1345: private class Clear implements ActionListener {
1346: public void actionPerformed(ActionEvent ae) {
1347: user.setText("");
1348: pass.setText("");
1349: user.requestFocus();
1350: }
1351: }
1352:
1353: private class Cancel implements ActionListener {
1354: public void actionPerformed(ActionEvent ae) {
1355: done = CANCEL;
1356: synchronized (BasicAuthBox.this ) {
1357: BasicAuthBox.this .notifyAll();
1358: }
1359: }
1360: }
1361:
1362: private class Close extends WindowAdapter {
1363: public void windowClosing(WindowEvent we) {
1364: new Cancel().actionPerformed(null);
1365: }
1366: }
1367:
1368: /**
1369: * the method called by DefaultAuthHandler.
1370: *
1371: * @return the username/password pair
1372: */
1373: synchronized NVPair getInput(String l1, String l2, String l3) {
1374: line1.setText(l1);
1375: line2.setText(l2);
1376: line3.setText(l3);
1377:
1378: line1.invalidate();
1379: line2.invalidate();
1380: line3.invalidate();
1381:
1382: setResizable(true);
1383: pack();
1384: setResizable(false);
1385: setLocation(
1386: (screen.width - getPreferredSize().width) / 2,
1387: (int) ((screen.height - getPreferredSize().height) / 2 * .7));
1388: user.requestFocus();
1389: setVisible(true);
1390:
1391: try {
1392: wait();
1393: } catch (InterruptedException e) {
1394: }
1395:
1396: setVisible(false);
1397:
1398: NVPair result = new NVPair(user.getText(), pass.getText());
1399: user.setText("");
1400: pass.setText("");
1401:
1402: if (done == CANCEL)
1403: return null;
1404: else
1405: return result;
1406: }
1407: }
|