blob: 3126d83dba85c6cfcc18c569f68084e299a53dc1 [file] [log] [blame]
package org.jivesoftware.smack.sasl;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
import de.measite.smack.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;
/**
* This class is originally from http://code.google.com/p/fbgc/source/browse/trunk/daemon/src/main/java/org/albino/mechanisms/FacebookConnectSASLMechanism.java
* I just adapted to match the SMACK package scheme and
*/
public class SASLFacebookConnect extends SASLMechanism {
private String sessionKey = "";
private String sessionSecret = "";
private String apiKey = "";
static{
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM",
SASLFacebookConnect.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
}
public SASLFacebookConnect(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 {
final 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 Packet(){
@Override
public String toXML() {
return stanza.toString();
}
});
}
public void authenticate(String apiKeyAndSessionKey, String host, String sessionSecret)
throws IOException, XMPPException {
if(apiKeyAndSessionKey==null || sessionSecret==null)
throw new IllegalStateException("Invalid parameters!");
String[] keyArray = apiKeyAndSessionKey.split("\\|");
if(keyArray==null || keyArray.length != 2)
throw new IllegalStateException("Api key or session key is not present!");
this.apiKey = keyArray[0];
this.sessionKey = keyArray[1];
this.sessionSecret = sessionSecret;
this.authenticationId = sessionKey;
this.password = sessionSecret;
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
final 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
+"session_key="+sessionKey
+"v="+version
+sessionSecret;
try {
sig = MD5(sig);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
String composedResponse = "api_key="+apiKey+"&"
+"call_id="+callId+"&"
+"method="+method+"&"
+"nonce="+nonce+"&"
+"session_key="+sessionKey+"&"
+"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 Packet(){
@Override
public String toXML() {
return stanza.toString();
}
});
}
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);
}
}