/*
* Copyright 2006-2011 Daniel Henninger. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*
* Borrowed from fbgc, http://code.google.com/p/fbgc/
*/
package net.sf.kraken.protocols.xmpp.mechanisms;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;
/**
*/
public class FacebookConnectSASLMechanism extends SASLMechanism {
private String accessToken = "";
private String appSecret = "";
private String apiKey = "";
public FacebookConnectSASLMechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
// protected void authenticate() throws IOException, XMPPException {
// String[] mechanisms = { getName() };
// Map<String, String> props = new HashMap<String, String>();
// sc = Sasl.createSaslClient(mechanisms, null, "xmpp", hostname, props,
// this);
//
// super.authenticate();
// }
protected void authenticate() throws IOException, XMPPException {
StringBuilder stanza = new StringBuilder();
stanza.append("<auth mechanism=\"").append(getName());
stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
stanza.append("</auth>");
// Send the authentication to the server
getSASLAuthentication().send(new AuthMechanism(getName(), null));
}
public void authenticate(String apiKeyAndAppSecret, String host, String accessToken)
throws IOException, XMPPException {
if(apiKeyAndAppSecret==null || accessToken==null)
throw new IllegalStateException("Invalid parameters!");
String[] keyArray = apiKeyAndAppSecret.split("\\|");
if(keyArray==null || keyArray.length != 2)
throw new IllegalStateException("Api key or access token is not present!");
this.apiKey = keyArray[0];
this.appSecret = keyArray[1];
this.accessToken = accessToken;
this.authenticationId = accessToken;
this.password = accessToken;
this.hostname = host;
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
authenticate();
}
public void authenticate(String username, String host, CallbackHandler cbh)
throws IOException, XMPPException {
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
authenticate();
}
protected String getName() {
return "X-FACEBOOK-PLATFORM";
}
public void challengeReceived(String challenge) throws IOException {
// Build the challenge response stanza encoding the response text
StringBuilder stanza = new StringBuilder();
byte response[] = null;
if (challenge != null) {
String decodedResponse = new String(Base64.decode(challenge));
Map<String, String> parameters = getQueryMap(decodedResponse);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
Long callId = new GregorianCalendar().getTimeInMillis()/1000;
String sig = "api_key="+apiKey
+"call_id="+callId
+"method="+method
+"nonce="+nonce
+"access_token="+accessToken
+"v="+version
+appSecret;
try {
sig = MD5(sig);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
String composedResponse = "api_key="+apiKey+"&"
+"call_id="+callId+"&"
+"method="+method+"&"
+"nonce="+nonce+"&"
+"access_token="+accessToken+"&"
+"v="+version+"&"
+"sig="+sig;
response = composedResponse.getBytes();
}
String authenticationText="";
if (response != null) {
authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
stanza.append(authenticationText);
stanza.append("</response>");
// Send the authentication to the server
getSASLAuthentication().send(new Response(authenticationText));
}
private Map<String, String> getQueryMap(String query) {
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
return map;
}
private String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
public String MD5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("MD5");
byte[] md5hash = new byte[32];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
md5hash = md.digest();
return convertToHex(md5hash);
}
}