1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-10 18:59:41 +02:00

Introduce SmackException

SmackException (and it's subclasses) is for all errors/exceptions not
defined by any XMPP specification. XMPPException is now an abstract
class for all errors defined by the XMPP specifications.

Methods that involve an IQ exchange now either return the result, which
is obtained by IQ response, or they throw an XMPPErrorException if an IQ
error was the result of the IQ set/get. If there was no response from
the server within the default packet timeout, a NoResponseException will
be thrown.

XMPP SASL errors are now also reported accordingly.

SMACK-426
This commit is contained in:
Florian Schmaus 2014-03-12 11:50:05 +01:00
parent 4b6f09f962
commit 4b56446e40
109 changed files with 2040 additions and 1599 deletions

View file

@ -21,9 +21,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.util.StringUtils;
@ -35,8 +35,6 @@ import org.jivesoftware.smack.util.StringUtils;
* @author Matt Tucker
*/
public class AccountManager {
private static final Logger LOGGER = Logger.getLogger(AccountManager.class.getName());
private XMPPConnection connection;
private Registration info = null;
@ -74,8 +72,10 @@ public class AccountManager {
* behavior is to only create new accounts before having logged in to a server.
*
* @return true if the server support creating new accounts.
* @throws XMPPErrorException
* @throws NoResponseException
*/
public boolean supportsAccountCreation() {
public boolean supportsAccountCreation() throws NoResponseException, XMPPErrorException {
// Check if we already know that the server supports creating new accounts
if (accountCreationSupported) {
return true;
@ -83,16 +83,11 @@ public class AccountManager {
// No information is known yet (e.g. no stream feature was received from the server
// indicating that it supports creating new accounts) so send an IQ packet as a way
// to discover if this feature is supported
try {
if (info == null) {
getRegistrationInfo();
accountCreationSupported = info.getType() != IQ.Type.ERROR;
}
return accountCreationSupported;
}
catch (XMPPException xe) {
return false;
if (info == null) {
getRegistrationInfo();
accountCreationSupported = info.getType() != IQ.Type.ERROR;
}
return accountCreationSupported;
}
/**
@ -118,21 +113,19 @@ public class AccountManager {
* the user's email address.
*
* @return the required account attributes.
* @throws XMPPErrorException
* @throws NoResponseException
*/
public Collection<String> getAccountAttributes() {
try {
if (info == null) {
getRegistrationInfo();
}
Map<String, String> attributes = info.getAttributes();
if (attributes != null) {
return Collections.unmodifiableSet(attributes.keySet());
}
public Collection<String> getAccountAttributes() throws NoResponseException, XMPPErrorException {
if (info == null) {
getRegistrationInfo();
}
catch (XMPPException xe) {
LOGGER.log(Level.SEVERE, "Error retrieving account attributes from server", xe);
Map<String, String> attributes = info.getAttributes();
if (attributes != null) {
return Collections.unmodifiableSet(attributes.keySet());
} else {
return Collections.emptySet();
}
return Collections.emptySet();
}
/**
@ -142,18 +135,14 @@ public class AccountManager {
* @param name the name of the account attribute to return its value.
* @return the value of the account attribute or <tt>null</tt> if an account
* attribute wasn't found for the requested name.
* @throws XMPPErrorException
* @throws NoResponseException
*/
public String getAccountAttribute(String name) {
try {
if (info == null) {
getRegistrationInfo();
}
return info.getAttributes().get(name);
public String getAccountAttribute(String name) throws NoResponseException, XMPPErrorException {
if (info == null) {
getRegistrationInfo();
}
catch (XMPPException xe) {
LOGGER.log(Level.SEVERE, "Error retrieving account attribute " + name + " info from server", xe);
}
return null;
return info.getAttributes().get(name);
}
/**
@ -162,18 +151,14 @@ public class AccountManager {
* that will complete the registration process.
*
* @return the account creation instructions, or <tt>null</tt> if there are none.
* @throws XMPPErrorException
* @throws NoResponseException
*/
public String getAccountInstructions() {
try {
if (info == null) {
getRegistrationInfo();
}
return info.getInstructions();
}
catch (XMPPException xe) {
LOGGER.log(Level.SEVERE, "Error retrieving account instructions from server", xe);
return null;
public String getAccountInstructions() throws NoResponseException, XMPPErrorException {
if (info == null) {
getRegistrationInfo();
}
return info.getInstructions();
}
/**
@ -186,12 +171,10 @@ public class AccountManager {
*
* @param username the username.
* @param password the password.
* @throws XMPPException if an error occurs creating the account.
* @throws XMPPErrorException
* @throws NoResponseException
*/
public void createAccount(String username, String password) throws XMPPException {
if (!supportsAccountCreation()) {
throw new XMPPException("Server does not support account creation.");
}
public void createAccount(String username, String password) throws NoResponseException, XMPPErrorException {
// Create a map for all the required attributes, but give them blank values.
Map<String, String> attributes = new HashMap<String, String>();
for (String attributeName : getAccountAttributes()) {
@ -208,20 +191,17 @@ public class AccountManager {
* @param username the username.
* @param password the password.
* @param attributes the account attributes.
* @throws XMPPException if an error occurs creating the account.
* @throws XMPPErrorException if an error occurs creating the account.
* @throws NoResponseException if there was no response from the server.
* @see #getAccountAttributes()
*/
public void createAccount(String username, String password, Map<String, String> attributes)
throws XMPPException
{
if (!supportsAccountCreation()) {
throw new XMPPException("Server does not support account creation.");
}
throws NoResponseException, XMPPErrorException {
Registration reg = new Registration();
reg.setType(IQ.Type.SET);
reg.setTo(connection.getServiceName());
attributes.put("username",username);
attributes.put("password",password);
attributes.put("username", username);
attributes.put("password", password);
reg.setAttributes(attributes);
connection.createPacketCollectorAndSend(reg).nextResultOrThrow();
}
@ -232,9 +212,10 @@ public class AccountManager {
* support changing passwords; an XMPPException will be thrown when that is the case.
*
* @throws IllegalStateException if not currently logged-in to the server.
* @throws XMPPException if an error occurs when changing the password.
* @throws XMPPErrorException if an error occurs when changing the password.
* @throws NoResponseException if there was no response from the server.
*/
public void changePassword(String newPassword) throws XMPPException {
public void changePassword(String newPassword) throws NoResponseException, XMPPErrorException {
Registration reg = new Registration();
reg.setType(IQ.Type.SET);
reg.setTo(connection.getServiceName());
@ -251,12 +232,10 @@ public class AccountManager {
* support deleting accounts; an XMPPException will be thrown when that is the case.
*
* @throws IllegalStateException if not currently logged-in to the server.
* @throws XMPPException if an error occurs when deleting the account.
* @throws XMPPErrorException if an error occurs when deleting the account.
* @throws NoResponseException if there was no response from the server.
*/
public void deleteAccount() throws XMPPException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Must be logged in to delete a account.");
}
public void deleteAccount() throws NoResponseException, XMPPErrorException {
Registration reg = new Registration();
reg.setType(IQ.Type.SET);
reg.setTo(connection.getServiceName());
@ -269,10 +248,13 @@ public class AccountManager {
/**
* Gets the account registration info from the server.
* @throws XMPPErrorException
* @throws NoResponseException
*
* @throws XMPPException if an error occurs.
* @throws SmackException if there was no response from the server.
*/
private synchronized void getRegistrationInfo() throws XMPPException {
private synchronized void getRegistrationInfo() throws NoResponseException, XMPPErrorException {
Registration reg = new Registration();
reg.setTo(connection.getServiceName());
info = (Registration) connection.createPacketCollectorAndSend(reg).nextResultOrThrow();

View file

@ -20,6 +20,8 @@ package org.jivesoftware.smack;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.XMPPError;
@ -152,9 +154,10 @@ public class PacketCollector {
* <tt>null</tt> will be returned. This method does also cancel the PacketCollector.
*
* @return the next available packet.
* @throws XMPPException
* @throws XMPPErrorException in case an error response.
* @throws NoResponseException if there was no response from the server.
*/
public Packet nextResultOrThrow() throws XMPPException {
public Packet nextResultOrThrow() throws NoResponseException, XMPPErrorException {
return nextResultOrThrow(connection.getPacketReplyTimeout());
}
@ -164,18 +167,19 @@ public class PacketCollector {
*
* @param timeout the amount of time to wait for the next packet (in milleseconds).
* @return the next available packet.
* @throws XMPPException in case of no response or error response
* @throws NoResponseException if there was no response from the server.
* @throws XMPPErrorException in case an error response.
*/
public Packet nextResultOrThrow(long timeout) throws XMPPException {
public Packet nextResultOrThrow(long timeout) throws NoResponseException, XMPPErrorException {
Packet result = nextResult(timeout);
cancel();
if (result == null) {
throw new XMPPException(SmackError.NO_RESPONSE_FROM_SERVER);
throw new NoResponseException();
}
XMPPError xmppError = result.getError();
if (xmppError != null) {
throw new XMPPException(xmppError);
throw new XMPPErrorException(xmppError);
}
return result;

View file

@ -16,7 +16,9 @@
*/
package org.jivesoftware.smack;
import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.jivesoftware.smack.packet.StreamError;
import java.util.Random;
import java.util.logging.Logger;
/**
@ -146,7 +148,7 @@ public class ReconnectionManager implements ConnectionListener {
connection.connect();
}
}
catch (XMPPException e) {
catch (Exception e) {
// Fires the failed reconnection notification
ReconnectionManager.this.notifyReconnectionFailed(e);
}
@ -191,17 +193,13 @@ public class ReconnectionManager implements ConnectionListener {
public void connectionClosedOnError(Exception e) {
done = false;
if (e instanceof XMPPException) {
XMPPException xmppEx = (XMPPException) e;
if (e instanceof StreamErrorException) {
StreamErrorException xmppEx = (StreamErrorException) e;
StreamError error = xmppEx.getStreamError();
String reason = error.getCode();
// Make sure the error is not null
if (error != null) {
String reason = error.getCode();
if ("conflict".equals(reason)) {
return;
}
if ("conflict".equals(reason)) {
return;
}
}

View file

@ -30,6 +30,9 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.IQReplyFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
@ -186,17 +189,19 @@ public class Roster {
* Reloads the entire roster from the server. This is an asynchronous operation,
* which means the method will return immediately, and the roster will be
* reloaded at a later point when the server responds to the reload request.
*
* @throws IllegalStateException if connection is not logged in or logged in anonymously
*
* @throws SmackException If not logged in.
* @throws IllegalStateException if logged in anonymously
*/
public void reload() {
public void reload() throws XMPPException, SmackException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Not logged in to server.");
throw new NotLoggedInException();
}
if (connection.isAnonymous()) {
throw new IllegalStateException("Anonymous users can't have a roster.");
}
RosterPacket packet = new RosterPacket();
if (rosterStore != null && connection.isRosterVersioningSupported()) {
packet.setVersion(rosterStore.getRosterVersion());
@ -235,18 +240,19 @@ public class Roster {
* after a logout/login. This is due to the way that XMPP stores group information.
*
* @param name the name of the group.
* @return a new group.
* @throws IllegalStateException if connection is not logged in or logged in anonymously
* @return a new group, or null if the group already exists
* @throws NotLoggedInException If not logged in.
* @throws IllegalStateException if logged in anonymously
*/
public RosterGroup createGroup(String name) {
public RosterGroup createGroup(String name) throws NotLoggedInException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Not logged in to server.");
throw new NotLoggedInException();
}
if (connection.isAnonymous()) {
throw new IllegalStateException("Anonymous users can't have a roster.");
}
if (groups.containsKey(name)) {
throw new IllegalArgumentException("Group with name " + name + " alread exists.");
return null;
}
RosterGroup group = new RosterGroup(name, connection);
@ -262,12 +268,15 @@ public class Roster {
* @param name the nickname of the user.
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
* the roster entry won't belong to a group.
* @throws XMPPException if an XMPP exception occurs.
* @throws IllegalStateException if connection is not logged in or logged in anonymously
* @throws NotLoggedInException
* @throws NoResponseException if there was no response from the server.
* @throws XMPPErrorException if an XMPP exception occurs.
* @throws NotLoggedInException If not logged in.
* @throws IllegalStateException if logged in anonymously
*/
public void createEntry(String user, String name, String[] groups) throws XMPPException {
public void createEntry(String user, String name, String[] groups) throws NotLoggedInException, NoResponseException, XMPPErrorException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Not logged in to server.");
throw new NotLoggedInException();
}
if (connection.isAnonymous()) {
throw new IllegalStateException("Anonymous users can't have a roster.");
@ -300,12 +309,14 @@ public class Roster {
* to send an updated subscription status.
*
* @param entry a roster entry.
* @throws XMPPException if an XMPP error occurs.
* @throws XMPPErrorException if an XMPP error occurs.
* @throws NotLoggedInException if not logged in.
* @throws NoResponseException SmackException if there was no response from the server.
* @throws IllegalStateException if connection is not logged in or logged in anonymously
*/
public void removeEntry(RosterEntry entry) throws XMPPException {
public void removeEntry(RosterEntry entry) throws NotLoggedInException, NoResponseException, XMPPErrorException {
if (!connection.isAuthenticated()) {
throw new IllegalStateException("Not logged in to server.");
throw new NotLoggedInException();
}
if (connection.isAnonymous()) {
throw new IllegalStateException("Anonymous users can't have a roster.");
@ -654,7 +665,7 @@ public class Roster {
private void addUpdateEntry(Collection<String> addedEntries,
Collection<String> updatedEntries, RosterPacket.Item item,
RosterEntry entry) {
RosterEntry entry) throws SmackException {
RosterEntry oldEntry = entries.put(item.getUser(), entry);
if (oldEntry == null) {
addedEntries.add(item.getUser());
@ -922,10 +933,14 @@ public class Roster {
Collection<String> updatedEntries = new ArrayList<String>();
Collection<String> deletedEntries = new ArrayList<String>();
if (packet instanceof RosterPacket) {
nonemptyResult((RosterPacket) packet, addedEntries, updatedEntries, deletedEntries);
} else {
emptyResult(addedEntries, updatedEntries);
try {
if (packet instanceof RosterPacket) {
nonemptyResult((RosterPacket) packet, addedEntries, updatedEntries, deletedEntries);
} else {
emptyResult(addedEntries, updatedEntries);
}
} catch (SmackException e) {
return;
}
synchronized (Roster.this) {
@ -936,7 +951,7 @@ public class Roster {
fireRosterChangedEvent(addedEntries, updatedEntries, deletedEntries);
}
private void emptyResult(Collection<String> addedEntries, Collection<String> updatedEntries) {
private void emptyResult(Collection<String> addedEntries, Collection<String> updatedEntries) throws SmackException {
for(RosterPacket.Item item : rosterStore.getEntries()){
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
item.getItemType(), item.getItemStatus(), Roster.this, connection);
@ -945,7 +960,7 @@ public class Roster {
}
private void addEntries(Collection<String> addedEntries, Collection<String> updatedEntries,
Collection<String> deletedEntries, String version, Collection<Item> items) {
Collection<String> deletedEntries, String version, Collection<Item> items) throws SmackException {
for (RosterPacket.Item item : items) {
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
item.getItemType(), item.getItemStatus(), Roster.this, connection);
@ -968,7 +983,9 @@ public class Roster {
}
}
private void nonemptyResult(RosterPacket packet, Collection<String> addedEntries, Collection<String> updatedEntries, Collection<String> deletedEntries) {
private void nonemptyResult(RosterPacket packet, Collection<String> addedEntries,
Collection<String> updatedEntries, Collection<String> deletedEntries)
throws SmackException {
RosterPacket rosterPacket = (RosterPacket) packet;
String version = rosterPacket.getVersion();
@ -1021,7 +1038,12 @@ public class Roster {
Collection<String> deletedEntries = new ArrayList<String>();
Item item = items.iterator().next();
processPushItem(addedEntries, updatedEntries, deletedEntries, version, item);
try {
processPushItem(addedEntries, updatedEntries, deletedEntries, version, item);
}
catch (SmackException e) {
return;
}
connection.sendPacket(IQ.createResultIQ(rosterPacket));
@ -1032,7 +1054,7 @@ public class Roster {
}
private void processPushItem(Collection<String> addedEntries, Collection<String> updatedEntries,
Collection<String> deletedEntries, String version, Item item) {
Collection<String> deletedEntries, String version, Item item) throws SmackException {
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
item.getItemType(), item.getItemStatus(), Roster.this, connection);

View file

@ -23,6 +23,8 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.RosterPacket;
import org.jivesoftware.smack.util.StringUtils;
@ -158,9 +160,10 @@ public class RosterGroup {
* to receive the updated roster.
*
* @param entry a roster entry.
* @throws XMPPException if an error occured while trying to add the entry to the group.
* @throws XMPPErrorException if an error occured while trying to add the entry to the group.
* @throws NoResponseException if there was no response from the server.
*/
public void addEntry(RosterEntry entry) throws XMPPException {
public void addEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException {
PacketCollector collector = null;
// Only add the entry if it isn't already in the list.
synchronized (entries) {
@ -187,9 +190,10 @@ public class RosterGroup {
* to receive the updated roster.
*
* @param entry a roster entry.
* @throws XMPPException if an error occurred while trying to remove the entry from the group.
* @throws XMPPErrorException if an error occurred while trying to remove the entry from the group.
* @throws NoResponseException if there was no response from the server.
*/
public void removeEntry(RosterEntry entry) throws XMPPException {
public void removeEntry(RosterEntry entry) throws NoResponseException, XMPPErrorException {
PacketCollector collector = null;
// Only remove the entry if it's in the entry list.
// Remove the entry locally, if we wait for RosterPacketListenerprocess>>Packet(Packet)

View file

@ -17,12 +17,17 @@
package org.jivesoftware.smack;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.sasl.*;
import org.jivesoftware.smack.sasl.SASLMechanism.SASLFailure;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.SaslException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.*;
@ -68,17 +73,13 @@ public class SASLAuthentication {
* Boolean indicating if SASL negotiation has finished and was successful.
*/
private boolean saslNegotiated;
/**
* Boolean indication if SASL authentication has failed. When failed the server may end
* the connection.
*/
private boolean saslFailed;
private boolean resourceBinded;
private boolean sessionSupported;
/**
* The SASL related error condition if there was one provided by the server.
*/
private String errorCondition;
private SASLFailure saslFailure;
static {
@ -205,15 +206,18 @@ public class SASLAuthentication {
* @param resource the desired resource.
* @param cbh the CallbackHandler used to get information from the user
* @return the full JID provided by the server while binding a resource to the connection.
* @throws XMPPException if an error occures while authenticating.
* @throws IOException
* @throws XMPPErrorException
* @throws NoResponseException
* @throws SASLErrorException
*/
public String authenticate(String resource, CallbackHandler cbh)
throws XMPPException {
public String authenticate(String resource, CallbackHandler cbh) throws IOException,
NoResponseException, XMPPErrorException, SASLErrorException {
// Locate the SASLMechanism to use
String selectedMechanism = null;
for (String mechanism : mechanismsPreferences) {
if (implementedMechanisms.containsKey(mechanism) &&
serverMechanisms.contains(mechanism)) {
if (implementedMechanisms.containsKey(mechanism)
&& serverMechanisms.contains(mechanism)) {
selectedMechanism = mechanism;
break;
}
@ -221,58 +225,55 @@ public class SASLAuthentication {
if (selectedMechanism != null) {
// A SASL mechanism was found. Authenticate using the selected mechanism and then
// proceed to bind a resource
Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
Constructor<? extends SASLMechanism> constructor;
try {
Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);
constructor = mechanismClass.getConstructor(SASLAuthentication.class);
currentMechanism = constructor.newInstance(this);
// Trigger SASL authentication with the selected mechanism. We use
// connection.getHost() since GSAPI requires the FQDN of the server, which
// may not match the XMPP domain.
currentMechanism.authenticate(connection.getHost(), cbh);
// Wait until SASL negotiation finishes
synchronized (this) {
if (!saslNegotiated && !saslFailed) {
try {
wait(30000);
}
catch (InterruptedException e) {
// Ignore
}
}
}
if (saslFailed) {
// SASL authentication failed and the server may have closed the connection
// so throw an exception
if (errorCondition != null) {
throw new XMPPException("SASL authentication " +
selectedMechanism + " failed: " + errorCondition);
}
else {
throw new XMPPException("SASL authentication failed using mechanism " +
selectedMechanism);
}
}
if (saslNegotiated) {
// Bind a resource for this connection and
return bindResourceAndEstablishSession(resource);
} else {
// SASL authentication failed
}
}
catch (XMPPException e) {
throw e;
}
catch (Exception e) {
e.printStackTrace();
throw new SaslException("Exception when creating the SASLAuthentication instance",
e);
}
// Trigger SASL authentication with the selected mechanism. We use
// connection.getHost() since GSAPI requires the FQDN of the server, which
// may not match the XMPP domain.
currentMechanism.authenticate(connection.getHost(), cbh);
// Wait until SASL negotiation finishes
synchronized (this) {
if (!saslNegotiated && saslFailure == null) {
try {
wait(30000);
}
catch (InterruptedException e) {
// Ignore
}
}
}
if (saslFailure != null) {
// SASL authentication failed and the server may have closed the connection
// so throw an exception
throw new SASLErrorException(selectedMechanism, saslFailure);
}
if (saslNegotiated) {
// Bind a resource for this connection and
return bindResourceAndEstablishSession(resource);
}
else {
// SASL authentication failed
throw new SaslException();
}
}
else {
throw new XMPPException("SASL Authentication failed. No known authentication mechanisims.");
throw new SaslException(
"SASL Authentication failed. No known authentication mechanisims.");
}
throw new XMPPException("SASL authentication failed");
}
/**
@ -287,15 +288,20 @@ public class SASLAuthentication {
* @param password the password to send to the server.
* @param resource the desired resource.
* @return the full JID provided by the server while binding a resource to the connection.
* @throws XMPPException if an error occures while authenticating.
* @throws XMPPErrorException
* @throws SASLErrorException
* @throws IOException
* @throws SaslException
* @throws SmackException
*/
public String authenticate(String username, String password, String resource)
throws XMPPException {
throws XMPPErrorException, SASLErrorException, SaslException, IOException,
SmackException {
// Locate the SASLMechanism to use
String selectedMechanism = null;
for (String mechanism : mechanismsPreferences) {
if (implementedMechanisms.containsKey(mechanism) &&
serverMechanisms.contains(mechanism)) {
if (implementedMechanisms.containsKey(mechanism)
&& serverMechanisms.contains(mechanism)) {
selectedMechanism = mechanism;
break;
}
@ -303,65 +309,56 @@ public class SASLAuthentication {
if (selectedMechanism != null) {
// A SASL mechanism was found. Authenticate using the selected mechanism and then
// proceed to bind a resource
Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
try {
Class<? extends SASLMechanism> mechanismClass = implementedMechanisms.get(selectedMechanism);
Constructor<? extends SASLMechanism> constructor = mechanismClass.getConstructor(SASLAuthentication.class);
currentMechanism = constructor.newInstance(this);
// Trigger SASL authentication with the selected mechanism. We use
// connection.getHost() since GSAPI requires the FQDN of the server, which
// may not match the XMPP domain.
//The serviceName is basically the value that XMPP server sends to the client as being the location
//of the XMPP service we are trying to connect to. This should have the format: host [ "/" serv-name ]
//as per RFC-2831 guidelines
String serviceName = connection.getServiceName();
currentMechanism.authenticate(username, connection.getHost(), serviceName, password);
// Wait until SASL negotiation finishes
synchronized (this) {
if (!saslNegotiated && !saslFailed) {
try {
wait(30000);
}
catch (InterruptedException e) {
// Ignore
}
}
}
if (saslFailed) {
// SASL authentication failed and the server may have closed the connection
// so throw an exception
if (errorCondition != null) {
throw new XMPPException("SASL authentication " +
selectedMechanism + " failed: " + errorCondition);
}
else {
throw new XMPPException("SASL authentication failed using mechanism " +
selectedMechanism);
}
}
if (saslNegotiated) {
// Bind a resource for this connection and
return bindResourceAndEstablishSession(resource);
}
else {
// SASL authentication failed
throw new XMPPException("SASL authentication failed");
}
}
catch (XMPPException e) {
throw e;
}
catch (Exception e) {
throw new SaslException("Exception when creating the SASLAuthentication instance",
e);
}
// Trigger SASL authentication with the selected mechanism. We use
// connection.getHost() since GSAPI requires the FQDN of the server, which
// may not match the XMPP domain.
// The serviceName is basically the value that XMPP server sends to the client as being
// the location of the XMPP service we are trying to connect to. This should have the
// format: host ["/" serv-name ] as per RFC-2831 guidelines
String serviceName = connection.getServiceName();
currentMechanism.authenticate(username, connection.getHost(), serviceName, password);
// Wait until SASL negotiation finishes
synchronized (this) {
if (!saslNegotiated && saslFailure == null) {
try {
wait(30000);
}
catch (InterruptedException e) {
// Ignore
}
}
}
if (saslFailure != null) {
// SASL authentication failed and the server may have closed the connection
// so throw an exception
throw new SASLErrorException(selectedMechanism, saslFailure);
}
if (saslNegotiated) {
// Bind a resource for this connection and
return bindResourceAndEstablishSession(resource);
}
else {
// SASL authentication failed
throw new XMPPException("SASL authentication failed", e);
throw new SaslException();
}
}
else {
// No SASL method was found, throw an exception
throw new XMPPException("SASL authentication not supported by server");
throw new SaslException(
"SASL Authentication failed. No known authentication mechanisims.");
}
}
@ -374,16 +371,19 @@ public class SASLAuthentication {
* no username.
*
* @return the full JID provided by the server while binding a resource to the connection.
* @throws XMPPException if an error occures while authenticating.
* @throws SASLErrorException
* @throws IOException
* @throws SaslException
* @throws XMPPErrorException if an error occures while authenticating.
* @throws SmackException if there was no response from the server.
*/
public String authenticateAnonymously() throws XMPPException {
try {
public String authenticateAnonymously() throws SASLErrorException, SaslException, IOException, SmackException, XMPPErrorException {
currentMechanism = new SASLAnonymous(this);
currentMechanism.authenticate(null,null,null,"");
// Wait until SASL negotiation finishes
synchronized (this) {
if (!saslNegotiated && !saslFailed) {
if (!saslNegotiated && saslFailure == null) {
try {
wait(5000);
}
@ -393,15 +393,10 @@ public class SASLAuthentication {
}
}
if (saslFailed) {
if (saslFailure != null) {
// SASL authentication failed and the server may have closed the connection
// so throw an exception
if (errorCondition != null) {
throw new XMPPException("SASL authentication failed: " + errorCondition);
}
else {
throw new XMPPException("SASL authentication failed");
}
throw new SASLErrorException(currentMechanism.toString(), saslFailure);
}
if (saslNegotiated) {
@ -409,14 +404,11 @@ public class SASLAuthentication {
return bindResourceAndEstablishSession(null);
}
else {
throw new XMPPException("SASL authentication failed");
throw new SaslException();
}
} catch (IOException e) {
throw new XMPPException("IOException while anonymous SASL authentication", e);
}
}
private String bindResourceAndEstablishSession(String resource) throws XMPPException {
private String bindResourceAndEstablishSession(String resource) throws NoResponseException, XMPPErrorException {
// Wait until server sends response containing the <bind> element
synchronized (this) {
if (!resourceBinded) {
@ -430,8 +422,9 @@ public class SASLAuthentication {
}
if (!resourceBinded) {
// Server never offered resource binding
throw new XMPPException("Resource binding not offered by server");
// Server never offered resource binding, which is REQURIED in XMPP client and server
// implementations as per RFC6120 7.2
throw new IllegalStateException("Resource binding not offered by server");
}
Bind bindResource = new Bind();
@ -497,12 +490,12 @@ public class SASLAuthentication {
* Notification message saying that SASL authentication has failed. The server may have
* closed the connection depending on the number of possible retries.
*
* @param condition the error condition provided by the server.
* @param saslFailure the SASL failure as reported by the server
* @see <a href="https://tools.ietf.org/html/rfc6120#section-6.5">RFC6120 6.5</a>
*/
void authenticationFailed(String condition) {
void authenticationFailed(SASLFailure saslFailure) {
synchronized (this) {
saslFailed = true;
errorCondition = condition;
this.saslFailure = saslFailure;
// Wake up the thread that is waiting in the #authenticate method
notify();
}
@ -540,7 +533,7 @@ public class SASLAuthentication {
*/
protected void init() {
saslNegotiated = false;
saslFailed = false;
saslFailure = null;
resourceBinded = false;
sessionSupported = false;
}

View file

@ -1,40 +0,0 @@
/**
*
* Copyright the original author or authors
*
* 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 org.jivesoftware.smack;
public enum SmackError {
NO_RESPONSE_FROM_SERVER("No response from server.");
private String message;
private SmackError(String errMessage) {
message = errMessage;
}
public String getErrorMessage() {
return message;
}
public static SmackError getErrorCode(String message) {
for (SmackError code : values()) {
if (code.message.equals(message)) {
return code;
}
}
return null;
}
}

View file

@ -0,0 +1,159 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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 org.jivesoftware.smack;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.util.dns.HostAddress;
/**
* Smack uses SmackExceptions for errors that are not defined by any XMPP specification.
*
* @author Florian Schmaus
*/
public class SmackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1844674365368214457L;
/**
* Creates a new SmackException with the Throwable that was the root cause of the exception.
*
* @param wrappedThrowable the root cause of the exception.
*/
public SmackException(Throwable wrappedThrowable) {
super(wrappedThrowable);
}
public SmackException(String message) {
super(message);
}
public SmackException(String message, Throwable wrappedThrowable) {
super(message, wrappedThrowable);
}
protected SmackException() {
}
/**
* Exception thrown always when there was no response to an IQ request within the packet reply
* timeout of the used connection instance.
*/
public static class NoResponseException extends SmackException {
/**
*
*/
private static final long serialVersionUID = -6523363748984543636L;
public NoResponseException() {
}
}
public static class NotLoggedInException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 3216216839100019278L;
public NotLoggedInException() {
}
}
public static class AlreadyLoggedInException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 5011416918049935231L;
public AlreadyLoggedInException() {
}
}
public static class NotConnectedException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 9197980400776001173L;
public NotConnectedException() {
}
}
public static class IllegalStateChangeException extends SmackException {
/**
*
*/
private static final long serialVersionUID = -1766023961577168927L;
public IllegalStateChangeException() {
}
}
public static class SecurityRequiredException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 384291845029773545L;
public SecurityRequiredException() {
}
}
public static class ConnectionException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 1686944201672697996L;
private final List<HostAddress> failedAddresses;
public ConnectionException(Throwable wrappedThrowable) {
super(wrappedThrowable);
failedAddresses = new ArrayList<HostAddress>(0);
}
public ConnectionException(List<HostAddress> failedAddresses) {
this.failedAddresses = failedAddresses;
}
public List<HostAddress> getFailedAddresses() {
return failedAddresses;
}
}
public static class FeatureNotSupportedException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 4713404802621452016L;
public FeatureNotSupportedException(String message) {
super(message);
}
}
}

View file

@ -16,6 +16,7 @@
*/
package org.jivesoftware.smack;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Constructor;
@ -38,6 +39,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.sasl.SaslException;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.filter.IQReplyFilter;
@ -357,9 +361,12 @@ public abstract class XMPPConnection {
* Listeners will be preserved from a previous connection if the reconnection
* occurs after an abrupt termination.
*
* @throws XMPPException if an error occurs while trying to establish the connection.
* @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
* @throws IOException
* @throws ConnectionException with detailed information about the failed connection.
*/
public abstract void connect() throws XMPPException;
public abstract void connect() throws SmackException, IOException, XMPPException;
/**
* Logs in to the server using the strongest authentication mode supported by
@ -382,9 +389,12 @@ public abstract class XMPPConnection {
*
* @param username the username.
* @param password the password or <tt>null</tt> if using a CallbackHandler.
* @throws XMPPException if an error occurs.
* @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
* @throws IOException
* @throws SaslException
*/
public void login(String username, String password) throws XMPPException {
public void login(String username, String password) throws XMPPException, SmackException, SaslException, IOException {
login(username, password, "Smack");
}
@ -410,11 +420,12 @@ public abstract class XMPPConnection {
* @param username the username.
* @param password the password or <tt>null</tt> if using a CallbackHandler.
* @param resource the resource.
* @throws XMPPException if an error occurs.
* @throws IllegalStateException if not connected to the server, or already logged in
* to the serrver.
* @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
* @throws IOException
* @throws SaslException
*/
public abstract void login(String username, String password, String resource) throws XMPPException;
public abstract void login(String username, String password, String resource) throws XMPPException, SmackException, SaslException, IOException;
/**
* Logs in to the server anonymously. Very few servers are configured to support anonymous
@ -422,11 +433,12 @@ public abstract class XMPPConnection {
* does succeed, your XMPP address will likely be in the form "123ABC@server/789XYZ" or
* "server/123ABC" (where "123ABC" and "789XYZ" is a random value generated by the server).
*
* @throws XMPPException if an error occurs or anonymous logins are not supported by the server.
* @throws IllegalStateException if not connected to the server, or already logged in
* to the serrver.
* @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
* @throws IOException
* @throws SaslException
*/
public abstract void loginAnonymously() throws XMPPException;
public abstract void loginAnonymously() throws XMPPException, SmackException, SaslException, IOException;
/**
* Sends the specified packet to the server.
@ -470,8 +482,10 @@ public abstract class XMPPConnection {
* {@link RosterListener}s will throw an IllegalStateException.
*
* @return the user's roster.
* @throws XMPPException if an error occurs on the XMPP protocol level.
* @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
*/
public abstract Roster getRoster();
public abstract Roster getRoster() throws XMPPException, SmackException;
/**
* Returns the SASLAuthentication manager that is responsible for authenticating with

View file

@ -26,7 +26,7 @@ import org.jivesoftware.smack.packet.XMPPError;
* and textual description of the problem, which are encapsulated in the XMPPError
* class. When appropriate, an XMPPError instance is attached instances of this exception.<p>
*
* When a stream error occured, the server will send a stream error to the client before
* When a stream error occurred, the server will send a stream error to the client before
* closing the connection. Stream errors are unrecoverable errors. When a stream error
* is sent to the client an XMPPException will be thrown containing the StreamError sent
* by the server.
@ -34,17 +34,14 @@ import org.jivesoftware.smack.packet.XMPPError;
* @see XMPPError
* @author Matt Tucker
*/
public class XMPPException extends Exception {
public abstract class XMPPException extends Exception {
private static final long serialVersionUID = 6881651633890968625L;
private StreamError streamError = null;
private XMPPError error = null;
private SmackError smackError = null;
/**
* Creates a new XMPPException.
*/
public XMPPException() {
protected XMPPException() {
super();
}
@ -53,52 +50,10 @@ public class XMPPException extends Exception {
*
* @param message description of the exception.
*/
public XMPPException(String message) {
protected XMPPException(String message) {
super(message);
}
/**
* Creates a new XMPPException with a Smack specific error code.
*
* @param code the root cause of the exception.
*/
public XMPPException(SmackError code) {
super(code.getErrorMessage());
smackError = code;
}
/**
* Creates a new XMPPException with the Throwable that was the root cause of the
* exception.
*
* @param wrappedThrowable the root cause of the exception.
*/
public XMPPException(Throwable wrappedThrowable) {
super(wrappedThrowable);
}
/**
* Creates a new XMPPException with the stream error that was the root case of the
* exception. When a stream error is received from the server then the underlying
* TCP connection will be closed by the server.
*
* @param streamError the root cause of the exception.
*/
public XMPPException(StreamError streamError) {
super();
this.streamError = streamError;
}
/**
* Cretaes a new XMPPException with the XMPPError that was the root case of the
* exception.
*
* @param error the root cause of the exception.
*/
public XMPPException(XMPPError error) {
super();
this.error = error;
}
/**
* Creates a new XMPPException with a description of the exception and the
@ -107,95 +62,116 @@ public class XMPPException extends Exception {
* @param message a description of the exception.
* @param wrappedThrowable the root cause of the exception.
*/
public XMPPException(String message, Throwable wrappedThrowable) {
protected XMPPException(String message, Throwable wrappedThrowable) {
super(message, wrappedThrowable);
}
/**
* Creates a new XMPPException with a description of the exception, an XMPPError,
* and the Throwable that was the root cause of the exception.
*
* @param message a description of the exception.
* @param error the root cause of the exception.
* @param wrappedThrowable the root cause of the exception.
*/
public XMPPException(String message, XMPPError error, Throwable wrappedThrowable) {
super(message, wrappedThrowable);
this.error = error;
}
public static class XMPPErrorException extends XMPPException {
/**
*
*/
private static final long serialVersionUID = 212790389529249604L;
private final XMPPError error;
/**
* Creates a new XMPPException with a description of the exception and the
* XMPPException that was the root cause of the exception.
*
* @param message a description of the exception.
* @param error the root cause of the exception.
*/
public XMPPException(String message, XMPPError error) {
super(message);
this.error = error;
}
/**
* Returns the XMPPError asscociated with this exception, or <tt>null</tt> if there
* isn't one.
*
* @return the XMPPError asscociated with this exception.
*/
public XMPPError getXMPPError() {
return error;
}
/**
* Returns the SmackError asscociated with this exception, or <tt>null</tt> if there
* isn't one.
*
* @return the SmackError asscociated with this exception.
*/
public SmackError getSmackError() {
return smackError;
}
/**
* Returns the StreamError asscociated with this exception, or <tt>null</tt> if there
* isn't one. The underlying TCP connection is closed by the server after sending the
* stream error to the client.
*
* @return the StreamError asscociated with this exception.
*/
public StreamError getStreamError() {
return streamError;
}
public String getMessage() {
String msg = super.getMessage();
// If the message was not set, but there is an XMPPError, return the
// XMPPError as the message.
if (msg == null && error != null) {
return error.toString();
/**
* Creates a new XMPPException with the XMPPError that was the root case of the exception.
*
* @param error the root cause of the exception.
*/
public XMPPErrorException(XMPPError error) {
super();
this.error = error;
}
else if (msg == null && streamError != null) {
/**
* Creates a new XMPPException with a description of the exception, an XMPPError, and the
* Throwable that was the root cause of the exception.
*
* @param message a description of the exception.
* @param error the root cause of the exception.
* @param wrappedThrowable the root cause of the exception.
*/
public XMPPErrorException(String message, XMPPError error, Throwable wrappedThrowable) {
super(message, wrappedThrowable);
this.error = error;
}
/**
* Creates a new XMPPException with a description of the exception and the XMPPException
* that was the root cause of the exception.
*
* @param message a description of the exception.
* @param error the root cause of the exception.
*/
public XMPPErrorException(String message, XMPPError error) {
super(message);
this.error = error;
}
/**
* Returns the XMPPError associated with this exception, or <tt>null</tt> if there isn't
* one.
*
* @return the XMPPError associated with this exception.
*/
public XMPPError getXMPPError() {
return error;
}
@Override
public String getMessage() {
String superMessage = super.getMessage();
if (superMessage != null) {
return superMessage;
}
else {
return error.toString();
}
}
@Override
public String toString() {
return getMessage();
}
}
public static class StreamErrorException extends XMPPException {
/**
*
*/
private static final long serialVersionUID = 3400556867134848886L;
private final StreamError streamError;
/**
* Creates a new XMPPException with the stream error that was the root case of the
* exception. When a stream error is received from the server then the underlying connection
* will be closed by the server.
*
* @param streamError the root cause of the exception.
*/
public StreamErrorException(StreamError streamError) {
super();
this.streamError = streamError;
}
/**
* Returns the StreamError associated with this exception. The underlying TCP connection is
* closed by the server after sending the stream error to the client.
*
* @return the StreamError associated with this exception.
*/
public StreamError getStreamError() {
return streamError;
}
@Override
public String getMessage() {
return streamError.toString();
}
return msg;
}
public String toString() {
StringBuilder buf = new StringBuilder();
String message = super.getMessage();
if (message != null) {
buf.append(message).append(": ");
@Override
public String toString() {
return getMessage();
}
if (error != null) {
buf.append(error);
}
if (streamError != null) {
buf.append(streamError);
}
if (getCause() != null) {
buf.append("\n -- caused by: ").append(getCause());
}
return buf.toString();
}
}

View file

@ -292,15 +292,26 @@ public class XMPPError {
public static final Condition unexpected_request = new Condition("unexpected-request");
public static final Condition request_timeout = new Condition("request-timeout");
private String value;
private final String value;
public Condition(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
@Override
public boolean equals(Object other) {
return toString().equals(other.toString());
}
@Override
public int hashCode() {
return value.hashCode();
}
}

View file

@ -0,0 +1,45 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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 org.jivesoftware.smack.sasl;
public enum SASLError {
aborted,
account_disabled,
credentials_expired,
encryption_required,
incorrect_encoding,
invalid_authzid,
invalid_mechanism,
malformed_request,
not_authorized,
temporary_auth_failure;
@Override
public String toString() {
return this.name().replace('_', '-');
}
public static SASLError fromString(String string) {
string = string.replace('-', '_');
for (SASLError error : SASLError.values()) {
if (error.name().equals(string))
return error;
}
return null;
}
}

View file

@ -0,0 +1,59 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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 org.jivesoftware.smack.sasl;
import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism.SASLFailure;
public class SASLErrorException extends XMPPException {
/**
*
*/
private static final long serialVersionUID = 6247573875760717257L;
private final SASLFailure saslFailure;
private final String mechanism;
private final Map<String,String> texts;
public SASLErrorException(String mechanism, SASLFailure saslFailure) {
this.mechanism = mechanism;
this.saslFailure = saslFailure;
this.texts = new HashMap<String, String>();
}
public SASLErrorException(String mechanism, SASLFailure saslFailure, Map<String,String> texts) {
this.mechanism = mechanism;
this.saslFailure = saslFailure;
this.texts = texts;
}
public SASLFailure getSASLFailure() {
return saslFailure;
}
public String getMechanism() {
return mechanism;
}
public Map<String,String> getTexts() {
return texts;
}
}

View file

@ -17,12 +17,13 @@
package org.jivesoftware.smack.sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslException;
import javax.security.auth.callback.CallbackHandler;
/**
@ -55,7 +56,7 @@ public class SASLGSSAPIMechanism extends SASLMechanism {
* @param cbh the CallbackHandler (not used with GSSAPI)
* @throws IOException If a network error occures while authenticating.
*/
public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, SaslException {
String[] mechanisms = { getName() };
Map<String,String> props = new HashMap<String,String>();
props.put(Sasl.SERVER_AUTH,"TRUE");
@ -74,7 +75,7 @@ public class SASLGSSAPIMechanism extends SASLMechanism {
* @param password the password of the user (ignored for GSSAPI)
* @throws IOException If a network error occures while authenticating.
*/
public void authenticate(String username, String host, String password) throws IOException, XMPPException {
public void authenticate(String username, String host, String password) throws IOException, SaslException {
String[] mechanisms = { getName() };
Map<String,String> props = new HashMap<String, String>();
props.put(Sasl.SERVER_AUTH,"TRUE");

View file

@ -16,7 +16,6 @@
*/
package org.jivesoftware.smack.sasl;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
@ -129,9 +128,9 @@ public abstract class SASLMechanism implements CallbackHandler {
* serviceName format is: host [ "/" serv-name ] as per RFC-2831
* @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.
* @throws SaslException
*/
public void authenticate(String username, String host, String serviceName, String password) throws IOException, XMPPException {
public void authenticate(String username, String host, String serviceName, String password) throws IOException, SaslException {
//Since we were not provided with a CallbackHandler, we will use our own with the given
//information
@ -153,24 +152,20 @@ public abstract class SASLMechanism implements CallbackHandler {
* @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.
* @throws SaslException If a protocol error occurs or the user is not authenticated.
*/
public void authenticate(String host, CallbackHandler cbh) throws IOException, XMPPException {
public void authenticate(String host, CallbackHandler cbh) throws IOException, SaslException {
String[] mechanisms = { getName() };
Map<String,String> props = new HashMap<String,String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
authenticate();
}
protected void authenticate() throws IOException, XMPPException {
protected void authenticate() throws IOException, SaslException {
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);
if (sc.hasInitialResponse()) {
byte[] response = sc.evaluateChallenge(new byte[0]);
authenticationText = StringUtils.encodeBase64(response, false);
}
// Send the authentication to the server
@ -349,11 +344,19 @@ public abstract class SASLMechanism implements CallbackHandler {
/**
* A SASL failure stanza.
*/
public static class Failure extends Packet {
final private String condition;
public static class SASLFailure extends Packet {
private final SASLError saslError;
private final String saslErrorString;
public Failure(String condition) {
this.condition = condition;
public SASLFailure(String saslError) {
SASLError error = SASLError.fromString(saslError);
if (error == null) {
// RFC6120 6.5 states that unknown condition must be treat as generic authentication failure.
this.saslError = SASLError.not_authorized;
} else {
this.saslError = error;
}
this.saslErrorString = saslError;
}
/**
@ -361,17 +364,22 @@ public abstract class SASLMechanism implements CallbackHandler {
*
* @return the SASL related error condition.
*/
public String getCondition() {
return condition;
public SASLError getSASLError() {
return saslError;
}
/**
*
* @return the SASL error as String
*/
public String getSASLErrorString() {
return saslErrorString;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
if (condition != null &&
condition.trim().length() > 0) {
stanza.append("<").append(condition).append("/>");
}
stanza.append("<").append(saslErrorString).append("/>");
stanza.append("</failure>");
return stanza.toString();
}

View file

@ -42,7 +42,7 @@ import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
import org.jivesoftware.smack.sasl.SASLMechanism.SASLFailure;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@ -636,7 +636,7 @@ public class PacketParserUtils {
* @return a SASL Failure packet.
* @throws Exception if an exception occurs while parsing the packet.
*/
public static Failure parseSASLFailure(XmlPullParser parser) throws Exception {
public static SASLFailure parseSASLFailure(XmlPullParser parser) throws Exception {
String condition = null;
boolean done = false;
while (!done) {
@ -653,7 +653,7 @@ public class PacketParserUtils {
}
}
}
return new Failure(condition);
return new SASLFailure(condition);
}
/**

View file

@ -348,7 +348,7 @@ public class ChatConnectionTest {
try {
con.connect();
con.login("me", "secret");
} catch (XMPPException e) {
} catch (Exception e) {
// No need for handling in a dummy connection.
}
return con;

View file

@ -74,7 +74,7 @@ public class DummyConnection extends XMPPConnection {
}
@Override
public void connect() throws XMPPException {
public void connect() {
connectionID = "dummy-" + new Random(new Date().getTime()).nextInt();
if (reconnect) {

View file

@ -520,8 +520,9 @@ public class RosterTest {
*
* @param connection the dummy connection of which the provided roster belongs to.
* @param roster the roster (or buddy list) which should be initialized.
* @throws SmackException
*/
public static void initRoster(DummyConnection connection, Roster roster) throws InterruptedException, XMPPException {
public static void initRoster(DummyConnection connection, Roster roster) throws InterruptedException, XMPPException, SmackException {
roster.reload();
while (true) {
final Packet sentPacket = connection.getSentPacket();

View file

@ -83,9 +83,11 @@ public class RosterVersioningTest {
/**
* Tests that receiving an empty roster result causes the roster to be populated
* by all entries of the roster store.
* @throws SmackException
* @throws XMPPException
*/
@Test(timeout = 5000)
public void testEqualVersionStored() throws InterruptedException, IOException {
public void testEqualVersionStored() throws InterruptedException, IOException, XMPPException, SmackException {
connection.getRoster().reload();
answerWithEmptyRosterResult();
@ -116,9 +118,11 @@ public class RosterVersioningTest {
/**
* Tests that a non-empty roster result empties the store.
* @throws SmackException
* @throws XMPPException
*/
@Test(timeout = 5000)
public void testOtherVersionStored() throws InterruptedException {
public void testOtherVersionStored() throws InterruptedException, XMPPException, SmackException {
connection.getRoster().reload();
Item vaglafItem = vaglafItem();

View file

@ -19,7 +19,7 @@ package org.jivesoftware.smack.parsing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
@ -79,7 +79,7 @@ public class ParsingExceptionTest {
@Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
throw new XMPPException("Test Exception");
throw new SmackException("Test Exception");
}
}