001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.impl.auth;
028
029import java.util.Locale;
030
031import org.apache.http.FormattedHeader;
032import org.apache.http.Header;
033import org.apache.http.HttpRequest;
034import org.apache.http.auth.AUTH;
035import org.apache.http.auth.AuthenticationException;
036import org.apache.http.auth.ChallengeState;
037import org.apache.http.auth.ContextAwareAuthScheme;
038import org.apache.http.auth.Credentials;
039import org.apache.http.auth.MalformedChallengeException;
040import org.apache.http.protocol.HTTP;
041import org.apache.http.protocol.HttpContext;
042import org.apache.http.util.Args;
043import org.apache.http.util.CharArrayBuffer;
044
045/**
046 * Abstract authentication scheme class that serves as a basis
047 * for all authentication schemes supported by HttpClient. This class
048 * defines the generic way of parsing an authentication challenge. It
049 * does not make any assumptions regarding the format of the challenge
050 * nor does it impose any specific way of responding to that challenge.
051 *
052 *
053 * @since 4.0
054 */
055public abstract class AuthSchemeBase implements ContextAwareAuthScheme {
056
057    protected ChallengeState challengeState;
058
059    /**
060     * Creates an instance of {@code AuthSchemeBase} with the given challenge
061     * state.
062     *
063     * @since 4.2
064     *
065     * @deprecated (4.3) do not use.
066     */
067    @Deprecated
068    public AuthSchemeBase(final ChallengeState challengeState) {
069        super();
070        this.challengeState = challengeState;
071    }
072
073    public AuthSchemeBase() {
074        super();
075    }
076
077    /**
078     * Processes the given challenge token. Some authentication schemes
079     * may involve multiple challenge-response exchanges. Such schemes must be able
080     * to maintain the state information when dealing with sequential challenges
081     *
082     * @param header the challenge header
083     *
084     * @throws MalformedChallengeException is thrown if the authentication challenge
085     * is malformed
086     */
087    @Override
088    public void processChallenge(final Header header) throws MalformedChallengeException {
089        Args.notNull(header, "Header");
090        final String authheader = header.getName();
091        if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) {
092            this.challengeState = ChallengeState.TARGET;
093        } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) {
094            this.challengeState = ChallengeState.PROXY;
095        } else {
096            throw new MalformedChallengeException("Unexpected header name: " + authheader);
097        }
098
099        final CharArrayBuffer buffer;
100        int pos;
101        if (header instanceof FormattedHeader) {
102            buffer = ((FormattedHeader) header).getBuffer();
103            pos = ((FormattedHeader) header).getValuePos();
104        } else {
105            final String s = header.getValue();
106            if (s == null) {
107                throw new MalformedChallengeException("Header value is null");
108            }
109            buffer = new CharArrayBuffer(s.length());
110            buffer.append(s);
111            pos = 0;
112        }
113        while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
114            pos++;
115        }
116        final int beginIndex = pos;
117        while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
118            pos++;
119        }
120        final int endIndex = pos;
121        final String s = buffer.substring(beginIndex, endIndex);
122        if (!s.equalsIgnoreCase(getSchemeName())) {
123            throw new MalformedChallengeException("Invalid scheme identifier: " + s);
124        }
125
126        parseChallenge(buffer, pos, buffer.length());
127    }
128
129
130    @Override
131    @SuppressWarnings("deprecation")
132    public Header authenticate(
133            final Credentials credentials,
134            final HttpRequest request,
135            final HttpContext context) throws AuthenticationException {
136        return authenticate(credentials, request);
137    }
138
139    protected abstract void parseChallenge(
140            CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException;
141
142    /**
143     * Returns {@code true} if authenticating against a proxy, {@code false}
144     * otherwise.
145     */
146    public boolean isProxy() {
147        return this.challengeState != null && this.challengeState == ChallengeState.PROXY;
148    }
149
150    /**
151     * Returns {@link ChallengeState} value or {@code null} if unchallenged.
152     *
153     * @since 4.2
154     */
155    public ChallengeState getChallengeState() {
156        return this.challengeState;
157    }
158
159    @Override
160    public String toString() {
161        final String name = getSchemeName();
162        if (name != null) {
163            return name.toUpperCase(Locale.ROOT);
164        } else {
165            return super.toString();
166        }
167    }
168
169}