mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-10 18:59:41 +02:00
Add support for XEP-0198: Stream Management
- De-duplicate code by moving it into AbstractXMPPConnection - Introduce TopLevelStreamElement as superclass for all XMPP stream elements. - Add SynchronizationPoint, ParserUtils - Add ParserUtils Fixes SMACK-333 and SMACK-521
This commit is contained in:
parent
07c10a7444
commit
fc51f3df48
69 changed files with 3277 additions and 1083 deletions
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
@ -28,27 +29,43 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.compress.packet.Compress;
|
||||
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.CapsExtension;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Mechanisms;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Registration;
|
||||
import org.jivesoftware.smack.packet.RosterVer;
|
||||
import org.jivesoftware.smack.packet.Session;
|
||||
import org.jivesoftware.smack.packet.StartTls;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
import org.jivesoftware.smack.rosterstore.RosterStore;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jxmpp.util.XmppStringUtils;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
|
||||
public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||
private static final Logger LOGGER = Logger.getLogger(AbstractXMPPConnection.class.getName());
|
||||
|
@ -105,6 +122,15 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
protected final Map<PacketInterceptor, InterceptorWrapper> interceptors =
|
||||
new ConcurrentHashMap<PacketInterceptor, InterceptorWrapper>();
|
||||
|
||||
protected final Lock connectionLock = new ReentrantLock();
|
||||
|
||||
protected final Map<String, PacketExtension> streamFeatures = new HashMap<String, PacketExtension>();
|
||||
|
||||
/**
|
||||
* The full JID of the authenticated user.
|
||||
*/
|
||||
protected String user;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -125,7 +151,21 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
protected Writer writer;
|
||||
|
||||
/**
|
||||
* Set to success if the last features stanza from the server has been parsed. A XMPP connection
|
||||
* handshake can invoke multiple features stanzas, e.g. when TLS is activated a second feature
|
||||
* stanza is send by the server. This is set to true once the last feature stanza has been
|
||||
* parsed.
|
||||
*/
|
||||
protected final SynchronizationPoint<Exception> lastFeaturesReceived = new SynchronizationPoint<Exception>(
|
||||
AbstractXMPPConnection.this);
|
||||
|
||||
/**
|
||||
* Set to success if the sasl feature has been received.
|
||||
*/
|
||||
protected final SynchronizationPoint<SmackException> saslFeatureReceived = new SynchronizationPoint<SmackException>(
|
||||
AbstractXMPPConnection.this);
|
||||
|
||||
/**
|
||||
* The SASLAuthentication manager that is responsible for authenticating with the server.
|
||||
*/
|
||||
|
@ -142,21 +182,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
protected final ConnectionConfiguration config;
|
||||
|
||||
/**
|
||||
* Holds the Caps Node information for the used XMPP service (i.e. the XMPP server)
|
||||
*/
|
||||
private String serviceCapsNode;
|
||||
|
||||
/**
|
||||
* Defines how the from attribute of outgoing stanzas should be handled.
|
||||
*/
|
||||
private FromMode fromMode = FromMode.OMITTED;
|
||||
|
||||
/**
|
||||
* Stores whether the server supports rosterVersioning
|
||||
*/
|
||||
private boolean rosterVersioningSupported = false;
|
||||
|
||||
protected XMPPInputOutputStream compressionHandler;
|
||||
|
||||
/**
|
||||
|
@ -200,22 +230,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
protected int port;
|
||||
|
||||
/**
|
||||
* Set to true if the server requires the connection to be binded in order to continue.
|
||||
* <p>
|
||||
* Note that we use AtomicBoolean here because it allows us to set the Boolean *object*, which
|
||||
* we also use as synchronization object. A plain non-atomic Boolean object would be newly created
|
||||
* for every change of the boolean value, which makes it useless as object for wait()/notify().
|
||||
*/
|
||||
private AtomicBoolean bindingRequired = new AtomicBoolean(false);
|
||||
|
||||
private boolean sessionSupported;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Exception connectionException;
|
||||
|
||||
/**
|
||||
* Flag that indicates if the user is currently authenticated with the server.
|
||||
*/
|
||||
|
@ -227,6 +241,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
protected boolean wasAuthenticated = false;
|
||||
|
||||
private boolean anonymous = false;
|
||||
|
||||
/**
|
||||
* Create a new XMPPConnection to a XMPP server.
|
||||
*
|
||||
|
@ -267,14 +283,14 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
@Override
|
||||
public abstract boolean isAuthenticated();
|
||||
|
||||
@Override
|
||||
public abstract boolean isAnonymous();
|
||||
|
||||
@Override
|
||||
public abstract boolean isSecureConnection();
|
||||
|
||||
protected abstract void sendPacketInternal(Packet packet) throws NotConnectedException;
|
||||
|
||||
@Override
|
||||
public abstract void send(PlainStreamElement element) throws NotConnectedException;
|
||||
|
||||
@Override
|
||||
public abstract boolean isUsingCompression();
|
||||
|
||||
|
@ -292,22 +308,16 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
public void connect() throws SmackException, IOException, XMPPException {
|
||||
saslAuthentication.init();
|
||||
bindingRequired.set(false);
|
||||
sessionSupported = false;
|
||||
connectionException = null;
|
||||
saslFeatureReceived.init();
|
||||
lastFeaturesReceived.init();
|
||||
connectInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method that concrete subclasses of XMPPConnection need to implement to perform their
|
||||
* way of XMPP connection establishment. Implementations must guarantee that this method will
|
||||
* block until the last features stanzas has been parsed and the features have been reported
|
||||
* back to XMPPConnection (e.g. by calling @{link {@link AbstractXMPPConnection#serverRequiresBinding()}
|
||||
* and such).
|
||||
* <p>
|
||||
* Also implementations are required to perform an automatic login if the previous connection
|
||||
* state was logged (authenticated).
|
||||
*
|
||||
* way of XMPP connection establishment. Implementations are required to perform an automatic
|
||||
* login if the previous connection state was logged (authenticated).
|
||||
*
|
||||
* @throws SmackException
|
||||
* @throws IOException
|
||||
* @throws XMPPException
|
||||
|
@ -383,44 +393,20 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
*/
|
||||
public abstract void loginAnonymously() throws XMPPException, SmackException, IOException;
|
||||
|
||||
/**
|
||||
* Notification message saying that the server requires the client to bind a
|
||||
* resource to the stream.
|
||||
*/
|
||||
protected void serverRequiresBinding() {
|
||||
synchronized (bindingRequired) {
|
||||
bindingRequired.set(true);
|
||||
bindingRequired.notify();
|
||||
}
|
||||
}
|
||||
protected void bindResourceAndEstablishSession(String resource) throws XMPPErrorException,
|
||||
IOException, SmackException {
|
||||
|
||||
/**
|
||||
* Notification message saying that the server supports sessions. When a server supports
|
||||
* sessions the client needs to send a Session packet after successfully binding a resource
|
||||
* for the session.
|
||||
*/
|
||||
protected void serverSupportsSession() {
|
||||
sessionSupported = true;
|
||||
}
|
||||
// Wait until either:
|
||||
// - the servers last features stanza has been parsed
|
||||
// - the timeout occurs
|
||||
LOGGER.finer("Waiting for last features to be received before continuing with resource binding");
|
||||
lastFeaturesReceived.checkIfSuccessOrWait();
|
||||
|
||||
protected String bindResourceAndEstablishSession(String resource) throws XMPPErrorException,
|
||||
ResourceBindingNotOfferedException, NoResponseException, NotConnectedException {
|
||||
|
||||
synchronized (bindingRequired) {
|
||||
if (!bindingRequired.get()) {
|
||||
try {
|
||||
bindingRequired.wait(getPacketReplyTimeout());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (!bindingRequired.get()) {
|
||||
// Server never offered resource binding, which is REQURIED in XMPP client and
|
||||
// server
|
||||
// implementations as per RFC6120 7.2
|
||||
throw new ResourceBindingNotOfferedException();
|
||||
}
|
||||
}
|
||||
if (!hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) {
|
||||
// Server never offered resource binding, which is REQURIED in XMPP client and
|
||||
// server implementations as per RFC6120 7.2
|
||||
throw new ResourceBindingNotOfferedException();
|
||||
}
|
||||
|
||||
// Resource binding, see RFC6120 7.
|
||||
|
@ -435,9 +421,10 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
throw e;
|
||||
}
|
||||
Bind response = packetCollector.nextResultOrThrow();
|
||||
String userJID = response.getJid();
|
||||
user = response.getJid();
|
||||
setServiceName(XmppStringUtils.parseDomain(user));
|
||||
|
||||
if (sessionSupported && !getConfiguration().isLegacySessionDisabled()) {
|
||||
if (hasFeature(Session.ELEMENT, Session.NAMESPACE) && !getConfiguration().isLegacySessionDisabled()) {
|
||||
Session session = new Session();
|
||||
packetCollector = createPacketCollector(new PacketIDFilter(session));
|
||||
try {
|
||||
|
@ -448,58 +435,59 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
packetCollector.nextResultOrThrow();
|
||||
}
|
||||
return userJID;
|
||||
}
|
||||
|
||||
protected void setConnectionException(Exception e) {
|
||||
connectionException = e;
|
||||
}
|
||||
protected void afterSuccessfulLogin(final boolean anonymous, final boolean resumed) throws NotConnectedException {
|
||||
// Indicate that we're now authenticated.
|
||||
this.authenticated = true;
|
||||
this.anonymous = anonymous;
|
||||
|
||||
protected void throwConnectionExceptionOrNoResponse() throws IOException, NoResponseException, SmackException {
|
||||
if (connectionException != null) {
|
||||
if (connectionException instanceof IOException) {
|
||||
throw (IOException) connectionException;
|
||||
} else if (connectionException instanceof SmackException) {
|
||||
throw (SmackException) connectionException;
|
||||
} else {
|
||||
throw new SmackException(connectionException);
|
||||
}
|
||||
} else {
|
||||
throw new NoResponseException();
|
||||
// If debugging is enabled, change the the debug window title to include the
|
||||
// name we are now logged-in as.
|
||||
// If DEBUG_ENABLED was set to true AFTER the connection was created the debugger
|
||||
// will be null
|
||||
if (config.isDebuggerEnabled() && debugger != null) {
|
||||
debugger.userHasLogged(user);
|
||||
}
|
||||
callConnectionAuthenticatedListener();
|
||||
|
||||
// Set presence to online. It is important that this is done after
|
||||
// callConnectionAuthenticatedListener(), as this call will also
|
||||
// eventually load the roster. And we should load the roster before we
|
||||
// send the initial presence.
|
||||
if (config.isSendPresence() && !resumed) {
|
||||
sendPacket(new Presence(Presence.Type.available));
|
||||
}
|
||||
}
|
||||
|
||||
protected Reader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
protected Writer getWriter() {
|
||||
return writer;
|
||||
@Override
|
||||
public boolean isAnonymous() {
|
||||
return anonymous;
|
||||
}
|
||||
|
||||
protected void setServiceName(String serviceName) {
|
||||
config.setServiceName(serviceName);
|
||||
}
|
||||
|
||||
|
||||
protected void setLoginInfo(String username, String password, String resource) {
|
||||
config.setLoginInfo(username, password, resource);
|
||||
}
|
||||
|
||||
protected void serverSupportsAccountCreation() {
|
||||
AccountManager.getInstance(this).setSupportsAccountCreation(true);
|
||||
}
|
||||
|
||||
protected void maybeResolveDns() throws Exception {
|
||||
config.maybeResolveDns();
|
||||
}
|
||||
|
||||
protected Lock getConnectionLock() {
|
||||
return connectionLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Packet packet) throws NotConnectedException {
|
||||
if (!isConnected()) {
|
||||
throw new NotConnectedException();
|
||||
}
|
||||
if (packet == null) {
|
||||
throw new NullPointerException("Packet is null.");
|
||||
throw new IllegalArgumentException("Packet must not be null");
|
||||
}
|
||||
switch (fromMode) {
|
||||
case OMITTED:
|
||||
|
@ -800,35 +788,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servers Entity Caps node
|
||||
*
|
||||
* XMPPConnection holds this information in order to avoid a dependency to
|
||||
* smack-extensions where EntityCapsManager lives from smack.
|
||||
*
|
||||
* @param node
|
||||
*/
|
||||
protected void setServiceCapsNode(String node) {
|
||||
serviceCapsNode = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceCapsNode() {
|
||||
return serviceCapsNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRosterVersioningSupported() {
|
||||
return rosterVersioningSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the server supports roster versioning as defined in XEP-0237.
|
||||
*/
|
||||
protected void setRosterVersioningSupported() {
|
||||
rosterVersioningSupported = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPacketReplyTimeout() {
|
||||
return packetReplyTimeout;
|
||||
|
@ -1055,6 +1014,112 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
return config.isRosterLoadedAtLogin();
|
||||
}
|
||||
|
||||
protected final void parseFeatures(XmlPullParser parser) throws XmlPullParserException,
|
||||
IOException, SecurityRequiredException, NotConnectedException {
|
||||
streamFeatures.clear();
|
||||
final int initialDepth = parser.getDepth();
|
||||
while (true) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initialDepth + 1) {
|
||||
String name = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
switch (name) {
|
||||
case StartTls.ELEMENT:
|
||||
StartTls startTls = PacketParserUtils.parseStartTlsFeature(parser);
|
||||
addStreamFeature(startTls);
|
||||
break;
|
||||
case Mechanisms.ELEMENT:
|
||||
Mechanisms mechanisms = new Mechanisms(PacketParserUtils.parseMechanisms(parser));
|
||||
addStreamFeature(mechanisms);
|
||||
break;
|
||||
case Bind.ELEMENT:
|
||||
addStreamFeature(Bind.Feature.INSTANCE);
|
||||
break;
|
||||
case CapsExtension.ELEMENT:
|
||||
// Set the entity caps node for the server if one is send
|
||||
// See http://xmpp.org/extensions/xep-0115.html#stream
|
||||
String node = parser.getAttributeValue(null, "node");
|
||||
String ver = parser.getAttributeValue(null, "ver");
|
||||
String hash = parser.getAttributeValue(null, "hash");
|
||||
CapsExtension capsExtension = new CapsExtension(node, ver, hash);
|
||||
addStreamFeature(capsExtension);
|
||||
break;
|
||||
case Session.ELEMENT:
|
||||
addStreamFeature(Session.Feature.INSTANCE);
|
||||
break;
|
||||
case RosterVer.ELEMENT:
|
||||
if(namespace.equals(RosterVer.NAMESPACE)) {
|
||||
addStreamFeature(RosterVer.INSTANCE);
|
||||
}
|
||||
else {
|
||||
LOGGER.severe("Unkown Roster Versioning Namespace: "
|
||||
+ namespace
|
||||
+ ". Roster versioning not enabled");
|
||||
}
|
||||
break;
|
||||
case Compress.Feature.ELEMENT:
|
||||
Compress.Feature compression = PacketParserUtils.parseCompressionFeature(parser);
|
||||
addStreamFeature(compression);
|
||||
break;
|
||||
case Registration.Feature.ELEMENT:
|
||||
addStreamFeature(Registration.Feature.INSTANCE);
|
||||
break;
|
||||
default:
|
||||
parseFeaturesSubclass(name, namespace, parser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initialDepth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE)) {
|
||||
// Only proceed with SASL auth if TLS is disabled or if the server doesn't announce it
|
||||
if (!hasFeature(StartTls.ELEMENT, StartTls.NAMESPACE)
|
||||
|| config.getSecurityMode() == SecurityMode.disabled) {
|
||||
saslFeatureReceived.reportSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
// If the server reported the bind feature then we are that that we did SASL and maybe
|
||||
// STARTTLS. We can then report that the last 'stream:features' have been parsed
|
||||
if (hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) {
|
||||
if (!hasFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE)
|
||||
|| !config.isCompressionEnabled()) {
|
||||
// This was was last features from the server is either it did not contain
|
||||
// compression or if we disabled it
|
||||
lastFeaturesReceived.reportSuccess();
|
||||
}
|
||||
}
|
||||
afterFeaturesReceived();
|
||||
}
|
||||
|
||||
protected void parseFeaturesSubclass (String name, String namespace, XmlPullParser parser) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
|
||||
protected void afterFeaturesReceived() throws SecurityRequiredException, NotConnectedException {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <F extends PacketExtension> F getFeature(String element, String namespace) {
|
||||
return (F) streamFeatures.get(XmppStringUtils.generateKey(element, namespace));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFeature(String element, String namespace) {
|
||||
return getFeature(element, namespace) != null;
|
||||
}
|
||||
|
||||
protected void addStreamFeature(PacketExtension feature) {
|
||||
String key = XmppStringUtils.generateKey(feature.getElementName(), feature.getNamespace());
|
||||
streamFeatures.put(key, feature);
|
||||
}
|
||||
|
||||
private final ScheduledExecutorService removeCallbacksService = new ScheduledThreadPoolExecutor(1,
|
||||
new SmackExecutorThreadFactory(connectionCounterValue));
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
|
|||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.filter.AndFilter;
|
||||
import org.jivesoftware.smack.filter.IQReplyFilter;
|
||||
import org.jivesoftware.smack.filter.IQTypeFilter;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||
|
@ -45,6 +44,7 @@ import org.jivesoftware.smack.packet.IQ;
|
|||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.packet.RosterVer;
|
||||
import org.jivesoftware.smack.packet.RosterPacket.Item;
|
||||
import org.jivesoftware.smack.rosterstore.RosterStore;
|
||||
import org.jxmpp.util.XmppStringUtils;
|
||||
|
@ -227,12 +227,15 @@ public class Roster {
|
|||
}
|
||||
|
||||
RosterPacket packet = new RosterPacket();
|
||||
if (rosterStore != null && connection.isRosterVersioningSupported()) {
|
||||
if (rosterStore != null && isRosterVersioningSupported()) {
|
||||
packet.setVersion(rosterStore.getRosterVersion());
|
||||
}
|
||||
PacketFilter filter = new IQReplyFilter(packet, connection);
|
||||
connection.addPacketListener(new RosterResultListener(), filter);
|
||||
connection.sendPacket(packet);
|
||||
connection.sendIqWithResponseCallback(packet, new RosterResultListener(), new ExceptionCallback() {
|
||||
@Override
|
||||
public void processException(Exception exception) {
|
||||
LOGGER.log(Level.SEVERE, "Exception reloading roster" , exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -791,6 +794,10 @@ public class Roster {
|
|||
|| item.getItemType().equals(RosterPacket.ItemType.both);
|
||||
}
|
||||
|
||||
private boolean isRosterVersioningSupported() {
|
||||
return connection.hasFeature(RosterVer.ELEMENT, RosterVer.NAMESPACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration for the subscription mode options.
|
||||
*/
|
||||
|
@ -939,23 +946,13 @@ public class Roster {
|
|||
}
|
||||
|
||||
/**
|
||||
* Handles the case of the empty IQ-result for roster versioning.
|
||||
*
|
||||
* Intended to listen for a concrete roster result and deregisters
|
||||
* itself after a processed packet.
|
||||
* Handles roster reults as described in RFC 6121 2.1.4
|
||||
*/
|
||||
private class RosterResultListener implements PacketListener {
|
||||
|
||||
@Override
|
||||
public void processPacket(Packet packet) {
|
||||
connection.removePacketListener(this);
|
||||
|
||||
IQ result = (IQ)packet;
|
||||
if (!result.getType().equals(IQ.Type.result)) {
|
||||
LOGGER.severe("Roster result IQ not of type result. Packet: " + result.toXML());
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.fine("RosterResultListener received stanza");
|
||||
Collection<String> addedEntries = new ArrayList<String>();
|
||||
Collection<String> updatedEntries = new ArrayList<String>();
|
||||
Collection<String> deletedEntries = new ArrayList<String>();
|
||||
|
|
|
@ -19,17 +19,17 @@ package org.jivesoftware.smack;
|
|||
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.packet.Mechanisms;
|
||||
import org.jivesoftware.smack.sasl.SASLAnonymous;
|
||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStanzas.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStanzas.Success;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -128,13 +128,12 @@ public class SASLAuthentication {
|
|||
}
|
||||
|
||||
private final AbstractXMPPConnection connection;
|
||||
private Collection<String> serverMechanisms = new ArrayList<String>();
|
||||
private SASLMechanism currentMechanism = null;
|
||||
|
||||
/**
|
||||
* Boolean indicating if SASL negotiation has finished and was successful.
|
||||
*/
|
||||
private boolean saslNegotiated;
|
||||
private boolean authenticationSuccessful;
|
||||
|
||||
/**
|
||||
* Either of type {@link SmackException} or {@link SASLErrorException}
|
||||
|
@ -152,7 +151,7 @@ public class SASLAuthentication {
|
|||
* @return true if the server offered ANONYMOUS SASL as a way to authenticate users.
|
||||
*/
|
||||
public boolean hasAnonymousAuthentication() {
|
||||
return serverMechanisms.contains("ANONYMOUS");
|
||||
return serverMechanisms().contains("ANONYMOUS");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +160,7 @@ public class SASLAuthentication {
|
|||
* @return true if the server offered SASL authentication besides ANONYMOUS SASL.
|
||||
*/
|
||||
public boolean hasNonAnonymousAuthentication() {
|
||||
return !serverMechanisms.isEmpty() && (serverMechanisms.size() != 1 || !hasAnonymousAuthentication());
|
||||
return !serverMechanisms().isEmpty() && (serverMechanisms().size() != 1 || !hasAnonymousAuthentication());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,7 +196,7 @@ public class SASLAuthentication {
|
|||
|
||||
maybeThrowException();
|
||||
|
||||
if (!saslNegotiated) {
|
||||
if (!authenticationSuccessful) {
|
||||
throw new NoResponseException();
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +243,7 @@ public class SASLAuthentication {
|
|||
|
||||
maybeThrowException();
|
||||
|
||||
if (!saslNegotiated) {
|
||||
if (!authenticationSuccessful) {
|
||||
throw new NoResponseException();
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +282,7 @@ public class SASLAuthentication {
|
|||
|
||||
maybeThrowException();
|
||||
|
||||
if (!saslNegotiated) {
|
||||
if (!authenticationSuccessful) {
|
||||
throw new NoResponseException();
|
||||
}
|
||||
}
|
||||
|
@ -299,17 +298,6 @@ public class SASLAuthentication {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets the available SASL mechanism reported by the server. The server will report the
|
||||
* available SASL mechanism once the TLS negotiation was successful. This information is
|
||||
* stored and will be used when doing the authentication for logging in the user.
|
||||
*
|
||||
* @param mechanisms collection of strings with the available SASL mechanism reported
|
||||
* by the server.
|
||||
*/
|
||||
public void setAvailableSASLMethods(Collection<String> mechanisms) {
|
||||
this.serverMechanisms = mechanisms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link #challengeReceived(String, boolean)}, with <code>finalChallenge</code> set
|
||||
|
@ -355,7 +343,7 @@ public class SASLAuthentication {
|
|||
if (success.getData() != null) {
|
||||
challengeReceived(success.getData(), true);
|
||||
}
|
||||
saslNegotiated = true;
|
||||
authenticationSuccessful = true;
|
||||
// Wake up the thread that is waiting in the #authenticate method
|
||||
synchronized (this) {
|
||||
notify();
|
||||
|
@ -381,13 +369,17 @@ public class SASLAuthentication {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean authenticationSuccessful() {
|
||||
return authenticationSuccessful;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the internal state in order to be able to be reused. The authentication
|
||||
* is used by the connection at the first login and then reused after the connection
|
||||
* is disconnected and then reconnected.
|
||||
*/
|
||||
protected void init() {
|
||||
saslNegotiated = false;
|
||||
authenticationSuccessful = false;
|
||||
saslException = null;
|
||||
}
|
||||
|
||||
|
@ -404,7 +396,7 @@ public class SASLAuthentication {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (serverMechanisms.contains(mechanismName)) {
|
||||
if (serverMechanisms().contains(mechanismName)) {
|
||||
// Create a new instance of the SASLMechanism for every authentication attempt.
|
||||
selectedMechanism = mechanism.instanceForAuthentication(connection);
|
||||
break;
|
||||
|
@ -412,4 +404,9 @@ public class SASLAuthentication {
|
|||
}
|
||||
return selectedMechanism;
|
||||
}
|
||||
|
||||
private List<String> serverMechanisms() {
|
||||
Mechanisms mechanisms = connection.getFeature(Mechanisms.ELEMENT, Mechanisms.NAMESPACE);
|
||||
return mechanisms.getMechanisms();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,17 @@ public class SmackException extends Exception {
|
|||
}
|
||||
}
|
||||
|
||||
public static class AlreadyConnectedException extends SmackException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 5011416918049135231L;
|
||||
|
||||
public AlreadyConnectedException() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotConnectedException extends SmackException {
|
||||
|
||||
/**
|
||||
|
@ -120,6 +131,10 @@ public class SmackException extends Exception {
|
|||
|
||||
public SecurityRequiredException() {
|
||||
}
|
||||
|
||||
public SecurityRequiredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SecurityNotPossibleException extends SmackException {
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
*
|
||||
* 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.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
|
||||
public class SynchronizationPoint<E extends Exception> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SynchronizationPoint.class.getName());
|
||||
|
||||
private final AbstractXMPPConnection connection;
|
||||
private final Lock connectionLock;
|
||||
private final Condition condition;
|
||||
|
||||
private State state;
|
||||
private E failureException;
|
||||
|
||||
public SynchronizationPoint(AbstractXMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
this.connectionLock = connection.getConnectionLock();
|
||||
this.condition = connection.getConnectionLock().newCondition();
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
state = State.Initial;
|
||||
failureException = null;
|
||||
}
|
||||
|
||||
public void sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException,
|
||||
NotConnectedException {
|
||||
assert (state == State.Initial);
|
||||
connectionLock.lock();
|
||||
try {
|
||||
if (request != null) {
|
||||
if (request instanceof Packet) {
|
||||
connection.sendPacket((Packet) request);
|
||||
}
|
||||
else if (request instanceof PlainStreamElement){
|
||||
connection.send((PlainStreamElement) request);
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported element type");
|
||||
}
|
||||
state = State.RequestSent;
|
||||
}
|
||||
waitForConditionOrTimeout();
|
||||
}
|
||||
finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
checkForResponse();
|
||||
}
|
||||
|
||||
public void sendAndWaitForResponseOrThrow(PlainStreamElement request) throws E, NoResponseException,
|
||||
NotConnectedException {
|
||||
sendAndWaitForResponse(request);
|
||||
switch (state) {
|
||||
case Failure:
|
||||
if (failureException != null) {
|
||||
throw failureException;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Success, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E {
|
||||
checkIfSuccessOrWait();
|
||||
if (state == State.Failure) {
|
||||
throw failureException;
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfSuccessOrWait() throws NoResponseException {
|
||||
connectionLock.lock();
|
||||
try {
|
||||
if (state == State.Success) {
|
||||
// Return immediately
|
||||
return;
|
||||
}
|
||||
waitForConditionOrTimeout();
|
||||
} finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
checkForResponse();
|
||||
}
|
||||
|
||||
public void reportSuccess() {
|
||||
connectionLock.lock();
|
||||
try {
|
||||
state = State.Success;
|
||||
condition.signal();
|
||||
}
|
||||
finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void reportFailure() {
|
||||
reportFailure(null);
|
||||
}
|
||||
|
||||
public void reportFailure(E failureException) {
|
||||
connectionLock.lock();
|
||||
try {
|
||||
state = State.Failure;
|
||||
this.failureException = failureException;
|
||||
condition.signal();
|
||||
}
|
||||
finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wasSuccessful() {
|
||||
return state == State.Success;
|
||||
}
|
||||
|
||||
public boolean requestSent() {
|
||||
return state == State.RequestSent;
|
||||
}
|
||||
|
||||
private void waitForConditionOrTimeout() {
|
||||
long remainingWait = TimeUnit.MILLISECONDS.toNanos(connection.getPacketReplyTimeout());
|
||||
while (state == State.RequestSent || state == State.Initial) {
|
||||
try {
|
||||
remainingWait = condition.awaitNanos(
|
||||
remainingWait);
|
||||
if (remainingWait <= 0) {
|
||||
state = State.NoResponse;
|
||||
break;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.FINE, "was interrupted while waiting, this should not happen", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a response and throw a {@link NoResponseException} if there was none.
|
||||
* <p>
|
||||
* The exception is thrown, if state is one of 'Initial', 'NoResponse' or 'RequestSent'
|
||||
* </p>
|
||||
* @throws NoResponseException
|
||||
*/
|
||||
private void checkForResponse() throws NoResponseException {
|
||||
switch (state) {
|
||||
case Initial:
|
||||
case NoResponse:
|
||||
case RequestSent:
|
||||
throw new NoResponseException();
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private enum State {
|
||||
Initial,
|
||||
RequestSent,
|
||||
NoResponse,
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ import org.jivesoftware.smack.filter.IQReplyFilter;
|
|||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
import org.jivesoftware.smack.rosterstore.RosterStore;
|
||||
|
||||
/**
|
||||
|
@ -151,6 +153,13 @@ public interface XMPPConnection {
|
|||
*/
|
||||
public void sendPacket(Packet packet) throws NotConnectedException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @throws NotConnectedException
|
||||
*/
|
||||
public void send(PlainStreamElement element) throws NotConnectedException;
|
||||
|
||||
/**
|
||||
* Returns the roster for the user.
|
||||
* <p>
|
||||
|
@ -272,23 +281,6 @@ public interface XMPPConnection {
|
|||
*/
|
||||
public void removePacketInterceptor(PacketInterceptor packetInterceptor);
|
||||
|
||||
/**
|
||||
* Retrieve the servers Entity Caps node
|
||||
*
|
||||
* XMPPConnection holds this information in order to avoid a dependency to
|
||||
* smackx where EntityCapsManager lives from smack.
|
||||
*
|
||||
* @return the servers entity caps node
|
||||
*/
|
||||
public String getServiceCapsNode();
|
||||
|
||||
/**
|
||||
* Returns true if the server supports roster versioning as defined in XEP-0237.
|
||||
*
|
||||
* @return true if the server supports roster versioning
|
||||
*/
|
||||
public boolean isRosterVersioningSupported();
|
||||
|
||||
/**
|
||||
* Returns the current value of the reply timeout in milliseconds for request for this
|
||||
* XMPPConnection instance.
|
||||
|
@ -354,13 +346,33 @@ public interface XMPPConnection {
|
|||
|
||||
/**
|
||||
* Returns true if the roster will be loaded from the server when logging in. This
|
||||
* is the common behaviour for clients but sometimes clients may want to differ this
|
||||
* is the common behavior for clients but sometimes clients may want to differ this
|
||||
* or just never do it if not interested in rosters.
|
||||
*
|
||||
* @return true if the roster will be loaded from the server when logging in.
|
||||
* @see <a href="http://xmpp.org/rfcs/rfc6121.html#roster-login">RFC 6121 2.2 - Retrieving the Roster on Login</a>
|
||||
*/
|
||||
public boolean isRosterLoadedAtLogin();
|
||||
|
||||
/**
|
||||
* Get the feature packet extensions for a given stream feature of the
|
||||
* server, or <code>null</code> if the server doesn't support that feature.
|
||||
*
|
||||
* @param element
|
||||
* @param namespace
|
||||
* @return a packet extensions of the feature or <code>null</code>
|
||||
*/
|
||||
public <F extends PacketExtension> F getFeature(String element, String namespace);
|
||||
|
||||
/**
|
||||
* Return true if the server supports the given stream feature.
|
||||
*
|
||||
* @param element
|
||||
* @param namespace
|
||||
* @return
|
||||
*/
|
||||
public boolean hasFeature(String element, String namespace);
|
||||
|
||||
/**
|
||||
* Send a stanza and wait asynchronously for a response by using <code>replyFilter</code>.
|
||||
* <p>
|
||||
|
@ -464,4 +476,5 @@ public interface XMPPConnection {
|
|||
* @return the timestamp in milliseconds
|
||||
*/
|
||||
public long getLastStanzaReceived();
|
||||
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ public abstract class XMPPException extends Exception {
|
|||
super(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with a description of the exception and the
|
||||
* Throwable that was the root cause of the exception.
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
*
|
||||
* 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.compress.packet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.FullStreamElement;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class Compress extends FullStreamElement {
|
||||
|
||||
public static final String ELEMENT = "compress";
|
||||
public static final String NAMESPACE = "http://jabber.org/protocol/compress";
|
||||
|
||||
public final String method;
|
||||
|
||||
public Compress(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.rightAngleBracket();
|
||||
xml.element("method", method);
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static class Feature implements PacketExtension {
|
||||
public static final String ELEMENT = "compression";
|
||||
|
||||
public final List<String> methods;
|
||||
|
||||
public Feature(List<String> methods) {
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
public List<String> getMethods() {
|
||||
return Collections.unmodifiableList(methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.rightAngleBracket();
|
||||
for (String method : methods) {
|
||||
xml.element("method", method);
|
||||
}
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.compress.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.FullStreamElement;
|
||||
|
||||
public class Compressed extends FullStreamElement {
|
||||
|
||||
public static final String ELEMENT = "compressed";
|
||||
public static final String NAMESPACE = Compress.NAMESPACE;
|
||||
|
||||
public static final Compressed INSTANCE = new Compressed();
|
||||
|
||||
private Compressed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||
}
|
||||
}
|
|
@ -110,6 +110,7 @@ public class Java7ZlibInputOutputStream extends XMPPInputOutputStream {
|
|||
flushMethodInt = FULL_FLUSH_INT;
|
||||
}
|
||||
return new DeflaterOutputStream(outputStream, new Deflater(compressionLevel)) {
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (!supported) {
|
||||
super.flush();
|
||||
|
|
|
@ -176,10 +176,11 @@ public class ConsoleDebugger implements SmackDebugger {
|
|||
}
|
||||
|
||||
public void userHasLogged(String user) {
|
||||
boolean isAnonymous = "".equals(XmppStringUtils.parseLocalpart(user));
|
||||
String localpart = XmppStringUtils.parseLocalpart(user);
|
||||
boolean isAnonymous = "".equals(localpart);
|
||||
String title =
|
||||
"User logged (" + connection.hashCode() + "): "
|
||||
+ (isAnonymous ? "" : XmppStringUtils.parseBareAddress(user))
|
||||
+ (isAnonymous ? "" : localpart)
|
||||
+ "@"
|
||||
+ connection.getServiceName()
|
||||
+ ":"
|
||||
|
|
|
@ -51,18 +51,6 @@ public class Bind extends IQ {
|
|||
return jid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder getChildElementXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
|
||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||
xml.optElement("resource", resource);
|
||||
xml.optElement("jid", jid);
|
||||
xml.closeElement(ELEMENT);
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static Bind newSet(String resource) {
|
||||
Bind bind = new Bind(resource, null);
|
||||
bind.setType(IQ.Type.set);
|
||||
|
@ -72,4 +60,38 @@ public class Bind extends IQ {
|
|||
public static Bind newResult(String jid) {
|
||||
return new Bind(null, jid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder getChildElementXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||
xml.optElement("resource", resource);
|
||||
xml.optElement("jid", jid);
|
||||
xml.closeElement(ELEMENT);
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static class Feature implements PacketExtension {
|
||||
|
||||
public static final Feature INSTANCE = new Feature();
|
||||
|
||||
private Feature() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2009 Jonas Ådahl, 2011-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.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
/**
|
||||
* A XEP-0115 Entity Capabilities extension.
|
||||
* <p>
|
||||
* Note that this is currently in smack-core as it's a potential stream feature.
|
||||
* TODO: In feature versions of Smack, it should be possible to register
|
||||
* "providers" for stream features too, so that this class can be moved back to
|
||||
* smack-extensions.
|
||||
* </p>
|
||||
*/
|
||||
public class CapsExtension implements PacketExtension {
|
||||
public static final String NAMESPACE = "http://jabber.org/protocol/caps";
|
||||
public static final String ELEMENT = "c";
|
||||
|
||||
private final String node, ver, hash;
|
||||
|
||||
public CapsExtension(String node, String version, String hash) {
|
||||
this.node = node;
|
||||
this.ver = version;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public String getVer() {
|
||||
return ver;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* <c xmlns='http://jabber.org/protocol/caps'
|
||||
* hash='sha-1'
|
||||
* node='http://code.google.com/p/exodus'
|
||||
* ver='QgayPKawpkPSDYmwT/WM94uAlu0='/>
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.attribute("hash", hash).attribute("node", node).attribute("ver", ver);
|
||||
xml.closeEmptyElement();
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static CapsExtension from(Packet stanza) {
|
||||
return stanza.getExtension(ELEMENT, NAMESPACE);
|
||||
}
|
||||
}
|
|
@ -24,14 +24,7 @@ package org.jivesoftware.smack.packet;
|
|||
public interface Element {
|
||||
|
||||
/**
|
||||
* Returns the root element name.
|
||||
*
|
||||
* @return the element name.
|
||||
*/
|
||||
public String getElementName();
|
||||
|
||||
/**
|
||||
* Returns the XML representation of the PacketExtension.
|
||||
* Returns the XML representation of this Element.
|
||||
*
|
||||
* @return the packet extension as XML.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
/**
|
||||
* Base class for Stream elements. Everything that is not a stanza (RFC 6120 8.), ie. message,
|
||||
* presence and iq, should sublcass this class instead of {@link Packet}.
|
||||
*
|
||||
* @author Florian Schmaus
|
||||
*/
|
||||
public abstract class FullStreamElement extends PlainStreamElement implements PacketExtension {
|
||||
|
||||
}
|
|
@ -40,6 +40,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
*/
|
||||
public abstract class IQ extends Packet {
|
||||
|
||||
public static final String ELEMENT = "iq";
|
||||
public static final String QUERY_ELEMENT = "query";
|
||||
|
||||
private Type type = Type.get;
|
||||
|
@ -78,7 +79,7 @@ public abstract class IQ extends Packet {
|
|||
@Override
|
||||
public CharSequence toXML() {
|
||||
XmlStringBuilder buf = new XmlStringBuilder();
|
||||
buf.halfOpenElement("iq");
|
||||
buf.halfOpenElement(ELEMENT);
|
||||
addCommonAttributes(buf);
|
||||
if (type == null) {
|
||||
buf.attribute("type", "get");
|
||||
|
@ -94,7 +95,7 @@ public abstract class IQ extends Packet {
|
|||
if (error != null) {
|
||||
buf.append(error.toXML());
|
||||
}
|
||||
buf.closeElement("iq");
|
||||
buf.closeElement(ELEMENT);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class Mechanisms implements PacketExtension {
|
||||
|
||||
public static final String ELEMENT = "mechanisms";
|
||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||
|
||||
public final List<String> mechanisms = new LinkedList<String>();
|
||||
|
||||
public Mechanisms(String mechanism) {
|
||||
mechanisms.add(mechanism);
|
||||
}
|
||||
|
||||
public Mechanisms(Collection<String> mechanisms) {
|
||||
this.mechanisms.addAll(mechanisms);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public List<String> getMechanisms() {
|
||||
return Collections.unmodifiableList(mechanisms);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.rightAngleBracket();
|
||||
for (String mechanism : mechanisms) {
|
||||
xml.element("mechanism", mechanism);
|
||||
}
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
}
|
|
@ -53,6 +53,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
*/
|
||||
public class Message extends XmlLangStanza {
|
||||
|
||||
public static final String ELEMENT = "message";
|
||||
public static final String BODY = "body";
|
||||
|
||||
private Type type = Type.normal;
|
||||
|
@ -394,7 +395,7 @@ public class Message extends XmlLangStanza {
|
|||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder buf = new XmlStringBuilder();
|
||||
buf.halfOpenElement("message");
|
||||
buf.halfOpenElement(ELEMENT);
|
||||
buf.xmllangAttribute(language);
|
||||
addCommonAttributes(buf);
|
||||
if (type != Type.normal) {
|
||||
|
@ -440,7 +441,7 @@ public class Message extends XmlLangStanza {
|
|||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
buf.append(getExtensionsXML());
|
||||
buf.closeElement("message");
|
||||
buf.closeElement(ELEMENT);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
/**
|
||||
* Interface to represent a XML element. This is similar to {@link PacketExtension}, but does not
|
||||
* carry a namespace and is usually included as child element of an packet extension.
|
||||
*/
|
||||
public interface NamedElement extends Element {
|
||||
|
||||
/**
|
||||
* Returns the root element name.
|
||||
*
|
||||
* @return the element name.
|
||||
*/
|
||||
public String getElementName();
|
||||
|
||||
}
|
|
@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public abstract class Packet {
|
||||
public abstract class Packet extends TopLevelStreamElement {
|
||||
|
||||
public static final String TEXT = "text";
|
||||
public static final String ITEM = "item";
|
||||
|
@ -246,15 +246,6 @@ public abstract class Packet {
|
|||
packetExtensions.remove(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet as XML. Every concrete extension of Packet must implement
|
||||
* this method. In addition to writing out packet-specific data, every sub-class
|
||||
* should also write out the error and the extensions data if they are defined.
|
||||
*
|
||||
* @return the XML format of the packet as a String.
|
||||
*/
|
||||
public abstract CharSequence toXML();
|
||||
|
||||
/**
|
||||
* Returns the extension sub-packets (including properties data) as an XML
|
||||
* String, or the Empty String if there are no packet extensions.
|
||||
|
|
|
@ -28,7 +28,7 @@ package org.jivesoftware.smack.packet;
|
|||
* @see org.jivesoftware.smack.provider.PacketExtensionProvider
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface PacketExtension extends Element {
|
||||
public interface PacketExtension extends NamedElement {
|
||||
|
||||
/**
|
||||
* Returns the root element XML namespace.
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
/**
|
||||
* Plain stream elements, ie. everything that is <b>not a stanza</b> as defined
|
||||
* RFC 6120 8. Stanzas are {@link Message}, {@link Presence} and {@link IQ}.
|
||||
* Everything else should sublcass this class instead of {@link Packet}.
|
||||
* <p>
|
||||
* It is important to cleanly distinguish between stanzas and non-stanzas. For
|
||||
* example plain stream elements don't count into the stanza count of XEP-198
|
||||
* Stream Management.
|
||||
* </p>
|
||||
*
|
||||
* @author Florian Schmaus
|
||||
*/
|
||||
public abstract class PlainStreamElement extends TopLevelStreamElement {
|
||||
|
||||
}
|
|
@ -46,6 +46,8 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
*/
|
||||
public class Registration extends IQ {
|
||||
|
||||
public static final String NAMESPACE = "jabber:iq:register";
|
||||
|
||||
private String instructions = null;
|
||||
private Map<String, String> attributes = null;
|
||||
|
||||
|
@ -90,8 +92,8 @@ public class Registration extends IQ {
|
|||
@Override
|
||||
public XmlStringBuilder getChildElementXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
xml.halfOpenElement("query");
|
||||
xml.xmlnsAttribute("jabber:iq:register");
|
||||
xml.halfOpenElement(QUERY_ELEMENT);
|
||||
xml.xmlnsAttribute(NAMESPACE);
|
||||
xml.rightAngleBracket();
|
||||
xml.optElement("instructions", instructions);
|
||||
if (attributes != null && attributes.size() > 0) {
|
||||
|
@ -102,7 +104,33 @@ public class Registration extends IQ {
|
|||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
xml.append(getExtensionsXML());
|
||||
xml.closeElement("query");
|
||||
xml.closeElement(QUERY_ELEMENT);
|
||||
return xml;
|
||||
}
|
||||
|
||||
public static class Feature implements PacketExtension {
|
||||
|
||||
public static final String ELEMENT = "register";
|
||||
public static final String NAMESPACE = "http://jabber.org/features/iq-register";
|
||||
public static final Feature INSTANCE = new Registration.Feature();
|
||||
|
||||
private Feature() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence toXML() {
|
||||
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class RosterVer implements PacketExtension {
|
||||
|
||||
public static final String ELEMENT = "ver";
|
||||
public static final String NAMESPACE = "urn:xmpp:features:rosterver";
|
||||
|
||||
public static final RosterVer INSTANCE = new RosterVer();
|
||||
|
||||
private RosterVer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.closeEmptyElement();
|
||||
return xml;
|
||||
}
|
||||
|
||||
}
|
|
@ -32,12 +32,40 @@ package org.jivesoftware.smack.packet;
|
|||
*/
|
||||
public class Session extends IQ {
|
||||
|
||||
public static final String ELEMENT = "session";
|
||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-session";
|
||||
|
||||
private static final String SESSION = '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||
|
||||
public Session() {
|
||||
setType(IQ.Type.set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getChildElementXML() {
|
||||
return "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>";
|
||||
public String getChildElementXML() {
|
||||
return SESSION;
|
||||
}
|
||||
|
||||
public static class Feature implements PacketExtension {
|
||||
|
||||
public static final Session.Feature INSTANCE = new Feature();
|
||||
|
||||
private Feature() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXML() {
|
||||
return SESSION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class StartTls extends FullStreamElement {
|
||||
|
||||
public static final String ELEMENT = "starttls";
|
||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-tls";
|
||||
|
||||
private final boolean required;
|
||||
|
||||
public StartTls() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public StartTls(boolean required) {
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
public boolean required() {
|
||||
return required;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.rightAngleBracket();
|
||||
xml.condEmptyElement(required, "required");
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class StreamOpen extends FullStreamElement {
|
||||
|
||||
public static final String ELEMENT = "stream:stream";
|
||||
public static final String NAMESPACE = "jabber:client";
|
||||
public static final String VERSION = "1.0";
|
||||
|
||||
private final String service;
|
||||
|
||||
public StreamOpen(String service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML() {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||
xml.attribute("to", service);
|
||||
xml.attribute("xmlns:stream", "http://etherx.jabber.org/streams");
|
||||
xml.attribute("version", VERSION);
|
||||
xml.rightAngleBracket();
|
||||
return xml;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
/**
|
||||
* A XMPP top level stream element. This is either a stanza ({@link Packet}) or
|
||||
* just a plain stream element ({@link PlainStreamElement}).
|
||||
*/
|
||||
public abstract class TopLevelStreamElement implements Element {
|
||||
|
||||
}
|
|
@ -72,6 +72,10 @@ public class XMPPError {
|
|||
private String message;
|
||||
private List<PacketExtension> applicationExtensions = null;
|
||||
|
||||
public XMPPError(String condition) {
|
||||
this(new Condition(condition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error with the specified condition inferring the type.
|
||||
* If the Condition is predefined, client code should be like:
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jxmpp.util.XmppStringUtils;
|
||||
|
||||
/**
|
||||
* Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of
|
||||
|
@ -111,13 +112,13 @@ public final class ProviderManager {
|
|||
public static void addLoader(ProviderLoader loader) {
|
||||
if (loader.getIQProviderInfo() != null) {
|
||||
for (IQProviderInfo info : loader.getIQProviderInfo()) {
|
||||
iqProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
iqProviders.put(getKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
}
|
||||
}
|
||||
|
||||
if (loader.getExtensionProviderInfo() != null) {
|
||||
for (ExtensionProviderInfo info : loader.getExtensionProviderInfo()) {
|
||||
extensionProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
extensionProviders.put(getKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +144,7 @@ public final class ProviderManager {
|
|||
* @return the IQ provider.
|
||||
*/
|
||||
public static Object getIQProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
return iqProviders.get(key);
|
||||
}
|
||||
|
||||
|
@ -176,7 +177,7 @@ public final class ProviderManager {
|
|||
throw new IllegalArgumentException("Provider must be an IQProvider " +
|
||||
"or a Class instance sublcassing IQ.");
|
||||
}
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
iqProviders.put(key, provider);
|
||||
}
|
||||
|
||||
|
@ -189,7 +190,7 @@ public final class ProviderManager {
|
|||
* @param namespace the XML namespace.
|
||||
*/
|
||||
public static void removeIQProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
iqProviders.remove(key);
|
||||
}
|
||||
|
||||
|
@ -213,7 +214,7 @@ public final class ProviderManager {
|
|||
* @return the extenion provider.
|
||||
*/
|
||||
public static Object getExtensionProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
return extensionProviders.get(key);
|
||||
}
|
||||
|
||||
|
@ -233,7 +234,7 @@ public final class ProviderManager {
|
|||
throw new IllegalArgumentException("Provider must be a PacketExtensionProvider " +
|
||||
"or a Class instance.");
|
||||
}
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
extensionProviders.put(key, provider);
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ public final class ProviderManager {
|
|||
* @param namespace the XML namespace.
|
||||
*/
|
||||
public static void removeExtensionProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
String key = getKey(elementName, namespace);
|
||||
extensionProviders.remove(key);
|
||||
}
|
||||
|
||||
|
@ -261,14 +262,7 @@ public final class ProviderManager {
|
|||
return Collections.unmodifiableCollection(extensionProviders.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String key for a given element name and namespace.
|
||||
*
|
||||
* @param elementName the element name.
|
||||
* @param namespace the namespace.
|
||||
* @return a unique key for the element name and namespace pair.
|
||||
*/
|
||||
private static String getProviderKey(String elementName, String namespace) {
|
||||
return elementName + '#' + namespace;
|
||||
private static String getKey(String elementName, String namespace) {
|
||||
return XmppStringUtils.generateKey(elementName, namespace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStanzas.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
|
||||
public class SASLErrorException extends XMPPException {
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ package org.jivesoftware.smack.sasl;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStanzas.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStanzas.Response;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||
|
||||
|
@ -181,7 +181,7 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
authenticationText = "=";
|
||||
}
|
||||
// Send the authentication to the server
|
||||
connection.sendPacket(new AuthMechanism(getName(), authenticationText));
|
||||
connection.send(new AuthMechanism(getName(), authenticationText));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,7 +218,7 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
}
|
||||
|
||||
// Send the authentication to the server
|
||||
connection.sendPacket(responseStanza);
|
||||
connection.send(responseStanza);
|
||||
}
|
||||
|
||||
protected byte[] evaluateChallenge(byte[] challenge) throws SmackException {
|
||||
|
|
|
@ -16,18 +16,18 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.sasl.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
import org.jivesoftware.smack.sasl.SASLError;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class SaslStanzas {
|
||||
public class SaslStreamElements {
|
||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||
|
||||
/**
|
||||
* Initiating SASL authentication by select a mechanism.
|
||||
*/
|
||||
public static class AuthMechanism extends Packet {
|
||||
public static class AuthMechanism extends PlainStreamElement {
|
||||
public static final String ELEMENT = "auth";
|
||||
|
||||
private final String mechanism;
|
||||
|
@ -55,9 +55,9 @@ public class SaslStanzas {
|
|||
}
|
||||
|
||||
/**
|
||||
* A SASL challenge stanza.
|
||||
* A SASL challenge stream element.
|
||||
*/
|
||||
public static class Challenge extends Packet {
|
||||
public static class Challenge extends PlainStreamElement {
|
||||
public static final String ELEMENT = "challenge";
|
||||
|
||||
private final String data;
|
||||
|
@ -77,9 +77,9 @@ public class SaslStanzas {
|
|||
}
|
||||
|
||||
/**
|
||||
* A SASL response stanza.
|
||||
* A SASL response stream element.
|
||||
*/
|
||||
public static class Response extends Packet {
|
||||
public static class Response extends PlainStreamElement {
|
||||
public static final String ELEMENT = "response";
|
||||
|
||||
private final String authenticationText;
|
||||
|
@ -103,15 +103,15 @@ public class SaslStanzas {
|
|||
}
|
||||
|
||||
/**
|
||||
* A SASL success stanza.
|
||||
* A SASL success stream element.
|
||||
*/
|
||||
public static class Success extends Packet {
|
||||
public static class Success extends PlainStreamElement {
|
||||
public static final String ELEMENT = "success";
|
||||
|
||||
final private String data;
|
||||
|
||||
/**
|
||||
* Construct a new SASL success stanza with optional additional data for the SASL layer
|
||||
* Construct a new SASL success stream element with optional additional data for the SASL layer
|
||||
* (RFC6120 6.3.10)
|
||||
*
|
||||
* @param data additional data for the SASL layer or <code>null</code>
|
||||
|
@ -140,9 +140,9 @@ public class SaslStanzas {
|
|||
}
|
||||
|
||||
/**
|
||||
* A SASL failure stanza.
|
||||
* A SASL failure stream element.
|
||||
*/
|
||||
public static class SASLFailure extends Packet {
|
||||
public static class SASLFailure extends PlainStreamElement {
|
||||
public static final String ELEMENT = "failure";
|
||||
|
||||
private final SASLError saslError;
|
|
@ -123,15 +123,20 @@ public class DNSUtil {
|
|||
} else {
|
||||
srvDomain = domain;
|
||||
}
|
||||
List<SRVRecord> srvRecords = dnsResolver.lookupSRVRecords(srvDomain);
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
String logMessage = "Resolved SRV RR for " + srvDomain + ":";
|
||||
for (SRVRecord r : srvRecords)
|
||||
logMessage += " " + r;
|
||||
LOGGER.fine(logMessage);
|
||||
try {
|
||||
List<SRVRecord> srvRecords = dnsResolver.lookupSRVRecords(srvDomain);
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
String logMessage = "Resolved SRV RR for " + srvDomain + ":";
|
||||
for (SRVRecord r : srvRecords)
|
||||
logMessage += " " + r;
|
||||
LOGGER.fine(logMessage);
|
||||
}
|
||||
List<HostAddress> sortedRecords = sortSRVRecords(srvRecords);
|
||||
addresses.addAll(sortedRecords);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.WARNING, "Exception while resolving SRV records for " + domain, e);
|
||||
}
|
||||
List<HostAddress> sortedRecords = sortSRVRecords(srvRecords);
|
||||
addresses.addAll(sortedRecords);
|
||||
|
||||
// Step two: Add the hostname to the end of the list
|
||||
addresses.add(new HostAddress(domain));
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -30,6 +31,7 @@ import java.util.logging.Level;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.compress.packet.Compress;
|
||||
import org.jivesoftware.smack.packet.Bind;
|
||||
import org.jivesoftware.smack.packet.DefaultPacketExtension;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
@ -39,12 +41,13 @@ import org.jivesoftware.smack.packet.PacketExtension;
|
|||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Registration;
|
||||
import org.jivesoftware.smack.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.packet.StartTls;
|
||||
import org.jivesoftware.smack.packet.StreamError;
|
||||
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.packet.SaslStanzas.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
@ -63,9 +66,7 @@ public class PacketParserUtils {
|
|||
}
|
||||
|
||||
public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException {
|
||||
XmlPullParser parser = newXmppParser();
|
||||
parser.setInput(reader);
|
||||
|
||||
XmlPullParser parser = newXmppParser(reader);
|
||||
// Wind the parser forward to the first start tag
|
||||
int event = parser.getEventType();
|
||||
while (event != XmlPullParser.START_TAG) {
|
||||
|
@ -116,20 +117,17 @@ public class PacketParserUtils {
|
|||
* @throws Exception
|
||||
*/
|
||||
public static Packet parseStanza(XmlPullParser parser, XMPPConnection connection) throws Exception {
|
||||
final int eventType = parser.getEventType();
|
||||
if (eventType != XmlPullParser.START_TAG) {
|
||||
throw new IllegalArgumentException("Parser not at start tag");
|
||||
}
|
||||
assert(parser.getEventType() == XmlPullParser.START_TAG);
|
||||
final String name = parser.getName();
|
||||
switch (name) {
|
||||
case "message":
|
||||
case Message.ELEMENT:
|
||||
return parseMessage(parser);
|
||||
case "iq":
|
||||
case IQ.ELEMENT:
|
||||
return parseIQ(parser, connection);
|
||||
case "presence":
|
||||
case Presence.ELEMENT:
|
||||
return parsePresence(parser);
|
||||
default:
|
||||
return null;
|
||||
throw new IllegalArgumentException("Can only parse message, iq or presence, not " + name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,6 +149,25 @@ public class PacketParserUtils {
|
|||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XmlPullParser suitable for parsing XMPP. This means in particular that
|
||||
* FEATURE_PROCESS_NAMESPACES is enabled.
|
||||
* <p>
|
||||
* Note that not all XmlPullParser implementations will return a String on
|
||||
* <code>getText()</code> if the parser is on START_TAG or END_TAG. So you must not rely on this
|
||||
* behavior when using the parser.
|
||||
* </p>
|
||||
*
|
||||
* @param reader
|
||||
* @return A suitable XmlPullParser for XMPP parsing
|
||||
* @throws XmlPullParserException
|
||||
*/
|
||||
public static XmlPullParser newXmppParser(Reader reader) throws XmlPullParserException {
|
||||
XmlPullParser parser = newXmppParser();
|
||||
parser.setInput(reader);
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message packet.
|
||||
*
|
||||
|
@ -525,8 +542,7 @@ public class PacketParserUtils {
|
|||
else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) {
|
||||
iqPacket = parseRegistration(parser);
|
||||
}
|
||||
else if (elementName.equals("bind") &&
|
||||
namespace.equals("urn:ietf:params:xml:ns:xmpp-bind")) {
|
||||
else if (elementName.equals(Bind.ELEMENT) && namespace.equals(Bind.NAMESPACE)) {
|
||||
iqPacket = parseResourceBinding(parser);
|
||||
}
|
||||
// Otherwise, see if there is a registered provider for
|
||||
|
@ -688,25 +704,35 @@ public class PacketParserUtils {
|
|||
return registration;
|
||||
}
|
||||
|
||||
private static Bind parseResourceBinding(XmlPullParser parser) throws IOException,
|
||||
XmlPullParserException {
|
||||
public static Bind parseResourceBinding(XmlPullParser parser) throws IOException,
|
||||
XmlPullParserException {
|
||||
assert (parser.getEventType() == XmlPullParser.START_TAG);
|
||||
int initalDepth = parser.getDepth();
|
||||
String name;
|
||||
Bind bind = null;
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
outerloop: while (true) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("resource")) {
|
||||
switch (eventType) {
|
||||
case XmlPullParser.START_TAG:
|
||||
name = parser.getName();
|
||||
switch (name) {
|
||||
case "resource":
|
||||
bind = Bind.newSet(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("jid")) {
|
||||
break;
|
||||
case "jid":
|
||||
bind = Bind.newResult(parser.nextText());
|
||||
break;
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(Bind.ELEMENT)) {
|
||||
done = true;
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
name = parser.getName();
|
||||
if (name.equals(Bind.ELEMENT) && parser.getDepth() == initalDepth) {
|
||||
break outerloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (parser.getEventType() == XmlPullParser.END_TAG);
|
||||
return bind;
|
||||
}
|
||||
|
||||
|
@ -715,9 +741,11 @@ public class PacketParserUtils {
|
|||
*
|
||||
* @param parser the XML parser, positioned at the start of the mechanisms stanza.
|
||||
* @return a collection of Stings with the mechanisms included in the mechanisms stanza.
|
||||
* @throws Exception if an exception occurs while parsing the stanza.
|
||||
* @throws IOException
|
||||
* @throws XmlPullParserException
|
||||
*/
|
||||
public static Collection<String> parseMechanisms(XmlPullParser parser) throws Exception {
|
||||
public static Collection<String> parseMechanisms(XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
List<String> mechanisms = new ArrayList<String>();
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
|
@ -739,32 +767,42 @@ public class PacketParserUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse the available compression methods reported from the server.
|
||||
* Parse the Compression Feature reported from the server.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of the compression stanza.
|
||||
* @return a collection of Stings with the methods included in the compression stanza.
|
||||
* @return The CompressionFeature stream element
|
||||
* @throws XmlPullParserException if an exception occurs while parsing the stanza.
|
||||
*/
|
||||
public static Collection<String> parseCompressionMethods(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
List<String> methods = new ArrayList<String>();
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
public static Compress.Feature parseCompressionFeature(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
assert (parser.getEventType() == XmlPullParser.START_TAG);
|
||||
String name;
|
||||
final int initialDepth = parser.getDepth();
|
||||
List<String> methods = new LinkedList<String>();
|
||||
outerloop: while (true) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
if (elementName.equals("method")) {
|
||||
switch (eventType) {
|
||||
case XmlPullParser.START_TAG:
|
||||
name = parser.getName();
|
||||
switch (name) {
|
||||
case "method":
|
||||
methods.add(parser.nextText());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("compression")) {
|
||||
done = true;
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
name = parser.getName();
|
||||
switch (name) {
|
||||
case Compress.Feature.ELEMENT:
|
||||
if (parser.getDepth() == initialDepth) {
|
||||
break outerloop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
assert (parser.getEventType() == XmlPullParser.END_TAG);
|
||||
assert (parser.getDepth() == initialDepth);
|
||||
return new Compress.Feature(methods);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -840,21 +878,16 @@ public class PacketParserUtils {
|
|||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
public static XMPPError parseError(XmlPullParser parser) throws Exception {
|
||||
final String errorNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
|
||||
String type = null;
|
||||
String message = null;
|
||||
String condition = null;
|
||||
List<PacketExtension> extensions = new ArrayList<PacketExtension>();
|
||||
|
||||
// Parse the error header
|
||||
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||
if (parser.getAttributeName(i).equals("type")) {
|
||||
type = parser.getAttributeValue("", "type");
|
||||
}
|
||||
}
|
||||
boolean done = false;
|
||||
type = parser.getAttributeValue("", "type");
|
||||
// Parse the text and condition tags
|
||||
while (!done) {
|
||||
outerloop:
|
||||
while (true) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals(Packet.TEXT)) {
|
||||
|
@ -864,7 +897,7 @@ public class PacketParserUtils {
|
|||
// Condition tag, it can be xmpp error or an application defined error.
|
||||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
if (errorNamespace.equals(namespace)) {
|
||||
if (namespace.equals(XMPPError.NAMESPACE)) {
|
||||
condition = elementName;
|
||||
}
|
||||
else {
|
||||
|
@ -874,7 +907,7 @@ public class PacketParserUtils {
|
|||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("error")) {
|
||||
done = true;
|
||||
break outerloop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -942,6 +975,33 @@ public class PacketParserUtils {
|
|||
return extension;
|
||||
}
|
||||
|
||||
public static StartTls parseStartTlsFeature(XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
assert (parser.getEventType() == XmlPullParser.START_TAG);
|
||||
assert (parser.getNamespace().equals(StartTls.NAMESPACE));
|
||||
int initalDepth = parser.getDepth();
|
||||
boolean required = false;
|
||||
outerloop: while (true) {
|
||||
int event = parser.next();
|
||||
switch (event) {
|
||||
case XmlPullParser.START_TAG:
|
||||
String name = parser.getName();
|
||||
switch (name) {
|
||||
case "required":
|
||||
required = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
if (parser.getDepth() == initalDepth) {
|
||||
break outerloop;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(parser.getEventType() == XmlPullParser.END_TAG);
|
||||
return new StartTls(required);
|
||||
}
|
||||
|
||||
private static String getLanguageAttribute(XmlPullParser parser) {
|
||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||
String attributeName = parser.getAttributeName(i);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public class ParserUtils {
|
||||
public static void assertAtStartTag(XmlPullParser parser) throws XmlPullParserException {
|
||||
assert(parser.getEventType() == XmlPullParser.START_TAG);
|
||||
}
|
||||
|
||||
public static void assertAtEndTag(XmlPullParser parser) throws XmlPullParserException {
|
||||
assert(parser.getEventType() == XmlPullParser.END_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boolean value of an argument.
|
||||
*
|
||||
* @param parser
|
||||
* @param name
|
||||
* @return the boolean value or null of no argument of the given name exists
|
||||
*/
|
||||
public static Boolean getBooleanAttribute(XmlPullParser parser, String name) {
|
||||
String valueString = parser.getAttributeValue("", name);
|
||||
if (valueString == null)
|
||||
return null;
|
||||
valueString = valueString.toLowerCase(Locale.US);
|
||||
if (valueString.equals("true") || valueString.equals("0")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getBooleanAttribute(XmlPullParser parser, String name,
|
||||
boolean defaultValue) {
|
||||
Boolean bool = getBooleanAttribute(parser, name);
|
||||
if (bool == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
else {
|
||||
return bool;
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer getIntegerAttribute(XmlPullParser parser, String name) {
|
||||
String valueString = parser.getAttributeValue("", name);
|
||||
if (valueString == null)
|
||||
return null;
|
||||
return Integer.valueOf(valueString);
|
||||
}
|
||||
|
||||
public static int getIntegerAttribute(XmlPullParser parser, String name, int defaultValue) {
|
||||
Integer integer = getIntegerAttribute(parser, name);
|
||||
if (integer == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
else {
|
||||
return integer;
|
||||
}
|
||||
}
|
||||
|
||||
public static Long getLongAttribute(XmlPullParser parser, String name) {
|
||||
String valueString = parser.getAttributeValue("", name);
|
||||
if (valueString == null)
|
||||
return null;
|
||||
return Long.valueOf(valueString);
|
||||
}
|
||||
|
||||
public static long getLongAttribute(XmlPullParser parser, String name, long defaultValue) {
|
||||
Long l = getLongAttribute(parser, name);
|
||||
if (l == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
else {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package org.jivesoftware.smack.util;
|
||||
|
||||
import org.jivesoftware.smack.packet.Element;
|
||||
import org.jivesoftware.smack.packet.NamedElement;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
public class XmlStringBuilder implements Appendable, CharSequence {
|
||||
|
@ -33,7 +34,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
|||
prelude(pe);
|
||||
}
|
||||
|
||||
public XmlStringBuilder(Element e) {
|
||||
public XmlStringBuilder(NamedElement e) {
|
||||
this();
|
||||
halfOpenElement(e.getElementName());
|
||||
}
|
||||
|
@ -101,7 +102,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
|||
return this;
|
||||
}
|
||||
|
||||
public XmlStringBuilder closeElement(Element e) {
|
||||
public XmlStringBuilder closeElement(NamedElement e) {
|
||||
closeElement(e.getElementName());
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,10 @@ import java.util.concurrent.BlockingQueue;
|
|||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.packet.Element;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||
|
||||
/**
|
||||
* A dummy implementation of {@link XMPPConnection}, intended to be used during
|
||||
|
@ -53,7 +54,7 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
private String connectionID;
|
||||
private Roster roster;
|
||||
|
||||
private final BlockingQueue<Packet> queue = new LinkedBlockingQueue<Packet>();
|
||||
private final BlockingQueue<Element> queue = new LinkedBlockingQueue<Element>();
|
||||
|
||||
public DummyConnection() {
|
||||
this(new ConnectionConfiguration("example.com"));
|
||||
|
@ -178,6 +179,14 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
authenticated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(PlainStreamElement element) {
|
||||
if (SmackConfiguration.DEBUG_ENABLED) {
|
||||
System.out.println("[SEND]: " + element.toXML());
|
||||
}
|
||||
queue.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendPacketInternal(Packet packet) {
|
||||
if (SmackConfiguration.DEBUG_ENABLED) {
|
||||
|
@ -204,7 +213,7 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
* @throws InterruptedException
|
||||
*/
|
||||
public Packet getSentPacket() throws InterruptedException {
|
||||
return queue.poll();
|
||||
return (Packet) queue.poll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -217,7 +226,7 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
* @throws InterruptedException
|
||||
*/
|
||||
public Packet getSentPacket(int wait) throws InterruptedException {
|
||||
return queue.poll(wait, TimeUnit.SECONDS);
|
||||
return (Packet) queue.poll(wait, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,8 +69,6 @@ public class RosterVersioningTest {
|
|||
connection = new DummyConnection(conf);
|
||||
connection.connect();
|
||||
|
||||
connection.setRosterVersioningSupported();
|
||||
|
||||
connection.login("rostertest", "secret");
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,7 @@ final public class TestUtils {
|
|||
public static XmlPullParser getParser(Reader reader, String startTag) {
|
||||
XmlPullParser parser;
|
||||
try {
|
||||
parser = PacketParserUtils.newXmppParser();
|
||||
parser.setInput(reader);
|
||||
parser = PacketParserUtils.newXmppParser(reader);
|
||||
if (startTag == null) {
|
||||
return parser;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue