1b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/*
2b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Copyright 2007 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.signature;
18b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
19b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.IOException;
20b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URI;
21b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URISyntaxException;
22b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.ArrayList;
23b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Collection;
24b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Collections;
25b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.List;
26b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Map;
27b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.concurrent.ConcurrentHashMap;
28b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuth;
29b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuthAccessor;
30b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuthConsumer;
31b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuthException;
32b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuthMessage;
33b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.OAuthProblemException;
3464b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor// BEGIN android-changed
3564b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor// import org.apache.commons.codec.binary.Base64;
36c76276a1d6165cfb502b06f65b1735fef0754d26Doug Zongkerimport android.util.Base64;
3764b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor// END android-changed
38b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
39b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/**
40b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * A pair of algorithms for computing and verifying an OAuth digital signature.
41b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
42b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @author John Kristian
43b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @hide
44b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */
45b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpublic abstract class OAuthSignatureMethod {
46b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
47b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Add a signature to the message.
48b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws URISyntaxException
49b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws IOException */
50b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void sign(OAuthMessage message)
51b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    throws OAuthException, IOException, URISyntaxException {
52b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        message.addParameter(new OAuth.Parameter("oauth_signature",
53b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                getSignature(message)));
54b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
55b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
56b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
57b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Check whether the message has a valid signature.
58b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws URISyntaxException
59b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *
60b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * @throws OAuthProblemException
61b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     *             the signature is invalid
62b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
63b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void validate(OAuthMessage message)
64b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    throws IOException, OAuthException, URISyntaxException {
65b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        message.requireParameters("oauth_signature");
66b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String signature = message.getSignature();
67b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String baseString = getBaseString(message);
68b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (!isValid(signature, baseString)) {
69b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthProblemException problem = new OAuthProblemException(
70b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    "signature_invalid");
71b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            problem.setParameter("oauth_signature", signature);
72b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            problem.setParameter("oauth_signature_base_string", baseString);
73b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            problem.setParameter("oauth_signature_method", message
74b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    .getSignatureMethod());
75b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throw problem;
76b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
77b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
78b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
79b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected String getSignature(OAuthMessage message)
80b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    throws OAuthException, IOException, URISyntaxException {
81b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String baseString = getBaseString(message);
82b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String signature = getSignature(baseString);
83b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        // Logger log = Logger.getLogger(getClass().getName());
84b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        // if (log.isLoggable(Level.FINE)) {
85b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        // log.fine(signature + "=getSignature(" + baseString + ")");
86b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        // }
87b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return signature;
88b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
89b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
90b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected void initialize(String name, OAuthAccessor accessor)
91b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws OAuthException {
92b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String secret = accessor.consumer.consumerSecret;
93b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (name.endsWith(_ACCESSOR)) {
94b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // This code supports the 'Accessor Secret' extensions
95b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // described in http://oauth.pbwiki.com/AccessorSecret
96b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            final String key = OAuthConsumer.ACCESSOR_SECRET;
97b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Object accessorSecret = accessor.getProperty(key);
98b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (accessorSecret == null) {
99b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                accessorSecret = accessor.consumer.getProperty(key);
100b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
101b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (accessorSecret != null) {
102b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                secret = accessorSecret.toString();
103b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
104b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
105b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (secret == null) {
106b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            secret = "";
107b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
108b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        setConsumerSecret(secret);
109b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
110b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
111b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static final String _ACCESSOR = "-Accessor";
112b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
113b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Compute the signature for the given base string. */
114b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected abstract String getSignature(String baseString) throws OAuthException;
115b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
116b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Decide whether the signature is valid. */
117b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected abstract boolean isValid(String signature, String baseString)
118b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws OAuthException;
119b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
120b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private String consumerSecret;
121b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
122b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private String tokenSecret;
123b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
124b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected String getConsumerSecret() {
125b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return consumerSecret;
126b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
127b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
128b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected void setConsumerSecret(String consumerSecret) {
129b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.consumerSecret = consumerSecret;
130b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
131b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
132b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public String getTokenSecret() {
133b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return tokenSecret;
134b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
135b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
136b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public void setTokenSecret(String tokenSecret) {
137b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        this.tokenSecret = tokenSecret;
138b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
139b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
140b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static String getBaseString(OAuthMessage message)
141b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throws IOException, URISyntaxException {
142b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        List<Map.Entry<String, String>> parameters;
143b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String url = message.URL;
144b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        int q = url.indexOf('?');
145b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (q < 0) {
146b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parameters = message.getParameters();
147b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        } else {
148b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // Combine the URL query string with the other parameters:
149b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parameters = new ArrayList<Map.Entry<String, String>>();
150b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parameters.addAll(OAuth.decodeForm(message.URL.substring(q + 1)));
151b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            parameters.addAll(message.getParameters());
152b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            url = url.substring(0, q);
153b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
154b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return OAuth.percentEncode(message.method.toUpperCase()) + '&'
155b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                + OAuth.percentEncode(normalizeUrl(url)) + '&'
156b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                + OAuth.percentEncode(normalizeParameters(parameters));
157b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
158b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
159b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected static String normalizeUrl(String url) throws URISyntaxException {
160b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        URI uri = new URI(url);
161b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String scheme = uri.getScheme().toLowerCase();
162b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String authority = uri.getAuthority().toLowerCase();
163b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        boolean dropPort = (scheme.equals("http") && uri.getPort() == 80)
164b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                           || (scheme.equals("https") && uri.getPort() == 443);
165b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (dropPort) {
166b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // find the last : in the authority
167b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            int index = authority.lastIndexOf(":");
168b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (index >= 0) {
169b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                authority = authority.substring(0, index);
170b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
171b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
172b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String path = uri.getRawPath();
173b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (path == null || path.length() <= 0) {
174b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            path = "/"; // conforms to RFC 2616 section 3.2.2
175b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
176b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        // we know that there is no query and no fragment here.
177b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return scheme + "://" + authority + path;
178b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
179b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
180b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    protected static String normalizeParameters(
181b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Collection<? extends Map.Entry> parameters) throws IOException {
182b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parameters == null) {
183b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return "";
184b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
185b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        List<ComparableParameter> p = new ArrayList<ComparableParameter>(
186b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                parameters.size());
187b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        for (Map.Entry parameter : parameters) {
188b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (!"oauth_signature".equals(parameter.getKey())) {
189b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                p.add(new ComparableParameter(parameter));
190b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
191b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
192b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        Collections.sort(p);
193b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return OAuth.formEncode(getParameters(p));
194b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
195b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
19664b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor    // BEGIN android-changed
197b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static byte[] decodeBase64(String s) {
19864b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor        return Base64.decode(s, Base64.DEFAULT);
199b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
200b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
201b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static String base64Encode(byte[] b) {
20264b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor        return Base64.encodeToString(b, Base64.DEFAULT);
203b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
20464b058597da6260646ba8dc68fdcfb3982b16bffDan Egnor    // END android-changed
205b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
206b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static OAuthSignatureMethod newSigner(OAuthMessage message,
207b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthAccessor accessor) throws IOException, OAuthException {
208b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        message.requireParameters(OAuth.OAUTH_SIGNATURE_METHOD);
209b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        OAuthSignatureMethod signer = newMethod(message.getSignatureMethod(),
210b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                accessor);
211b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        signer.setTokenSecret(accessor.tokenSecret);
212b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return signer;
213b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
214b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
215b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** The factory for signature methods. */
216b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static OAuthSignatureMethod newMethod(String name,
217b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthAccessor accessor) throws OAuthException {
218b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        try {
219b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Class methodClass = NAME_TO_CLASS.get(name);
220b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (methodClass != null) {
221b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                OAuthSignatureMethod method = (OAuthSignatureMethod) methodClass
222b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                .newInstance();
223b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                method.initialize(name, accessor);
224b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                return method;
225b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
226b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            OAuthProblemException problem = new OAuthProblemException(
227b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            "signature_method_rejected");
228b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            String acceptable = OAuth.percentEncode(NAME_TO_CLASS.keySet());
229b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (acceptable.length() > 0) {
230b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                problem.setParameter("oauth_acceptable_signature_methods",
231b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        acceptable.toString());
232b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
233b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throw problem;
234b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        } catch (InstantiationException e) {
235b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throw new OAuthException(e);
236b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        } catch (IllegalAccessException e) {
237b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            throw new OAuthException(e);
238b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
239b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
240b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
241b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /**
242b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * Subsequently, newMethod(name) will attempt to instantiate the given
243b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     * class, with no constructor parameters.
244b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien     */
245b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public static void registerMethodClass(String name, Class clazz) {
246b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        NAME_TO_CLASS.put(name, clazz);
247b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
248b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
249b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static final Map<String, Class> NAME_TO_CLASS = new ConcurrentHashMap<String, Class>();
250b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    static {
251b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        registerMethodClass("HMAC-SHA1", HMAC_SHA1.class);
252b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        registerMethodClass("PLAINTEXT", PLAINTEXT.class);
253b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        registerMethodClass("RSA-SHA1", RSA_SHA1.class);
254b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        registerMethodClass("HMAC-SHA1" + _ACCESSOR, HMAC_SHA1.class);
255b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        registerMethodClass("PLAINTEXT" + _ACCESSOR, PLAINTEXT.class);
256b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
257b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
258b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** An efficiently sortable wrapper around a parameter. */
259b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static class ComparableParameter implements
260b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Comparable<ComparableParameter> {
261b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
262b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        ComparableParameter(Map.Entry value) {
263b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            this.value = value;
264b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            String n = toString(value.getKey());
265b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            String v = toString(value.getValue());
266b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            this.key = OAuth.percentEncode(n) + ' ' + OAuth.percentEncode(v);
267b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // ' ' is used because it comes before any character
268b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            // that can appear in a percentEncoded string.
269b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
270b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
271b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final Map.Entry value;
272b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
273b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        private final String key;
274b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
275b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        private static String toString(Object from) {
276b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return (from == null) ? null : from.toString();
277b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
278b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
279b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        public int compareTo(ComparableParameter that) {
280b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return this.key.compareTo(that.key);
281b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
282b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
283b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        @Override
284b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        public String toString() {
285b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return key;
286b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
287b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
288b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
289b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
290b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Retrieve the original parameters from a sorted collection. */
291b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static List<Map.Entry> getParameters(
292b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            Collection<ComparableParameter> parameters) {
293b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (parameters == null) {
294b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            return null;
295b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
296b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        List<Map.Entry> list = new ArrayList<Map.Entry>(parameters.size());
297b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        for (ComparableParameter parameter : parameters) {
298b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            list.add(parameter.value);
299b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
300b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return list;
301b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
302b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
303b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien}
304