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:
Gaston Dombiak 2005-08-27 02:29:04 +00:00 committed by gato
parent 59e393e2ef
commit ad6be5887c
2 changed files with 142 additions and 25 deletions

View File

@ -20,17 +20,16 @@
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.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
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.
@ -97,8 +96,7 @@ class PacketReader {
* @return a new packet collector.
*/
public PacketCollector createPacketCollector(PacketFilter packetFilter) {
PacketCollector packetCollector = new PacketCollector(this, packetFilter);
return packetCollector;
return new PacketCollector(this, packetFilter);
}
/**
@ -158,7 +156,8 @@ class PacketReader {
if (waitTime <= 0) {
break;
}
connectionIDLock.wait(waitTime);
// Wait 3 times the standard time since TLS may take a while
connectionIDLock.wait(waitTime * 3);
long now = System.currentTimeMillis();
waitTime -= now - start;
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.
*/
@ -274,19 +282,51 @@ class PacketReader {
// Get the connection id.
for (int i=0; i<parser.getAttributeCount(); i++) {
if (parser.getAttributeName(i).equals("id")) {
// Save the connectionID and notify that we've gotten it.
synchronized(connectionIDLock) {
connectionID = parser.getAttributeValue(i);
connectionIDLock.notifyAll();
if (("1.0".equals(parser.getAttributeValue("", "version")) &&
connection.isUsingTLS()) || (!"1.0".equals(
parser.getAttributeValue("", "version")))) {
// 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")) {
// 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) {
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.
*

View File

@ -154,6 +154,10 @@ class PacketWriter {
listenerThread.start();
}
void setWriter(Writer writer) {
this.writer = writer;
}
/**
* Shuts down the packet writer. Once this method has been called, no further
* packets will be written to the server.
@ -187,14 +191,7 @@ class PacketWriter {
private void writePackets() {
try {
// Open the stream.
StringBuffer stream = new StringBuffer();
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;
openStream();
// Write out packets from the queue.
while (!done) {
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.
*/
@ -282,7 +297,7 @@ class PacketWriter {
private PacketFilter packetFilter;
public ListenerWrapper(PacketListener packetListener,
PacketFilter packetFilter)
PacketFilter packetFilter)
{
this.packetListener = packetListener;
this.packetFilter = packetFilter;