package com.googlecode.flickrjandroid.oauth;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.googlecode.flickrjandroid.FlickrException;
import com.googlecode.flickrjandroid.Parameter;
import com.googlecode.flickrjandroid.RequestContext;
import com.googlecode.flickrjandroid.uploader.ImageParameter;
import com.googlecode.flickrjandroid.util.Base64;
import com.googlecode.flickrjandroid.util.UrlUtilities;
/**
* a simple program to get flickr token and token secret.
*
* @author Mark Zang, Toby Yu
*
*/
public class OAuthUtils {
private static final String PARAMETER_SEPARATOR = "&";
private static final String NAME_VALUE_SEPARATOR = "=";
public static final String ENC = "UTF-8";
/** Default charsets */
public static final String DEFAULT_CONTENT_CHARSET = "ISO-8859-1";
private static final String HMAC_SHA1 = "HmacSHA1";
public static final String REQUEST_METHOD_GET = "GET";
public static final String REQUEST_METHOD_POST = "POST";
// private static final Logger logger = LoggerFactory.getLogger(OAuthUtils.class);
public static void addOAuthParams(String apiSharedSecret, String url, List<Parameter> parameters)
throws FlickrException {
addBasicOAuthParams(parameters);
signPost(apiSharedSecret, url, parameters);
}
public static void addOAuthToken(List<Parameter> parameters) throws OAuthException {
OAuth oauth = RequestContext.getRequestContext().getOAuth();
if (oauth == null || oauth.getToken() == null)
throw new OAuthException("OAuth token not set");
parameters.add(new OAuthTokenParameter(oauth.getToken().getOauthToken()));
}
public static void signGet(String apiSharedSecret, String url, List<Parameter> parameters) throws FlickrException {
sign(OAuthUtils.REQUEST_METHOD_GET, url, apiSharedSecret, parameters);
}
public static void signPost(String apiSharedSecret, String url, List<Parameter> parameters) throws FlickrException {
sign(OAuthUtils.REQUEST_METHOD_POST, url, apiSharedSecret, parameters);
}
public static void sign(String requestMethod, String url, String apiSharedSecret, List<Parameter> parameters) throws FlickrException {
OAuth oauth = RequestContext.getRequestContext().getOAuth();
String tokenSecret = oauth != null && oauth.getToken() != null
? oauth.getToken().getOauthTokenSecret() : "";
// generate the oauth_signature
String signature = OAuthUtils.getSignature(
requestMethod,
url,
parameters,
apiSharedSecret, tokenSecret);
// This method call must be signed.
parameters.add(new Parameter("oauth_signature", signature));
}
public static boolean hasSigned() {
OAuth oauth = RequestContext.getRequestContext().getOAuth();
if (oauth == null)
return false;
return oauth.getToken() != null;
}
public static void addBasicOAuthParams(List<Parameter> parameters) {
OAuthUtils.addOAuthNonce(parameters);
OAuthUtils.addOAuthTimestamp(parameters);
OAuthUtils.addOAuthSignatureMethod(parameters);
OAuthUtils.addOAuthVersion(parameters);
}
public static String getSignature(String requestMethod, String url, List<Parameter> parameters
, String apiSecret, String tokenSecret)
throws FlickrException {
String baseString;
try {
baseString = getRequestBaseString(requestMethod, url.toLowerCase(Locale.US), parameters);
// logger.debug("Generated OAuth Base String: {}", baseString);
return hmacsha1(baseString, apiSecret, tokenSecret);
} catch (UnsupportedEncodingException e) {
throw new FlickrException(e);
} catch (InvalidKeyException e) {
throw new FlickrException(e);
} catch (IllegalStateException e) {
throw new FlickrException(e);
} catch (NoSuchAlgorithmException e) {
throw new FlickrException(e);
}
}
public static String getSignature(String url, List<Parameter> parameters
, String apiSecret, String tokenSecret)
throws FlickrException {
return getSignature(REQUEST_METHOD_GET, url, parameters, apiSecret, tokenSecret);
}
public static String getRequestBaseString(String oauth_request_method, String url, List<Parameter> parameters) throws UnsupportedEncodingException {
StringBuffer result = new StringBuffer();
result.append(oauth_request_method);
result.append(PARAMETER_SEPARATOR);
result.append(UrlUtilities.encode(url));
result.append(PARAMETER_SEPARATOR);
Collections.sort(parameters, new Comparator<Parameter>() {
@Override
public int compare(Parameter o1, Parameter o2) {
if (o1 instanceof ImageParameter && (o2 instanceof ImageParameter) == false) {
return 1;
}
if (o2 instanceof ImageParameter && (o1 instanceof ImageParameter) == false) {
return -1;
}
int result = o1.getName().compareTo(o2.getName());
if (result == 0) {
result = o1.getValue().toString().compareTo(o2.getValue().toString());
}
return result;
}
});
return result.append(UrlUtilities.encode(format(parameters, ENC))).toString();
}
public static String hmacsha1(String data, String key, String tokenSecret) throws IllegalStateException, UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
byte[] byteHMAC = null;
Mac mac = Mac.getInstance(HMAC_SHA1);
if (tokenSecret == null) {
tokenSecret = "";
}
SecretKeySpec spec = new SecretKeySpec((key + PARAMETER_SEPARATOR + tokenSecret).getBytes(), HMAC_SHA1);
mac.init(spec);
byteHMAC = mac.doFinal(data.getBytes(ENC));
return new String(Base64.encode(byteHMAC));
}
/**
* Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
* list of parameters in an HTTP PUT or HTTP POST.
*
* @param parameters The parameters to include.
* @param encoding The encoding to use.
*/
public static String format (
final List<Parameter> parameters,
final String encoding) {
final StringBuilder result = new StringBuilder();
for (final Parameter parameter : parameters) {
Object valueObj = parameter.getValue();
if ((parameter instanceof ImageParameter) == false) {
final String encodedName = UrlUtilities.encode(parameter.getName());
final String value = String.valueOf(valueObj);
final String encodedValue = value != null ? UrlUtilities.encode(value) : "";
if (result.length() > 0)
result.append(PARAMETER_SEPARATOR);
result.append(encodedName);
result.append(NAME_VALUE_SEPARATOR);
result.append(encodedValue);
}
}
return result.toString();
}
public static String decode (final String content, final String encoding) {
try {
return URLDecoder.decode(content,
encoding != null ? encoding : DEFAULT_CONTENT_CHARSET);
} catch (UnsupportedEncodingException problem) {
throw new IllegalArgumentException(problem);
}
}
private static void addOAuthNonce(final List<Parameter> parameters) {
parameters.add(new Parameter("oauth_nonce", Long.toString(System.nanoTime())));
}
private static void addOAuthSignatureMethod(final List<Parameter> parameters) {
parameters.add(new Parameter("oauth_signature_method", "HMAC-SHA1"));
}
private static void addOAuthTimestamp(final List<Parameter> parameters) {
parameters.add(new Parameter("oauth_timestamp", String.valueOf((System.currentTimeMillis() / 1000))));
}
private static void addOAuthVersion(final List<Parameter> parameters) {
parameters.add(new Parameter("oauth_version", "1.0"));
}
}