mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 22:12:05 +01:00
1. Added support for TLS. SMACK-76
2. Added support for SASL. SMACK-24 git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@2732 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
59e393e2ef
commit
ad6be5887c
2 changed files with 142 additions and 25 deletions
|
@ -20,17 +20,16 @@
|
||||||
|
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
import org.xmlpull.v1.*;
|
import org.jivesoftware.smack.filter.PacketFilter;
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
|
import org.jivesoftware.smack.provider.ProviderManager;
|
||||||
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.xmlpull.mxp1.MXParser;
|
import org.xmlpull.mxp1.MXParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.*;
|
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
|
||||||
import org.jivesoftware.smack.filter.PacketFilter;
|
|
||||||
import org.jivesoftware.smack.util.*;
|
|
||||||
import org.jivesoftware.smack.provider.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for XML traffic from the XMPP server and parses it into packet objects.
|
* Listens for XML traffic from the XMPP server and parses it into packet objects.
|
||||||
|
@ -97,8 +96,7 @@ class PacketReader {
|
||||||
* @return a new packet collector.
|
* @return a new packet collector.
|
||||||
*/
|
*/
|
||||||
public PacketCollector createPacketCollector(PacketFilter packetFilter) {
|
public PacketCollector createPacketCollector(PacketFilter packetFilter) {
|
||||||
PacketCollector packetCollector = new PacketCollector(this, packetFilter);
|
return new PacketCollector(this, packetFilter);
|
||||||
return packetCollector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,7 +156,8 @@ class PacketReader {
|
||||||
if (waitTime <= 0) {
|
if (waitTime <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
connectionIDLock.wait(waitTime);
|
// Wait 3 times the standard time since TLS may take a while
|
||||||
|
connectionIDLock.wait(waitTime * 3);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
waitTime -= now - start;
|
waitTime -= now - start;
|
||||||
start = now;
|
start = now;
|
||||||
|
@ -217,6 +216,15 @@ class PacketReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the parser using the latest connection's reader. Reseting the parser is necessary
|
||||||
|
* when the plain connection has been secured or when a new opening stream element is going
|
||||||
|
* to be sent by the server.
|
||||||
|
*/
|
||||||
|
private void resetParser() throws XmlPullParserException {
|
||||||
|
parser.setInput(connection.reader);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process listeners.
|
* Process listeners.
|
||||||
*/
|
*/
|
||||||
|
@ -274,19 +282,51 @@ class PacketReader {
|
||||||
// Get the connection id.
|
// Get the connection id.
|
||||||
for (int i=0; i<parser.getAttributeCount(); i++) {
|
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||||
if (parser.getAttributeName(i).equals("id")) {
|
if (parser.getAttributeName(i).equals("id")) {
|
||||||
// Save the connectionID and notify that we've gotten it.
|
if (("1.0".equals(parser.getAttributeValue("", "version")) &&
|
||||||
synchronized(connectionIDLock) {
|
connection.isUsingTLS()) || (!"1.0".equals(
|
||||||
connectionID = parser.getAttributeValue(i);
|
parser.getAttributeValue("", "version")))) {
|
||||||
connectionIDLock.notifyAll();
|
// Save the connectionID and notify that we've gotten it.
|
||||||
|
// Only notify if TLS has been secured or if the server
|
||||||
|
// does not support TLS
|
||||||
|
synchronized(connectionIDLock) {
|
||||||
|
connectionID = parser.getAttributeValue(i);
|
||||||
|
connectionIDLock.notifyAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parser.getAttributeName(i).equals("from")) {
|
else if (parser.getAttributeName(i).equals("from")) {
|
||||||
// Use the server name that the server says that it is.
|
// Use the server name that the server says that it is.
|
||||||
connection.host = parser.getAttributeValue(i);
|
connection.serviceName = parser.getAttributeValue(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (parser.getName().equals("features")) {
|
||||||
|
parseFeatures(parser);
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("proceed")) {
|
||||||
|
// Secure the connection by negotiating TLS
|
||||||
|
connection.proceedTLSReceived();
|
||||||
|
// Reset the state of the parser since a new stream element is going
|
||||||
|
// to be sent by the server
|
||||||
|
resetParser();
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("failure")) {
|
||||||
|
// TLS negotiation has failed so close the connection.
|
||||||
|
throw new Exception("TLS negotiation has failed");
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("challenge")) {
|
||||||
|
// The server is challenging the SASL authentication made by the client
|
||||||
|
connection.getSASLAuthentication().challengeReceived(parser.nextText());
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("success")) {
|
||||||
|
// The SASL authentication with the server was successful. The next step
|
||||||
|
// will be to bind the resource
|
||||||
|
connection.getSASLAuthentication().authenticated();
|
||||||
|
// Reset the state of the parser since a new stream element is going
|
||||||
|
// to be sent by the server
|
||||||
|
resetParser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (eventType == XmlPullParser.END_TAG) {
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
if (parser.getName().equals("stream")) {
|
if (parser.getName().equals("stream")) {
|
||||||
|
@ -338,6 +378,68 @@ class PacketReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseFeatures(XmlPullParser parser) throws Exception {
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
if (parser.getName().equals("starttls")) {
|
||||||
|
// Confirm the server that we want to use TLS
|
||||||
|
connection.startTLSReceived();
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("mechanisms") && connection.isUsingTLS()) {
|
||||||
|
// The server is reporting available SASL mechanism. Store this information
|
||||||
|
// which will be used later while logging (i.e. authenticating) into
|
||||||
|
// the server
|
||||||
|
connection.getSASLAuthentication()
|
||||||
|
.setAvailableSASLMethods(parseMechanisms(parser));
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("bind")) {
|
||||||
|
// The server requires the client to bind a resource to the stream
|
||||||
|
connection.getSASLAuthentication().bindingRequired();
|
||||||
|
}
|
||||||
|
else if (parser.getName().equals("session")) {
|
||||||
|
// The server supports sessions
|
||||||
|
connection.getSASLAuthentication().sessionsSupported();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals("features")) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of Stings with the mechanisms included in the mechanisms stanza.
|
||||||
|
*
|
||||||
|
* @param parser the XML parser, positioned at the start of an IQ packet.
|
||||||
|
* @return a collection of Stings with the mechanisms included in the mechanisms stanza.
|
||||||
|
* @throws Exception if an exception occurs while parsing the stanza.
|
||||||
|
*/
|
||||||
|
private Collection parseMechanisms(XmlPullParser parser) throws Exception {
|
||||||
|
List mechanisms = new ArrayList();
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String elementName = parser.getName();
|
||||||
|
if (elementName.equals("mechanism")) {
|
||||||
|
mechanisms.add(parser.nextText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals("mechanisms")) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mechanisms;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an IQ packet.
|
* Parses an IQ packet.
|
||||||
*
|
*
|
||||||
|
|
|
@ -154,6 +154,10 @@ class PacketWriter {
|
||||||
listenerThread.start();
|
listenerThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setWriter(Writer writer) {
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down the packet writer. Once this method has been called, no further
|
* Shuts down the packet writer. Once this method has been called, no further
|
||||||
* packets will be written to the server.
|
* packets will be written to the server.
|
||||||
|
@ -187,14 +191,7 @@ class PacketWriter {
|
||||||
private void writePackets() {
|
private void writePackets() {
|
||||||
try {
|
try {
|
||||||
// Open the stream.
|
// Open the stream.
|
||||||
StringBuffer stream = new StringBuffer();
|
openStream();
|
||||||
stream.append("<stream:stream");
|
|
||||||
stream.append(" to=\"" + connection.getHost() + "\"");
|
|
||||||
stream.append(" xmlns=\"jabber:client\"");
|
|
||||||
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\">");
|
|
||||||
writer.write(stream.toString());
|
|
||||||
writer.flush();
|
|
||||||
stream = null;
|
|
||||||
// Write out packets from the queue.
|
// Write out packets from the queue.
|
||||||
while (!done) {
|
while (!done) {
|
||||||
Packet packet = nextPacket();
|
Packet packet = nextPacket();
|
||||||
|
@ -273,6 +270,24 @@ class PacketWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends to the server a new stream element. This operation may be requested several times
|
||||||
|
* so we need to encapsulate the logic in one place. This message will be sent while doing
|
||||||
|
* TLS, SASL and resource binding.
|
||||||
|
*
|
||||||
|
* @throws IOException If an error occurs while sending the stanza to the server.
|
||||||
|
*/
|
||||||
|
void openStream() throws IOException {
|
||||||
|
StringBuffer stream = new StringBuffer();
|
||||||
|
stream.append("<stream:stream");
|
||||||
|
stream.append(" to=\"").append(connection.serviceName).append("\"");
|
||||||
|
stream.append(" xmlns=\"jabber:client\"");
|
||||||
|
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
|
||||||
|
stream.append(" version=\"1.0\">");
|
||||||
|
writer.write(stream.toString());
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper class to associate a packet filter with a listener.
|
* A wrapper class to associate a packet filter with a listener.
|
||||||
*/
|
*/
|
||||||
|
@ -282,7 +297,7 @@ class PacketWriter {
|
||||||
private PacketFilter packetFilter;
|
private PacketFilter packetFilter;
|
||||||
|
|
||||||
public ListenerWrapper(PacketListener packetListener,
|
public ListenerWrapper(PacketListener packetListener,
|
||||||
PacketFilter packetFilter)
|
PacketFilter packetFilter)
|
||||||
{
|
{
|
||||||
this.packetListener = packetListener;
|
this.packetListener = packetListener;
|
||||||
this.packetFilter = packetFilter;
|
this.packetFilter = packetFilter;
|
||||||
|
|
Loading…
Reference in a new issue