/**
 * Copyright 2010 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.eclipse.webdav.internal.authentication;

import java.util.Hashtable;
import org.eclipse.webdav.client.Policy;

/**
 * Parses the HTTP "WWW-Authenticate" and "Proxy-Authenticate" header
 * fields. These header fields have the following form:
 *
 * <code>
 * challenge	= auth-scheme 1*SP 1#auth-param
 * auth-scheme	= token
 * auth-param	= token "=" ( token | quoted-string )
 * </code>
 *
 * The authentication parameter realm is defined for all authentication
 * schemes:
 *
 * <code>
 * realm		= "realm" "=" realm-value
 * realm-value	= quoted-string
 * </code>
 *
 * A challenge may look like this:
 *
 * <code>
 * WWW-Authenticate: Digest
 * realm="testrealm@host.com",
 * qop="auth,auth-int",
 * nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
 * opaque="5ccc069c403ebaf9f0171e9517f40e41"
 * </code>
 */
public class AuthenticateChallenge {
	private String challenge;
	private Parser parser;

	private String authScheme;
	private Hashtable authParams;

	/**
	 * Parses the given authenticate challenge.
	 *
	 * @param challenge
	 * @throws ParserException if the challenge is malformed
	 */
	public AuthenticateChallenge(String challenge) throws ParserException {
		this.challenge = challenge;
		this.parser = new Parser(challenge);
		parse();
	}

	/**
	 * Returns the value of the given authentication parameter, or
	 * <code>null</code> if the param does not exist.
	 */
	public String get(String param) {
		return (String) authParams.get(param);
	}

	/**
	 * Returns the authentication scheme. For example, "Basic" or "Digest".
	 */
	public String getAuthScheme() {
		return authScheme;
	}

	/**
	 * Returns the authenticate challenge, unparsed.
	 */
	public String getChallenge() {
		return challenge;
	}

	/**
	 * Returns the realm authentication parameter, which must exist.
	 */
	public String getRealm() {
		return get("realm"); //$NON-NLS-1$
	}

	private void parse() throws ParserException {
		authParams = new Hashtable(5);
		authScheme = parser.nextToken();
		parser.skipWhiteSpace();

		boolean done = false;
		while (!done) {
			String param = parser.nextToken();
			parser.match('=');
			String value = null;
			parser.checkPosition();
			if (challenge.charAt(parser.pos) == '"') { //$NON-NLS-1$
				value = parser.nextQuotedString();
			} else {
				value = parser.nextToken();
			}
			authParams.put(param, value);
			parser.skipWhiteSpace();
			done = parser.pos == challenge.length();
			if (!done) {
				parser.match(',');
				parser.skipWhiteSpace();
			}
		}

		if (getRealm() == null) {
			throw new ParserException(Policy.bind("exception.missingRealm")); //$NON-NLS-1$
		}
	}
}
