1
0
Fork 0
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:
Florian Schmaus 2014-09-11 09:49:16 +02:00
parent 07c10a7444
commit fc51f3df48
69 changed files with 3277 additions and 1083 deletions

View file

@ -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));

View file

@ -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>();

View file

@ -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();
}
}

View file

@ -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 {

View file

@ -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,
}
}

View file

@ -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();
}

View file

@ -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.

View file

@ -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;
}
}
}

View file

@ -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 + "'/>";
}
}

View file

@ -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();

View file

@ -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()
+ ":"

View file

@ -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 + "'/>";
}
}
}

View file

@ -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);
}
}

View file

@ -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.
*/

View file

@ -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 {
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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.

View file

@ -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.

View file

@ -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 {
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 {
}

View file

@ -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:

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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));

View file

@ -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);

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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);
}
/**

View file

@ -69,8 +69,6 @@ public class RosterVersioningTest {
connection = new DummyConnection(conf);
connection.connect();
connection.setRosterVersioningSupported();
connection.login("rostertest", "secret");
}

View file

@ -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;
}