mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 05:52:06 +01:00
Improve Exceptions (SMACK-426)
This commit is contained in:
parent
9c8d7af3bf
commit
c592b4f046
9 changed files with 103 additions and 57 deletions
|
@ -18,6 +18,7 @@
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
|
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.Bind;
|
import org.jivesoftware.smack.packet.Bind;
|
||||||
import org.jivesoftware.smack.packet.Packet;
|
import org.jivesoftware.smack.packet.Packet;
|
||||||
|
@ -210,9 +211,10 @@ public class SASLAuthentication {
|
||||||
* @throws XMPPErrorException
|
* @throws XMPPErrorException
|
||||||
* @throws NoResponseException
|
* @throws NoResponseException
|
||||||
* @throws SASLErrorException
|
* @throws SASLErrorException
|
||||||
|
* @throws ResourceBindingNotOfferedException
|
||||||
*/
|
*/
|
||||||
public String authenticate(String resource, CallbackHandler cbh) throws IOException,
|
public String authenticate(String resource, CallbackHandler cbh) throws IOException,
|
||||||
NoResponseException, XMPPErrorException, SASLErrorException {
|
NoResponseException, XMPPErrorException, SASLErrorException, ResourceBindingNotOfferedException {
|
||||||
// Locate the SASLMechanism to use
|
// Locate the SASLMechanism to use
|
||||||
String selectedMechanism = null;
|
String selectedMechanism = null;
|
||||||
for (String mechanism : mechanismsPreferences) {
|
for (String mechanism : mechanismsPreferences) {
|
||||||
|
@ -265,8 +267,7 @@ public class SASLAuthentication {
|
||||||
return bindResourceAndEstablishSession(resource);
|
return bindResourceAndEstablishSession(resource);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// SASL authentication failed
|
throw new NoResponseException();
|
||||||
throw new SaslException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -352,8 +353,7 @@ public class SASLAuthentication {
|
||||||
return bindResourceAndEstablishSession(resource);
|
return bindResourceAndEstablishSession(resource);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// SASL authentication failed
|
throw new NoResponseException();
|
||||||
throw new SaslException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -404,11 +404,12 @@ public class SASLAuthentication {
|
||||||
return bindResourceAndEstablishSession(null);
|
return bindResourceAndEstablishSession(null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new SaslException();
|
throw new NoResponseException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String bindResourceAndEstablishSession(String resource) throws NoResponseException, XMPPErrorException {
|
private String bindResourceAndEstablishSession(String resource) throws XMPPErrorException,
|
||||||
|
ResourceBindingNotOfferedException, NoResponseException {
|
||||||
// Wait until server sends response containing the <bind> element
|
// Wait until server sends response containing the <bind> element
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!resourceBinded) {
|
if (!resourceBinded) {
|
||||||
|
@ -424,7 +425,7 @@ public class SASLAuthentication {
|
||||||
if (!resourceBinded) {
|
if (!resourceBinded) {
|
||||||
// Server never offered resource binding, which is REQURIED in XMPP client and server
|
// Server never offered resource binding, which is REQURIED in XMPP client and server
|
||||||
// implementations as per RFC6120 7.2
|
// implementations as per RFC6120 7.2
|
||||||
throw new IllegalStateException("Resource binding not offered by server");
|
throw new ResourceBindingNotOfferedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Bind bindResource = new Bind();
|
Bind bindResource = new Bind();
|
||||||
|
|
|
@ -152,8 +152,45 @@ public class SmackException extends Exception {
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = 4713404802621452016L;
|
private static final long serialVersionUID = 4713404802621452016L;
|
||||||
|
|
||||||
public FeatureNotSupportedException(String message) {
|
private final String feature;
|
||||||
super(message);
|
private final String jid;
|
||||||
|
|
||||||
|
public FeatureNotSupportedException(String feature) {
|
||||||
|
this(feature, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FeatureNotSupportedException(String feature, String jid) {
|
||||||
|
super(feature + " not supported" + (jid == null ? "" : " by '" + jid + "'"));
|
||||||
|
this.jid = jid;
|
||||||
|
this.feature = feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the feature which is not supported.
|
||||||
|
*
|
||||||
|
* @return the feature which is not supported
|
||||||
|
*/
|
||||||
|
public String getFeature() {
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get JID which does not support the feature. The JID can be null in cases when there are
|
||||||
|
* multiple JIDs queried for this feature.
|
||||||
|
*
|
||||||
|
* @return the JID which does not support the feature, or null
|
||||||
|
*/
|
||||||
|
public String getJid() {
|
||||||
|
return jid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ResourceBindingNotOfferedException extends SmackException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2346934138253437571L;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.sasl;
|
package org.jivesoftware.smack.sasl;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public enum SASLError {
|
public enum SASLError {
|
||||||
|
|
||||||
aborted,
|
aborted,
|
||||||
|
@ -29,6 +32,7 @@ public enum SASLError {
|
||||||
not_authorized,
|
not_authorized,
|
||||||
temporary_auth_failure;
|
temporary_auth_failure;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(SASLError.class.getName());
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.name().replace('_', '-');
|
return this.name().replace('_', '-');
|
||||||
|
@ -36,10 +40,12 @@ public enum SASLError {
|
||||||
|
|
||||||
public static SASLError fromString(String string) {
|
public static SASLError fromString(String string) {
|
||||||
string = string.replace('-', '_');
|
string = string.replace('-', '_');
|
||||||
for (SASLError error : SASLError.values()) {
|
SASLError saslError = null;
|
||||||
if (error.name().equals(string))
|
try {
|
||||||
return error;
|
saslError = SASLError.valueOf(string);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not transform string '" + string + "' to SASLError", e);
|
||||||
}
|
}
|
||||||
return null;
|
return saslError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,14 @@ public class SASLErrorException extends XMPPException {
|
||||||
private final Map<String,String> texts;
|
private final Map<String,String> texts;
|
||||||
|
|
||||||
public SASLErrorException(String mechanism, SASLFailure saslFailure) {
|
public SASLErrorException(String mechanism, SASLFailure saslFailure) {
|
||||||
|
super("SASLError using " + mechanism + ": " + saslFailure);
|
||||||
this.mechanism = mechanism;
|
this.mechanism = mechanism;
|
||||||
this.saslFailure = saslFailure;
|
this.saslFailure = saslFailure;
|
||||||
this.texts = new HashMap<String, String>();
|
this.texts = new HashMap<String, String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SASLErrorException(String mechanism, SASLFailure saslFailure, Map<String,String> texts) {
|
public SASLErrorException(String mechanism, SASLFailure saslFailure, Map<String,String> texts) {
|
||||||
|
super("SASLError using " + mechanism + ": " + saslFailure);
|
||||||
this.mechanism = mechanism;
|
this.mechanism = mechanism;
|
||||||
this.saslFailure = saslFailure;
|
this.saslFailure = saslFailure;
|
||||||
this.texts = texts;
|
this.texts = texts;
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class MultipleRecipientManager {
|
||||||
(replyRoom != null && replyRoom.trim().length() > 0)) {
|
(replyRoom != null && replyRoom.trim().length() > 0)) {
|
||||||
// Some specified JEP-33 features were requested so throw an exception alerting
|
// Some specified JEP-33 features were requested so throw an exception alerting
|
||||||
// the user that this features are not available
|
// the user that this features are not available
|
||||||
throw new FeatureNotSupportedException("Extended Stanza Addressing not supported by server");
|
throw new FeatureNotSupportedException("Extended Stanza Addressing");
|
||||||
}
|
}
|
||||||
// Send the packet to each individual recipient
|
// Send the packet to each individual recipient
|
||||||
sendToIndividualRecipients(connection, packet, to, cc, bcc);
|
sendToIndividualRecipients(connection, packet, to, cc, bcc);
|
||||||
|
|
|
@ -211,9 +211,11 @@ public class InBandBytestreamSession implements BytestreamSession {
|
||||||
connection.createPacketCollectorAndSend(close).nextResultOrThrow();
|
connection.createPacketCollectorAndSend(close).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
// Sadly we are unable to wrap the exception within the IOException, because the
|
// Sadly we are unable to use the IOException(Throwable) constructor because this
|
||||||
// IOException(String,Throwable) constructor is only support from Android API 9 on.
|
// constructor is only supported from Android API 9 on.
|
||||||
throw new IOException();
|
IOException ioException = new IOException();
|
||||||
|
ioException.initCause(e);
|
||||||
|
throw ioException;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputStream.cleanup();
|
this.inputStream.cleanup();
|
||||||
|
@ -769,9 +771,11 @@ public class InBandBytestreamSession implements BytestreamSession {
|
||||||
// close session unless it is already closed
|
// close session unless it is already closed
|
||||||
if (!this.isClosed) {
|
if (!this.isClosed) {
|
||||||
InBandBytestreamSession.this.close();
|
InBandBytestreamSession.this.close();
|
||||||
// Sadly we are unable to wrap the exception within the IOException, because the
|
// Sadly we are unable to use the IOException(Throwable) constructor because this
|
||||||
// IOException(String,Throwable) constructor is only support from Android API 9 on.
|
// constructor is only supported from Android API 9 on.
|
||||||
throw new IOException();
|
IOException ioException = new IOException();
|
||||||
|
ioException.initCause(e);
|
||||||
|
throw ioException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
|
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
@ -436,7 +437,7 @@ public final class Socks5BytestreamManager implements BytestreamManager {
|
||||||
XMPPErrorException discoveryException = null;
|
XMPPErrorException discoveryException = null;
|
||||||
// check if target supports SOCKS5 Bytestream
|
// check if target supports SOCKS5 Bytestream
|
||||||
if (!supportsSocks5(targetJID)) {
|
if (!supportsSocks5(targetJID)) {
|
||||||
throw new SmackException(targetJID + " doesn't support SOCKS5 Bytestream");
|
throw new FeatureNotSupportedException("SOCKS5 Bytestream", targetJID);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> proxies = new ArrayList<String>();
|
List<String> proxies = new ArrayList<String>();
|
||||||
|
|
|
@ -144,8 +144,9 @@ class Socks5Client {
|
||||||
* @return <code>true</code> if if a stream could be established, otherwise <code>false</code>.
|
* @return <code>true</code> if if a stream could be established, otherwise <code>false</code>.
|
||||||
* If <code>false</code> is returned the given Socket should be closed.
|
* If <code>false</code> is returned the given Socket should be closed.
|
||||||
* @throws SmackException
|
* @throws SmackException
|
||||||
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected boolean establish(Socket socket) throws SmackException {
|
protected boolean establish(Socket socket) throws SmackException, IOException {
|
||||||
|
|
||||||
byte[] connectionRequest;
|
byte[] connectionRequest;
|
||||||
byte[] connectionResponse;
|
byte[] connectionResponse;
|
||||||
|
@ -153,39 +154,35 @@ class Socks5Client {
|
||||||
* use DataInputStream/DataOutpuStream to assure read and write is completed in a single
|
* use DataInputStream/DataOutpuStream to assure read and write is completed in a single
|
||||||
* statement
|
* statement
|
||||||
*/
|
*/
|
||||||
try {
|
DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||||
DataInputStream in = new DataInputStream(socket.getInputStream());
|
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
|
||||||
|
|
||||||
// authentication negotiation
|
// authentication negotiation
|
||||||
byte[] cmd = new byte[3];
|
byte[] cmd = new byte[3];
|
||||||
|
|
||||||
cmd[0] = (byte) 0x05; // protocol version 5
|
cmd[0] = (byte) 0x05; // protocol version 5
|
||||||
cmd[1] = (byte) 0x01; // number of authentication methods supported
|
cmd[1] = (byte) 0x01; // number of authentication methods supported
|
||||||
cmd[2] = (byte) 0x00; // authentication method: no-authentication required
|
cmd[2] = (byte) 0x00; // authentication method: no-authentication required
|
||||||
|
|
||||||
out.write(cmd);
|
out.write(cmd);
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|
||||||
byte[] response = new byte[2];
|
byte[] response = new byte[2];
|
||||||
in.readFully(response);
|
in.readFully(response);
|
||||||
|
|
||||||
// check if server responded with correct version and no-authentication method
|
// check if server responded with correct version and no-authentication method
|
||||||
if (response[0] != (byte) 0x05 || response[1] != (byte) 0x00) {
|
if (response[0] != (byte) 0x05 || response[1] != (byte) 0x00) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// request SOCKS5 connection with given address/digest
|
|
||||||
connectionRequest = createSocks5ConnectRequest();
|
|
||||||
out.write(connectionRequest);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
// receive response
|
|
||||||
connectionResponse = Socks5Utils.receiveSocks5Message(in);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new SmackException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// request SOCKS5 connection with given address/digest
|
||||||
|
connectionRequest = createSocks5ConnectRequest();
|
||||||
|
out.write(connectionRequest);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
// receive response
|
||||||
|
connectionResponse = Socks5Utils.receiveSocks5Message(in);
|
||||||
|
|
||||||
// verify response
|
// verify response
|
||||||
connectionRequest[1] = (byte) 0x00; // set expected return status to 0
|
connectionRequest[1] = (byte) 0x00; // set expected return status to 0
|
||||||
return Arrays.equals(connectionRequest, connectionResponse);
|
return Arrays.equals(connectionRequest, connectionResponse);
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.io.OutputStream;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
|
@ -152,13 +153,10 @@ public class Socks5ByteStreamManagerTest {
|
||||||
|
|
||||||
fail("exception should be thrown");
|
fail("exception should be thrown");
|
||||||
}
|
}
|
||||||
catch (SmackException e) {
|
catch (FeatureNotSupportedException e) {
|
||||||
assertTrue(e.getMessage().contains("doesn't support SOCKS5 Bytestream"));
|
assertTrue(e.getFeature().equals("SOCKS5 Bytestream"));
|
||||||
}
|
assertTrue(e.getJid().equals(targetJID));
|
||||||
catch (IOException e) {
|
} catch(Exception e) {
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue