();
sc = Sasl.createSaslClient(mechanisms, username, "xmpp", serviceName, props, this);
authenticate();
}
/**
* Same as {@link #authenticate(String, String, String, String)}, but with the hostname used as the serviceName.
*
* Kept for backward compatibility only.
*
* @param username the username of the user being authenticated.
* @param host the hostname where the user account resides.
* @param password the password for this account.
* @throws IOException If a network error occurs while authenticating.
* @throws XMPPException If a protocol error occurs or the user is not authenticated.
* @deprecated Please use {@link #authenticate(String, String, String, String)} instead.
*/
public void authenticate(String username, String host, String password) throws IOException, XMPPException {
authenticate(username, host, host, password);
}
/**
* Builds and sends the auth stanza to the server. The callback handler will handle
* any additional information, such as the authentication ID or realm, if it is needed.
*
* @param username the username of the user being authenticated.
* @param host the hostname where the user account resides.
* @param cbh the CallbackHandler to obtain user information.
* @throws IOException If a network error occures while authenticating.
* @throws XMPPException If a protocol error occurs or the user is not authenticated.
*/
public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
String[] mechanisms = { getName() };
Map props = new HashMap();
sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
authenticate();
}
protected void authenticate() throws IOException, XMPPException {
String authenticationText = null;
try {
if(sc.hasInitialResponse()) {
byte[] response = sc.evaluateChallenge(new byte[0]);
authenticationText = StringUtils.encodeBase64(response, false);
}
} catch (SaslException e) {
throw new XMPPException("SASL authentication failed", e);
}
// Send the authentication to the server
getSASLAuthentication().send(new AuthMechanism(getName(), authenticationText));
}
/**
* The server is challenging the SASL mechanism for the stanza he just sent. Send a
* response to the server's challenge.
*
* @param challenge a base64 encoded string representing the challenge.
* @throws IOException if an exception sending the response occurs.
*/
public void challengeReceived(String challenge) throws IOException {
byte response[];
if(challenge != null) {
response = sc.evaluateChallenge(StringUtils.decodeBase64(challenge));
} else {
response = sc.evaluateChallenge(new byte[0]);
}
Packet responseStanza;
if (response == null) {
responseStanza = new Response();
}
else {
responseStanza = new Response(StringUtils.encodeBase64(response, false));
}
// Send the authentication to the server
getSASLAuthentication().send(responseStanza);
}
/**
* Returns the common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or GSSAPI.
*
* @return the common name of the SASL mechanism.
*/
protected abstract String getName();
protected SASLAuthentication getSASLAuthentication() {
return saslAuthentication;
}
/**
*
*/
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback ncb = (NameCallback)callbacks[i];
ncb.setName(authenticationId);
} else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback)callbacks[i];
pcb.setPassword(password.toCharArray());
} else if(callbacks[i] instanceof RealmCallback) {
RealmCallback rcb = (RealmCallback)callbacks[i];
//Retrieve the REALM from the challenge response that the server returned when the client initiated the authentication
//exchange. If this value is not null or empty, *this value* has to be sent back to the server in the client's response
//to the server's challenge
String text = rcb.getDefaultText();
//The SASL client (sc) created in smack uses rcb.getText when creating the negotiatedRealm to send it back to the server
//Make sure that this value matches the server's realm
rcb.setText(text);
} else if(callbacks[i] instanceof RealmChoiceCallback){
//unused
//RealmChoiceCallback rccb = (RealmChoiceCallback)callbacks[i];
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
/**
* Initiating SASL authentication by select a mechanism.
*/
public class AuthMechanism extends Packet {
final private String name;
final private String authenticationText;
public AuthMechanism(String name, String authenticationText) {
if (name == null) {
throw new NullPointerException("SASL mechanism name shouldn't be null.");
}
this.name = name;
this.authenticationText = authenticationText;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("");
if (authenticationText != null &&
authenticationText.trim().length() > 0) {
stanza.append(authenticationText);
}
stanza.append("");
return stanza.toString();
}
}
/**
* A SASL challenge stanza.
*/
public static class Challenge extends Packet {
final private String data;
public Challenge(String data) {
this.data = data;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("");
if (data != null &&
data.trim().length() > 0) {
stanza.append(data);
}
stanza.append("");
return stanza.toString();
}
}
/**
* A SASL response stanza.
*/
public class Response extends Packet {
final private String authenticationText;
public Response() {
authenticationText = null;
}
public Response(String authenticationText) {
if (authenticationText == null || authenticationText.trim().length() == 0) {
this.authenticationText = null;
}
else {
this.authenticationText = authenticationText;
}
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("");
if (authenticationText != null) {
stanza.append(authenticationText);
}
stanza.append("");
return stanza.toString();
}
}
/**
* A SASL success stanza.
*/
public static class Success extends Packet {
final private String data;
public Success(String data) {
this.data = data;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("");
if (data != null &&
data.trim().length() > 0) {
stanza.append(data);
}
stanza.append("");
return stanza.toString();
}
}
/**
* A SASL failure stanza.
*/
public static class Failure extends Packet {
final private String condition;
public Failure(String condition) {
this.condition = condition;
}
/**
* Get the SASL related error condition.
*
* @return the SASL related error condition.
*/
public String getCondition() {
return condition;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("");
if (condition != null &&
condition.trim().length() > 0) {
stanza.append("<").append(condition).append("/>");
}
stanza.append("");
return stanza.toString();
}
}
}