Don't use IQReplyFilter for resource binding

as the local username is only available after binding (and legacy
session establishment). This makes Smack compatible again with XMPP
services that use the user's JID as from attribute in the result IQ
after the bind set IQ, e.g. Facebook:

SENT:
<iq id='sqvTK-1' type='set'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
    <resource>Smack</resource>
  </bind>
</iq>

RCV:
<iq from='user.name.1@chat.facebook.com' id='sqvTK-1' type='result'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
    <jid>user.name.1@chat.facebook.com/Smack</jid>
  </bind>
</iq>

Fixes SMACK-590
This commit is contained in:
Florian Schmaus 2014-08-04 18:01:05 +02:00
parent 076c7d0b81
commit 6caf2cbdb5
5 changed files with 60 additions and 41 deletions

View File

@ -42,6 +42,7 @@ import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.debugger.SmackDebugger;
import org.jivesoftware.smack.filter.IQReplyFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
@ -431,15 +432,30 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
}
Bind bindResource = new Bind();
bindResource.setResource(resource);
Bind response = (Bind) createPacketCollectorAndSend(bindResource).nextResultOrThrow();
// Resource binding, see RFC6120 7.
// Note that we can not use IQReplyFilter here, since the users full JID is not yet
// available. It will become available right after the resource has been successfully bound.
Bind bindResource = Bind.newSet(resource);
PacketCollector packetCollector = createPacketCollector(new PacketIDFilter(bindResource));
try {
sendPacket(bindResource);
} catch (NotConnectedException e) {
packetCollector.cancel();
throw e;
}
Bind response = packetCollector.nextResultOrThrow();
String userJID = response.getJid();
if (sessionSupported && !getConfiguration().isLegacySessionDisabled()) {
Session session = new Session();
createPacketCollectorAndSend(session).nextResultOrThrow();
packetCollector = createPacketCollector(new PacketIDFilter(session));
try {
sendPacket(session);
} catch (NotConnectedException e) {
packetCollector.cancel();
throw e;
}
packetCollector.nextResultOrThrow();
}
return userJID;
}

View File

@ -139,9 +139,10 @@ public class PacketCollector {
*
* @return the next available packet.
*/
public Packet nextResult(long timeout) {
@SuppressWarnings("unchecked")
public <P extends Packet> P nextResult(long timeout) {
try {
return resultQueue.poll(timeout, TimeUnit.MILLISECONDS);
return (P) resultQueue.poll(timeout, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
@ -157,7 +158,7 @@ public class PacketCollector {
* @throws XMPPErrorException in case an error response.
* @throws NoResponseException if there was no response from the server.
*/
public Packet nextResultOrThrow() throws NoResponseException, XMPPErrorException {
public <P extends Packet> P nextResultOrThrow() throws NoResponseException, XMPPErrorException {
return nextResultOrThrow(connection.getPacketReplyTimeout());
}
@ -170,8 +171,8 @@ public class PacketCollector {
* @throws NoResponseException if there was no response from the server.
* @throws XMPPErrorException in case an error response.
*/
public Packet nextResultOrThrow(long timeout) throws NoResponseException, XMPPErrorException {
Packet result = nextResult(timeout);
public <P extends Packet> P nextResultOrThrow(long timeout) throws NoResponseException, XMPPErrorException {
P result = nextResult(timeout);
cancel();
if (result == null) {
throw new NoResponseException();

View File

@ -81,14 +81,13 @@ public class IQReplyFilter implements PacketFilter {
* @param iqPacket An IQ request. Filter for replies to this packet.
*/
public IQReplyFilter(IQ iqPacket, XMPPConnection conn) {
to = iqPacket.getTo();
if (conn.getUser() == null) {
// We have not yet been assigned a username, this can happen if the connection is
// in an early stage, i.e. when performing the SASL auth.
local = null;
if (iqPacket.getTo() != null) {
to = iqPacket.getTo().toLowerCase(Locale.US);
} else {
local = conn.getUser().toLowerCase(Locale.US);
to = null;
}
local = conn.getUser().toLowerCase(Locale.US);
server = conn.getServiceName().toLowerCase(Locale.US);
packetId = iqPacket.getPacketID();
@ -98,11 +97,10 @@ public class IQReplyFilter implements PacketFilter {
fromFilter = new OrFilter();
fromFilter.addFilter(FromMatchesFilter.createFull(to));
if (to == null) {
if (local != null)
fromFilter.addFilter(FromMatchesFilter.createBare(local));
fromFilter.addFilter(FromMatchesFilter.createBare(local));
fromFilter.addFilter(FromMatchesFilter.createFull(server));
}
else if (local != null && to.toLowerCase(Locale.US).equals(XmppStringUtils.parseBareAddress(local))) {
else if (to.equals(XmppStringUtils.parseBareAddress(local))) {
fromFilter.addFilter(FromMatchesFilter.createFull(null));
}
}

View File

@ -32,39 +32,44 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
*/
public class Bind extends IQ {
private String resource = null;
private String jid = null;
public static final String ELEMENT = "bind";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-bind";
public Bind() {
setType(IQ.Type.set);
private final String resource;
private final String jid;
public Bind(String resource, String jid) {
this.resource = resource;
this.jid = null;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getJid() {
return jid;
}
public void setJid(String jid) {
this.jid = jid;
}
@Override
public CharSequence getChildElementXML() {
public XmlStringBuilder getChildElementXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement("bind");
xml.xmlnsAttribute("urn:ietf:params:xml:ns:xmpp-bind");
xml.rightAngelBracket();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngelBracket();
xml.optElement("resource", resource);
xml.optElement("jid", jid);
xml.closeElement("bind");
xml.closeElement(ELEMENT);
return xml;
}
public static Bind newSet(String resource) {
Bind bind = new Bind(resource, null);
bind.setType(IQ.Type.set);
return bind;
}
public static Bind newResult(String jid) {
return new Bind(null, jid);
}
}

View File

@ -654,24 +654,23 @@ public class PacketParserUtils {
private static Bind parseResourceBinding(XmlPullParser parser) throws IOException,
XmlPullParserException {
Bind bind = new Bind();
Bind bind = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("resource")) {
bind.setResource(parser.nextText());
bind = Bind.newSet(parser.nextText());
}
else if (parser.getName().equals("jid")) {
bind.setJid(parser.nextText());
bind = Bind.newResult(parser.nextText());
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("bind")) {
if (parser.getName().equals(Bind.ELEMENT)) {
done = true;
}
}
}
return bind;
}