1b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/*
2b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Copyright 2007, 2008 Netflix, Inc.
3b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
4b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Licensed under the Apache License, Version 2.0 (the "License");
5b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * you may not use this file except in compliance with the License.
6b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * You may obtain a copy of the License at
7b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
8b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *     http://www.apache.org/licenses/LICENSE-2.0
9b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
10b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Unless required by applicable law or agreed to in writing, software
11b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * distributed under the License is distributed on an "AS IS" BASIS,
12b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * See the License for the specific language governing permissions and
14b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * limitations under the License.
15b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */
16b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
17b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpackage net.oauth;
18b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
19b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.IOException;
20b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.InputStream;
21b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.InputStreamReader;
22b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.Reader;
23b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URISyntaxException;
24b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.ArrayList;
25b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Collection;
26b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Collections;
27b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.HashMap;
28b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.List;
29b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Map;
30b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Set;
31b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.regex.Matcher;
32b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.regex.Pattern;
33b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.http.HttpMessage;
34b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.signature.OAuthSignatureMethod;
35b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
36b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/**
37b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * A request or response message used in the OAuth protocol.
38b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * <p>
39b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * The parameters in this class are not percent-encoded. Methods like
40b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * OAuthClient.invoke and OAuthResponseMessage.completeParameters are
41b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * responsible for percent-encoding parameters before transmission and decoding
42b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * them after reception.
43b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
44b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @author John Kristian
45b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @hide
46b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */
47b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpublic class OAuthMessage {
48b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
49b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public OAuthMessage(String method, String URL,
50b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Collection<? extends Map.Entry> parameters) {
51b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.method = method;
52b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.URL = URL;
53b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parameters == null) {
54b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            this.parameters = new ArrayList<Map.Entry<String, String>>();
55b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        } else {
56b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            this.parameters = new ArrayList<Map.Entry<String, String>>(parameters.size());
57b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            for (Map.Entry p : parameters) {
58b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                this.parameters.add(new OAuth.Parameter(
59b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        toString(p.getKey()), toString(p.getValue())));
60b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
61b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
62b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
63b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
64b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String method;
65b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String URL;
66b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
67b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private final List<Map.Entry<String, String>> parameters;
68b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private Map<String, String> parameterMap;
69b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private boolean parametersAreComplete = false;
70b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
71b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
72b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String toString() {
73b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return "OAuthMessage(" + method + ", " + URL + ", " + parameters + ")";
74b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
75b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
76b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** A caller is about to get a parameter. */
77b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private void beforeGetParameter() throws IOException {
78b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (!parametersAreComplete) {
79b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            completeParameters();
80b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parametersAreComplete = true;
81b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
82b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
83b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
84b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
85b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Finish adding parameters; for example read an HTTP response body and
86b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * parse parameters from it.
87b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
88b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected void completeParameters() throws IOException {
89b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
90b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
91b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public List<Map.Entry<String, String>> getParameters() throws IOException {
92b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        beforeGetParameter();
93b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return Collections.unmodifiableList(parameters);
94b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
95b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
96b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void addParameter(String key, String value) {
97b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        addParameter(new OAuth.Parameter(key, value));
98b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
99b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
100b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void addParameter(Map.Entry<String, String> parameter) {
101b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        parameters.add(parameter);
102b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        parameterMap = null;
103b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
104b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
105b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void addParameters(
106b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Collection<? extends Map.Entry<String, String>> parameters) {
107b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.parameters.addAll(parameters);
108b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        parameterMap = null;
109b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
110b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
111b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getParameter(String name) throws IOException {
112b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getParameterMap().get(name);
113b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
114b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
115b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getConsumerKey() throws IOException {
116b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getParameter(OAuth.OAUTH_CONSUMER_KEY);
117b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
118b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
119b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getToken() throws IOException {
120b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getParameter(OAuth.OAUTH_TOKEN);
121b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
122b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
123b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getSignatureMethod() throws IOException {
124b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getParameter(OAuth.OAUTH_SIGNATURE_METHOD);
125b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
126b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
127b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getSignature() throws IOException {
128b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getParameter(OAuth.OAUTH_SIGNATURE);
129b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
130b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
131b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected Map<String, String> getParameterMap() throws IOException {
132b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        beforeGetParameter();
133b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parameterMap == null) {
134b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parameterMap = OAuth.newMap(parameters);
135b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
136b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return parameterMap;
137b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
138b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
139b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
140b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * The MIME type of the body of this message.
141b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
142b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return the MIME type, or null to indicate the type is unknown.
143b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
144b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getBodyType() {
145b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return getHeader(HttpMessage.CONTENT_TYPE);
146b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
147b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
148b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
149b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * The character encoding of the body of this message.
150b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
151b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return the name of an encoding, or "ISO-8859-1" if no charset has been
152b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *         specified.
153b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
154b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getBodyEncoding() {
155b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return HttpMessage.DEFAULT_CHARSET;
156b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
157b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
158b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
159b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * The value of the last HTTP header with the given name. The name is case
160b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * insensitive.
161b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
162b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return the value of the last header, or null to indicate that there is
163b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *         no such header in this message.
164b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
165b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public final String getHeader(String name) {
166b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String value = null; // no such header
167b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        for (Map.Entry<String, String> header : getHeaders()) {
168b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (name.equalsIgnoreCase(header.getKey())) {
169b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                value = header.getValue();
170b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
171b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
172b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return value;
173b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
174b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
175b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** All HTTP headers.  You can add headers to this list. */
176b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public final List<Map.Entry<String, String>> getHeaders() {
177b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return headers;
178b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
179b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
180b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
181b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Read the body of the HTTP request or response and convert it to a String.
182b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * This method isn't repeatable, since it consumes and closes getBodyAsStream.
183b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
184b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return the body, or null to indicate there is no body.
185b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
186b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public final String readBodyAsString() throws IOException
187b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    {
188b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        InputStream body = getBodyAsStream();
189b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return readAll(body, getBodyEncoding());
190b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
191b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
192b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
193b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Get a stream from which to read the body of the HTTP request or response.
194b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * This is designed to support efficient streaming of a large message.
195b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * The caller must close the returned stream, to release the underlying
196b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * resources such as the TCP connection for an HTTP response.
197b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
198b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return a stream from which to read the body, or null to indicate there
199b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *         is no body.
200b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
201b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public InputStream getBodyAsStream() throws IOException {
202b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return null;
203b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
204b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
205b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Construct a verbose description of this message and its origins. */
206b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public Map<String, Object> getDump() throws IOException {
207b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        Map<String, Object> into = new HashMap<String, Object>();
208b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        dump(into);
209b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return into;
210b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
211b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
212b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected void dump(Map<String, Object> into) throws IOException {
213b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        into.put("URL", URL);
214b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parametersAreComplete) {
215b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            try {
216b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                into.putAll(getParameterMap());
217b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            } catch (Exception ignored) {
218b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
219b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
220b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
221b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
222b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
223b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Verify that the required parameter names are contained in the actual
224b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * collection.
225b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
226b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws OAuthProblemException
227b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *                 one or more parameters are absent.
228b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws IOException
229b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
230b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void requireParameters(String... names)
231b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws OAuthProblemException, IOException {
232b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        Set<String> present = getParameterMap().keySet();
233b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        List<String> absent = new ArrayList<String>();
234b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        for (String required : names) {
235b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (!present.contains(required)) {
236b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                absent.add(required);
237b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
238b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
239b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (!absent.isEmpty()) {
240b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_ABSENT);
241b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_ABSENT, OAuth.percentEncode(absent));
242b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throw problem;
243b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
244b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
245b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
246b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
247b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Add some of the parameters needed to request access to a protected
248b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * resource, if they aren't already in the message.
249b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
250b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws IOException
251b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws URISyntaxException
252b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
253b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void addRequiredParameters(OAuthAccessor accessor)
254b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws OAuthException, IOException, URISyntaxException {
255b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final Map<String, String> pMap = OAuth.newMap(parameters);
256b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (pMap.get(OAuth.OAUTH_TOKEN) == null && accessor.accessToken != null) {
257b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            addParameter(OAuth.OAUTH_TOKEN, accessor.accessToken);
258b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
259b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final OAuthConsumer consumer = accessor.consumer;
260b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (pMap.get(OAuth.OAUTH_CONSUMER_KEY) == null) {
261b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            addParameter(OAuth.OAUTH_CONSUMER_KEY, consumer.consumerKey);
262b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
263b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String signatureMethod = pMap.get(OAuth.OAUTH_SIGNATURE_METHOD);
264b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (signatureMethod == null) {
265b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            signatureMethod = (String) consumer.getProperty(OAuth.OAUTH_SIGNATURE_METHOD);
266b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (signatureMethod == null) {
267b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                signatureMethod = OAuth.HMAC_SHA1;
268b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
269b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            addParameter(OAuth.OAUTH_SIGNATURE_METHOD, signatureMethod);
270b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
271b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (pMap.get(OAuth.OAUTH_TIMESTAMP) == null) {
272b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            addParameter(OAuth.OAUTH_TIMESTAMP, (System.currentTimeMillis() / 1000) + "");
273b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
274b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (pMap.get(OAuth.OAUTH_NONCE) == null) {
275b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            addParameter(OAuth.OAUTH_NONCE, System.nanoTime() + "");
276b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
277b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (pMap.get(OAuth.OAUTH_VERSION) == null) {
278b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        	addParameter(OAuth.OAUTH_VERSION, OAuth.VERSION_1_0);
279b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
280b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.sign(accessor);
281b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
282b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
283b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
284b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Add a signature to the message.
285b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
286b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws URISyntaxException
287b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
288b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void sign(OAuthAccessor accessor) throws IOException,
289b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthException, URISyntaxException {
290b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        OAuthSignatureMethod.newSigner(this, accessor).sign(this);
291b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
292b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
293b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
294b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Check that the message is valid.
295b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
296b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws IOException
297b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws URISyntaxException
298b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
299b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws OAuthProblemException
300b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *                 the message is invalid
301b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
302b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void validateMessage(OAuthAccessor accessor, OAuthValidator validator)
303b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws OAuthException, IOException, URISyntaxException {
304b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        validator.validateMessage(this, accessor);
305b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
306b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
307b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
308b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Construct a WWW-Authenticate or Authentication header value, containing
309b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * the given realm plus all the parameters whose names begin with "oauth_".
310b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
311b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getAuthorizationHeader(String realm) throws IOException {
312b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        StringBuilder into = new StringBuilder();
313b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (realm != null) {
314b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            into.append(" realm=\"").append(OAuth.percentEncode(realm)).append('"');
315b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
316b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        beforeGetParameter();
317b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parameters != null) {
318b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            for (Map.Entry parameter : parameters) {
319b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                String name = toString(parameter.getKey());
320b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                if (name.startsWith("oauth_")) {
321b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    if (into.length() > 0) into.append(",");
322b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    into.append(" ");
323b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    into.append(OAuth.percentEncode(name)).append("=\"");
324b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    into.append(OAuth.percentEncode(toString(parameter.getValue()))).append('"');
325b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                }
326b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
327b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
328b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return AUTH_SCHEME + into.toString();
329b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
330b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
331b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
332b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Read all the data from the given stream, and close it.
333b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
334b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @return null if from is null, or the data from the stream converted to a
335b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *         String
336b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
337b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static String readAll(InputStream from, String encoding) throws IOException
338b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    {
339b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (from == null) {
340b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return null;
341b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
342b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        try {
343b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            StringBuilder into = new StringBuilder();
344b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Reader r = new InputStreamReader(from, encoding);
345b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            char[] s = new char[512];
346b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            for (int n; 0 < (n = r.read(s));) {
347b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                into.append(s, 0, n);
348b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
349b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return into.toString();
350b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        } finally {
351b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            from.close();
352b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
353b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
354b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
355b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
356b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Parse the parameters from an OAuth Authorization or WWW-Authenticate
357b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * header. The realm is included as a parameter. If the given header doesn't
358b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * start with "OAuth ", return an empty list.
359b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
360b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static List<OAuth.Parameter> decodeAuthorization(String authorization) {
361b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        List<OAuth.Parameter> into = new ArrayList<OAuth.Parameter>();
362b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (authorization != null) {
363b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Matcher m = AUTHORIZATION.matcher(authorization);
364b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (m.matches()) {
365b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) {
366b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    for (String nvp : m.group(2).split("\\s*,\\s*")) {
367b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        m = NVP.matcher(nvp);
368b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        if (m.matches()) {
369b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                            String name = OAuth.decodePercent(m.group(1));
370b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                            String value = OAuth.decodePercent(m.group(2));
371b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                            into.add(new OAuth.Parameter(name, value));
372b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        }
373b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    }
374b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                }
375b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
376b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
377b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return into;
378b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
379b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
380b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String AUTH_SCHEME = "OAuth";
381b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
382b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String GET = "GET";
383b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String POST = "POST";
384b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String PUT = "PUT";
385b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String DELETE = "DELETE";
386b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
387b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static final Pattern AUTHORIZATION = Pattern.compile("\\s*(\\w*)\\s+(.*)");
388b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static final Pattern NVP = Pattern.compile("(\\S*)\\s*\\=\\s*\"([^\"]*)\"");
389b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
390b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static final String toString(Object from) {
391b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return (from == null) ? null : from.toString();
392b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
393b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
394b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien}
395