blob: aed4c5e539efd0dda0ebf079d0149835770b77c1 [file] [log] [blame]
/*
* Copyright 2007 Netflix, 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 net.oauth;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author John Kristian
* @hide
*/
public class OAuth {
public static final String VERSION_1_0 = "1.0";
/** The encoding used to represent characters as bytes. */
public static final String ENCODING = "UTF-8";
/** The MIME type for a sequence of OAuth parameters. */
public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
public static final String OAUTH_TOKEN = "oauth_token";
public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
public static final String OAUTH_SIGNATURE = "oauth_signature";
public static final String OAUTH_TIMESTAMP = "oauth_timestamp";
public static final String OAUTH_NONCE = "oauth_nonce";
public static final String OAUTH_VERSION = "oauth_version";
public static final String HMAC_SHA1 = "HMAC-SHA1";
public static final String RSA_SHA1 = "RSA-SHA1";
public static class Problems {
public static final String TOKEN_NOT_AUTHORIZED = "token_not_authorized";
public static final String INVALID_USED_NONCE = "invalid_used_nonce";
public static final String SIGNATURE_INVALID = "signature_invalid";
public static final String INVALID_EXPIRED_TOKEN = "invalid_expired_token";
public static final String INVALID_CONSUMER_KEY = "invalid_consumer_key";
public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused";
public static final String TIMESTAMP_REFUSED = "timestamp_refused";
public static final String PARAMETER_REJECTED = "parameter_rejected";
public static final String PARAMETER_ABSENT = "parameter_absent";
public static final String VERSION_REJECTED = "version_rejected";
public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected";
public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent";
public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected";
public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps";
public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions";
}
/** Return true if the given Content-Type header means FORM_ENCODED. */
public static boolean isFormEncoded(String contentType) {
if (contentType == null) {
return false;
}
int semi = contentType.indexOf(";");
if (semi >= 0) {
contentType = contentType.substring(0, semi);
}
return FORM_ENCODED.equalsIgnoreCase(contentType.trim());
}
/**
* Construct a form-urlencoded document containing the given sequence of
* name/value pairs. Use OAuth percent encoding (not exactly the encoding
* mandated by HTTP).
*/
public static String formEncode(Iterable<? extends Map.Entry> parameters)
throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
formEncode(parameters, b);
return new String(b.toByteArray());
}
/**
* Write a form-urlencoded document into the given stream, containing the
* given sequence of name/value pairs.
*/
public static void formEncode(Iterable<? extends Map.Entry> parameters,
OutputStream into) throws IOException {
if (parameters != null) {
boolean first = true;
for (Map.Entry parameter : parameters) {
if (first) {
first = false;
} else {
into.write('&');
}
into.write(percentEncode(toString(parameter.getKey()))
.getBytes());
into.write('=');
into.write(percentEncode(toString(parameter.getValue()))
.getBytes());
}
}
}
/** Parse a form-urlencoded document. */
public static List<Parameter> decodeForm(String form) {
List<Parameter> list = new ArrayList<Parameter>();
if (!isEmpty(form)) {
for (String nvp : form.split("\\&")) {
int equals = nvp.indexOf('=');
String name;
String value;
if (equals < 0) {
name = decodePercent(nvp);
value = null;
} else {
name = decodePercent(nvp.substring(0, equals));
value = decodePercent(nvp.substring(equals + 1));
}
list.add(new Parameter(name, value));
}
}
return list;
}
/** Construct a &-separated list of the given values, percentEncoded. */
public static String percentEncode(Iterable values) {
StringBuilder p = new StringBuilder();
for (Object v : values) {
if (p.length() > 0) {
p.append("&");
}
p.append(OAuth.percentEncode(toString(v)));
}
return p.toString();
}
public static String percentEncode(String s) {
if (s == null) {
return "";
}
try {
return URLEncoder.encode(s, ENCODING)
// OAuth encodes some characters differently:
.replace("+", "%20").replace("*", "%2A")
.replace("%7E", "~");
// This could be done faster with more hand-crafted code.
} catch (UnsupportedEncodingException wow) {
throw new RuntimeException(wow.getMessage(), wow);
}
}
public static String decodePercent(String s) {
try {
return URLDecoder.decode(s, ENCODING);
// This implements http://oauth.pbwiki.com/FlexibleDecoding
} catch (java.io.UnsupportedEncodingException wow) {
throw new RuntimeException(wow.getMessage(), wow);
}
}
/**
* Construct a Map containing a copy of the given parameters. If several
* parameters have the same name, the Map will contain the first value,
* only.
*/
public static Map<String, String> newMap(Iterable<? extends Map.Entry> from) {
Map<String, String> map = new HashMap<String, String>();
if (from != null) {
for (Map.Entry f : from) {
String key = toString(f.getKey());
if (!map.containsKey(key)) {
map.put(key, toString(f.getValue()));
}
}
}
return map;
}
/** Construct a list of Parameters from name, value, name, value... */
public static List<Parameter> newList(String... parameters) {
List<Parameter> list = new ArrayList<Parameter>(parameters.length / 2);
for (int p = 0; p + 1 < parameters.length; p += 2) {
list.add(new Parameter(parameters[p], parameters[p + 1]));
}
return list;
}
/** A name/value pair. */
public static class Parameter implements Map.Entry<String, String> {
public Parameter(String key, String value) {
this.key = key;
this.value = value;
}
private final String key;
private String value;
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public String setValue(String value) {
try {
return this.value;
} finally {
this.value = value;
}
}
@Override
public String toString() {
return percentEncode(getKey()) + '=' + percentEncode(getValue());
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Parameter that = (Parameter) obj;
if (key == null) {
if (that.key != null)
return false;
} else if (!key.equals(that.key))
return false;
if (value == null) {
if (that.value != null)
return false;
} else if (!value.equals(that.value))
return false;
return true;
}
}
private static final String toString(Object from) {
return (from == null) ? null : from.toString();
}
/**
* Construct a URL like the given one, but with the given parameters added
* to its query string.
*/
public static String addParameters(String url, String... parameters)
throws IOException {
return addParameters(url, newList(parameters));
}
public static String addParameters(String url,
Iterable<? extends Map.Entry<String, String>> parameters)
throws IOException {
String form = formEncode(parameters);
if (form == null || form.length() <= 0) {
return url;
} else {
return url + ((url.indexOf("?") < 0) ? '?' : '&') + form;
}
}
public static boolean isEmpty(String str) {
return (str == null) || (str.length() == 0);
}
}