Merge branch '4.2'

This commit is contained in:
Florian Schmaus 2017-10-14 14:56:36 +02:00
commit 384c285fbc
20 changed files with 303 additions and 79 deletions

View File

@ -396,6 +396,12 @@ subprojects {
}
clirr {
// 2018-08-14: Disabled Clirr because
// - It reports an breaking change in android.jar (seems right, but there is nothing we can do about it)
// - Only the first smack-* projects are correctly checked,
// the other ones have the output of a clirr report from a previous project
// (Look at the clirr reports).
enabled false
semver false
}
}

18
repl
View File

@ -3,15 +3,29 @@ set -e
set -u
set -o pipefail
JDWP=false
JDWP_PORT=8000
while getopts d OPTION "$@"; do
while getopts djp: OPTION "$@"; do
case $OPTION in
d)
set -x
;;
j)
JDWP=true
;;
p)
JDWP_PORT=$OPTARG
;;
esac
done
EXTRA_JAVA_ARGS=()
if $JDWP; then
EXTRA_JAVA_ARGS+=("-Xdebug")
EXTRA_JAVA_ARGS+=("-Xrunjdwp:server=y,transport=dt_socket,address=${JDWP_PORT},suspend=n")
fi
PROJECT_ROOT=$(dirname "${BASH_SOURCE[0]}")
cd "${PROJECT_ROOT}"
@ -27,7 +41,7 @@ GRADLE_CLASSPATH="$(gradle :smack-repl:printClasspath --quiet |\
tail -n1)"
echo "Finished, starting REPL"
java \
java "${EXTRA_JAVA_ARGS[@]}" \
-Dscala.usejavacp=true \
-classpath "${GRADLE_CLASSPATH}" \
ammonite.Main \

View File

@ -43,7 +43,10 @@ import org.jivesoftware.smack.util.Async;
* </ol>
*
* {@link ReconnectionPolicy#FIXED_DELAY} - The reconnection mechanism will try to reconnect after a fixed delay
* independently from the number of reconnection attempts already performed
* independently from the number of reconnection attempts already performed.
* <p>
* Interrupting the reconnection thread will abort the reconnection mechanism.
* </p>
*
* @author Francisco Vives
* @author Luca Stucchi
@ -163,7 +166,7 @@ public final class ReconnectionManager {
private ReconnectionManager(AbstractXMPPConnection connection) {
weakRefConnection = new WeakReference<AbstractXMPPConnection>(connection);
reconnectionRunnable = new Thread() {
reconnectionRunnable = new Runnable() {
/**
* Holds the current number of reconnection attempts
@ -211,6 +214,10 @@ public final class ReconnectionManager {
if (connection == null) {
return;
}
// Reset attempts to zero since a new reconnection cycle is started once this runs.
attempts = 0;
// The process will try to reconnect until the connection is established or
// the user cancel the reconnection process AbstractXMPPConnection.disconnect().
while (isReconnectionPossible(connection)) {
@ -219,7 +226,10 @@ public final class ReconnectionManager {
// Sleep until we're ready for the next reconnection attempt. Notify
// listeners once per second about how much time remains before the next
// reconnection attempt.
while (isReconnectionPossible(connection) && remainingSeconds > 0) {
while (remainingSeconds > 0) {
if (!isReconnectionPossible(connection)) {
return;
}
try {
Thread.sleep(1000);
remainingSeconds--;
@ -228,8 +238,9 @@ public final class ReconnectionManager {
}
}
catch (InterruptedException e) {
LOGGER.log(Level.FINE, "waiting for reconnection interrupted", e);
break;
LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e);
// Exit the reconnection thread in case it was interrupted.
return;
}
}
@ -237,24 +248,18 @@ public final class ReconnectionManager {
listener.reconnectingIn(0);
}
if (!isReconnectionPossible(connection)) {
return;
}
// Makes a reconnection attempt
try {
if (isReconnectionPossible(connection)) {
try {
connection.connect();
} catch (SmackException.AlreadyConnectedException e) {
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
}
try {
connection.connect();
}
// TODO Starting with Smack 4.2, connect() will no
// longer login automatically. So change this and the
// previous lines to connection.connect().login() in the
// 4.2, or any later, branch.
if (!connection.isAuthenticated()) {
connection.login();
catch (SmackException.AlreadyConnectedException e) {
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
}
// Successfully reconnected.
attempts = 0;
connection.login();
}
catch (SmackException.AlreadyLoggedInException e) {
// This can happen if another thread concurrently triggers a reconnection
@ -262,12 +267,21 @@ public final class ReconnectionManager {
// failure. See also SMACK-725.
LOGGER.log(Level.FINER, "Reconnection not required, was already logged in", e);
}
catch (SmackException | IOException | XMPPException | InterruptedException e) {
catch (SmackException | IOException | XMPPException e) {
// Fires the failed reconnection notification
for (ConnectionListener listener : connection.connectionListeners) {
listener.reconnectionFailed(e);
}
// Failed to reconnect, try again.
continue;
} catch (InterruptedException e) {
LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e);
// Exit the reconnection thread in case it was interrupted.
return;
}
// Successfully reconnected .
return;
}
}
};
@ -314,7 +328,7 @@ public final class ReconnectionManager {
*
* @return true, if the reconnection mechanism is enabled.
*/
public boolean isAutomaticReconnectEnabled() {
public synchronized boolean isAutomaticReconnectEnabled() {
return automaticReconnectEnabled;
}
@ -348,6 +362,20 @@ public final class ReconnectionManager {
"Smack Reconnection Manager (" + connection.getConnectionCounter() + ')');
}
/**
* Abort a possibly running reconnection mechanism.
*
* @since 4.2.2
*/
public synchronized void abortPossiblyRunningReconnection() {
if (reconnectionThread == null) {
return;
}
reconnectionThread.interrupt();
reconnectionThread = null;
}
private final ConnectionListener connectionListener = new AbstractConnectionListener() {
@Override

View File

@ -184,7 +184,7 @@ public final class SmackConfiguration {
}
}
/**
/**
* Add a Collection of SASL mechanisms to the list to be used.
*
* @param mechs the Collection of SASL mechanisms to be added
@ -204,7 +204,7 @@ public final class SmackConfiguration {
defaultMechs.remove(mech);
}
/**
/**
* Remove a Collection of SASL mechanisms to the list to be used.
*
* @param mechs the Collection of SASL mechanisms to be removed
@ -339,7 +339,7 @@ public final class SmackConfiguration {
return defaultHostnameVerififer;
}
enum UnknownIqRequestReplyMode {
public enum UnknownIqRequestReplyMode {
doNotReply,
replyFeatureNotImplemented,
replyServiceUnavailable,

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.XmlStringBuilder;
@ -85,6 +86,7 @@ public class AbstractError {
* @return the descriptive text or null.
*/
public String getDescriptiveText(String xmllang) {
Objects.requireNonNull(xmllang, "xmllang must not be null");
return descriptiveTexts.get(xmllang);
}
@ -105,7 +107,8 @@ public class AbstractError {
String xmllang = entry.getKey();
String text = entry.getValue();
xml.halfOpenElement("text").xmlnsAttribute(textNamespace)
.xmllangAttribute(xmllang).rightAngleBracket();
.optXmlLangAttribute(xmllang)
.rightAngleBracket();
xml.escape(text);
xml.closeElement("text");
}
@ -120,6 +123,15 @@ public class AbstractError {
protected List<ExtensionElement> extensions;
public B setDescriptiveTexts(Map<String, String> descriptiveTexts) {
if (descriptiveTexts == null) {
this.descriptiveTexts = null;
return getThis();
}
for (String key : descriptiveTexts.keySet()) {
if (key == null) {
throw new IllegalArgumentException("descriptiveTexts cannot contain null key");
}
}
if (this.descriptiveTexts == null) {
this.descriptiveTexts = descriptiveTexts;
}

View File

@ -38,14 +38,14 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* <tr><td>conflict</td><td>CANCEL</td><td>8.3.3.2</td></tr>
* <tr><td>feature-not-implemented</td><td>CANCEL</td><td>8.3.3.3</td></tr>
* <tr><td>forbidden</td><td>AUTH</td><td>8.3.3.4</td></tr>
* <tr><td>gone</td><td>MODIFY</td><td>8.3.3.5</td></tr>
* <tr><td>gone</td><td>CANCEL</td><td>8.3.3.5</td></tr>
* <tr><td>internal-server-error</td><td>WAIT</td><td>8.3.3.6</td></tr>
* <tr><td>item-not-found</td><td>CANCEL</td><td>8.3.3.7</td></tr>
* <tr><td>jid-malformed</td><td>MODIFY</td><td>8.3.3.8</td></tr>
* <tr><td>not-acceptable</td><td> MODIFY</td><td>8.3.3.9</td></tr>
* <tr><td>not-acceptable</td><td>MODIFY</td><td>8.3.3.9</td></tr>
* <tr><td>not-allowed</td><td>CANCEL</td><td>8.3.3.10</td></tr>
* <tr><td>not-authorized</td><td>AUTH</td><td>8.3.3.11</td></tr>
* <tr><td>policy-violation</td><td>AUTH</td><td>8.3.3.12</td></tr>
* <tr><td>policy-violation</td><td>MODIFY</td><td>8.3.3.12</td></tr>
* <tr><td>recipient-unavailable</td><td>WAIT</td><td>8.3.3.13</td></tr>
* <tr><td>redirect</td><td>MODIFY</td><td>8.3.3.14</td></tr>
* <tr><td>registration-required</td><td>AUTH</td><td>8.3.3.15</td></tr>
@ -54,7 +54,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
* <tr><td>resource-constraint</td><td>WAIT</td><td>8.3.3.18</td></tr>
* <tr><td>service-unavailable</td><td>CANCEL</td><td>8.3.3.19</td></tr>
* <tr><td>subscription-required</td><td>AUTH</td><td>8.3.3.20</td></tr>
* <tr><td>undefined-condition</td><td>WAIT</td><td>8.3.3.21</td></tr>
* <tr><td>undefined-condition</td><td>MODIFY</td><td>8.3.3.21</td></tr>
* <tr><td>unexpected-request</td><td>WAIT</td><td>8.3.3.22</td></tr>
* </table>
*
@ -69,7 +69,7 @@ public class XMPPError extends AbstractError {
public static final String ERROR = "error";
private static final Logger LOGGER = Logger.getLogger(XMPPError.class.getName());
private static final Map<Condition, Type> CONDITION_TO_TYPE = new HashMap<Condition, Type>();
static final Map<Condition, Type> CONDITION_TO_TYPE = new HashMap<Condition, Type>();
static {
CONDITION_TO_TYPE.put(Condition.bad_request, Type.MODIFY);
@ -90,9 +90,10 @@ public class XMPPError extends AbstractError {
CONDITION_TO_TYPE.put(Condition.remote_server_not_found, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.remote_server_timeout, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.resource_constraint, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.CANCEL);
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.AUTH);
CONDITION_TO_TYPE.put(Condition.undefined_condition, Type.MODIFY);
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.WAIT);
}
private final Condition condition;

View File

@ -749,6 +749,12 @@ public class PacketParserUtils {
descriptiveTexts = new HashMap<String, String>();
}
String xmllang = getLanguageAttribute(parser);
if (xmllang == null) {
// XMPPError assumes the default locale, 'en', or the empty string.
// Establish the invariant that there is never null as a key.
xmllang = "";
}
String text = parser.nextText();
String previousValue = descriptiveTexts.put(xmllang, text);
assert (previousValue == null);

View File

@ -361,7 +361,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
}
public XmlStringBuilder optXmlLangAttribute(String lang) {
if (lang != null) {
if (!StringUtils.isNullOrEmpty(lang)) {
xmllangAttribute(lang);
}
return this;

View File

@ -59,6 +59,20 @@ public abstract class DNSResolver {
return new HostAddress(name, port, inetAddresses);
}
/**
* Lookup the IP addresses of a given host name. Returns <code>null</code> if there was an error, in which the error
* reason will be added in form of a <code>HostAddress</code> to <code>failedAddresses</code>. Returns a empty list
* in case the DNS name exists but has no associated A or AAAA resource records. Otherwise, if the resolution was
* successful <em>and</em> there is at least one A or AAAA resource record, then a non-empty list will be returned.
* <p>
* Concrete DNS resolver implementations are free to overwrite this, but have to stick to the interface contract.
* </p>
*
* @param name the DNS name to lookup
* @param failedAddresses a list with the failed addresses
* @param dnssecMode the selected DNSSEC mode
* @return A list, either empty or non-empty, or <code>null</code>
*/
protected List<InetAddress> lookupHostAddress0(String name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
// Default implementation of a DNS name lookup for A/AAAA records. It is assumed that this method does never
// support DNSSEC. Subclasses are free to override this method.

View File

@ -0,0 +1,35 @@
/**
*
* Copyright © 2017 Ingo Bauersachs
*
* 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 static org.jivesoftware.smack.packet.XMPPError.Condition;
import static org.jivesoftware.smack.packet.XMPPError.Type;
import static org.junit.Assert.assertEquals;
import java.util.Map;
import org.junit.Test;
public class XMPPErrorTest {
@Test
public void testConditionHasDefaultTypeMapping() throws NoSuchFieldException, IllegalAccessException {
Map<Condition, Type> conditionToTypeMap = XMPPError.CONDITION_TO_TYPE;
assertEquals("CONDITION_TO_TYPE map is likely out of sync with Condition enum",
Condition.values().length,
conditionToTypeMap.size());
}
}

View File

@ -25,7 +25,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.FactoryConfigurationError;
@ -35,6 +37,7 @@ import javax.xml.transform.TransformerException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
@ -867,4 +870,44 @@ public class PacketParserUtilsTest {
return otherLanguage;
}
@Test(expected = IllegalArgumentException.class)
public void descriptiveTextNullLangPassedMap() throws Exception {
final String text = "Dummy descriptive text";
Map<String, String> texts = new HashMap<>();
texts.put(null, text);
XMPPError
.getBuilder(XMPPError.Condition.internal_server_error)
.setDescriptiveTexts(texts)
.build();
}
@Test
public void ensureNoEmptyLangInDescriptiveText() throws Exception {
final String text = "Dummy descriptive text";
Map<String, String> texts = new HashMap<>();
texts.put("", text);
XMPPError error = XMPPError
.getBuilder(XMPPError.Condition.internal_server_error)
.setDescriptiveTexts(texts)
.build();
final String errorXml = XMLBuilder
.create(XMPPError.ERROR).a("type", "cancel").up()
.element("internal-server-error", XMPPError.NAMESPACE).up()
.element("text", XMPPError.NAMESPACE).t(text).up()
.asString();
XmlUnitUtils.assertSimilar(errorXml, error.toXML());
}
@Test
public void ensureNoNullLangInParsedDescriptiveTexts() throws Exception {
final String text = "Dummy descriptive text";
final String errorXml = XMLBuilder
.create(XMPPError.ERROR).a("type", "cancel").up()
.element("internal-server-error", XMPPError.NAMESPACE).up()
.element("text", XMPPError.NAMESPACE).t(text).up()
.asString();
XmlPullParser parser = TestUtils.getParser(errorXml);
XMPPError error = PacketParserUtils.parseError(parser).build();
assertEquals(text, error.getDescriptiveText());
}
}

View File

@ -87,13 +87,32 @@ public final class PushNotificationsManager extends Manager {
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @deprecated Use {@link #isSupported()} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.3
public boolean isSupportedByServer()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return ServiceDiscoveryManager.getInstanceFor(connection())
.serverSupportsFeature(PushNotificationsElements.NAMESPACE);
}
/**
* Returns true if Push Notifications are supported by this account.
*
* @return true if Push Notifications are supported by this account.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.2.2
*/
public boolean isSupported()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return ServiceDiscoveryManager.getInstanceFor(connection()).accountSupportsFeatures(
PushNotificationsElements.NAMESPACE);
}
/**
* Enable push notifications.
*

View File

@ -52,6 +52,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.util.cache.Cache;
import org.jxmpp.util.cache.ExpirationCache;
@ -680,6 +681,41 @@ public final class ServiceDiscoveryManager extends Manager {
return supportsFeatures(connection().getXMPPServiceDomain(), features);
}
/**
* Check if the given features are supported by the connection account. This means that the discovery information
* lookup will be performed on the bare JID of the connection managed by this ServiceDiscoveryManager.
*
* @param features the features to check
* @return <code>true</code> if all features are supported by the connection account, <code>false</code> otherwise
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.2.2
*/
public boolean accountSupportsFeatures(CharSequence... features)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return accountSupportsFeatures(Arrays.asList(features));
}
/**
* Check if the given collection of features are supported by the connection account. This means that the discovery
* information lookup will be performed on the bare JID of the connection managed by this ServiceDiscoveryManager.
*
* @param features a collection of features
* @return <code>true</code> if all features are supported by the connection account, <code>false</code> otherwise
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.2.2
*/
public boolean accountSupportsFeatures(Collection<? extends CharSequence> features)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
EntityBareJid accountJid = connection().getUser().asEntityBareJid();
return supportsFeatures(accountJid, features);
}
/**
* Queries the remote entity for it's features and returns true if the given feature is found.
*

View File

@ -215,7 +215,7 @@ public final class Roster extends Manager {
* Returns the default subscription processing mode to use when a new Roster is created. The
* subscription processing mode dictates what action Smack will take when subscription
* requests from other users are made. The default subscription mode
* is {@link SubscriptionMode#accept_all}.
* is {@link SubscriptionMode#reject_all}.
*
* @return the default subscription mode to use for new Rosters
*/
@ -227,7 +227,7 @@ public final class Roster extends Manager {
* Sets the default subscription processing mode to use when a new Roster is created. The
* subscription processing mode dictates what action Smack will take when subscription
* requests from other users are made. The default subscription mode
* is {@link SubscriptionMode#accept_all}.
* is {@link SubscriptionMode#reject_all}.
*
* @param subscriptionMode the default subscription mode to use for new Rosters.
*/
@ -385,7 +385,7 @@ public final class Roster extends Manager {
/**
* Returns the subscription processing mode, which dictates what action
* Smack will take when subscription requests from other users are made.
* The default subscription mode is {@link SubscriptionMode#accept_all}.
* The default subscription mode is {@link SubscriptionMode#reject_all}.
* <p>
* If using the manual mode, a PacketListener should be registered that
* listens for Presence packets that have a type of
@ -401,7 +401,7 @@ public final class Roster extends Manager {
/**
* Sets the subscription processing mode, which dictates what action
* Smack will take when subscription requests from other users are made.
* The default subscription mode is {@link SubscriptionMode#accept_all}.
* The default subscription mode is {@link SubscriptionMode#reject_all}.
* <p>
* If using the manual mode, a PacketListener should be registered that
* listens for Presence packets that have a type of
@ -1409,14 +1409,14 @@ public final class Roster extends Manager {
public enum SubscriptionMode {
/**
* Automatically accept all subscription and unsubscription requests. This is
* the default mode and is suitable for simple client. More complex client will
* Automatically accept all subscription and unsubscription requests.
* This is suitable for simple clients. More complex clients will
* likely wish to handle subscription requests manually.
*/
accept_all,
/**
* Automatically reject all subscription requests.
* Automatically reject all subscription requests. This is the default mode.
*/
reject_all,

View File

@ -22,6 +22,7 @@ import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNotSame;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.deletePath;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager;
@ -154,6 +155,8 @@ public class OmemoStoreTest extends AbstractOmemoIntegrationTest {
@Override
public void after() {
cleanServerSideTraces(alice);
cleanServerSideTraces(bob);
alice.shutdown();
bob.shutdown();
}

View File

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.omemo;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.BODY_OMEMO_HINT;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@ -132,6 +133,9 @@ public final class OmemoManager extends Manager {
}
});
PEPManager.getInstanceFor(connection).addPEPListener(deviceListUpdateListener);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY);
service = OmemoService.getInstance();
}
@ -810,16 +814,23 @@ public final class OmemoManager extends Manager {
Set<Integer> deviceListIds = omemoDeviceListElement.copyDeviceIds();
//enroll at the deviceList
deviceListIds.add(ourDeviceId);
omemoDeviceListElement = new OmemoDeviceListVAxolotlElement(deviceListIds);
final OmemoDeviceListVAxolotlElement newOmemoDeviceListElement = new OmemoDeviceListVAxolotlElement(deviceListIds);
try {
OmemoService.publishDeviceIds(OmemoManager.this, omemoDeviceListElement);
} catch (SmackException | InterruptedException | XMPPException.XMPPErrorException e) {
//TODO: It might be dangerous NOT to retry publishing our deviceId
LOGGER.log(Level.SEVERE,
"Could not publish our device list after an update without our id was received: "
+ e.getMessage());
}
// PEPListener is a synchronous listener. Avoid any deadlocks by using an async task to update the device list.
Async.go(new Runnable() {
@Override
public void run() {
try {
OmemoService.publishDeviceIds(OmemoManager.this, newOmemoDeviceListElement);
}
catch (SmackException | InterruptedException | XMPPException.XMPPErrorException e) {
// TODO: It might be dangerous NOT to retry publishing our deviceId
LOGGER.log(Level.SEVERE,
"Could not publish our device list after an update without our id was received: "
+ e.getMessage());
}
}
});
}
}
}

View File

@ -19,7 +19,6 @@ package org.jivesoftware.smackx.omemo;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
@ -55,7 +54,6 @@ import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.carbons.CarbonCopyReceivedListener;
import org.jivesoftware.smackx.carbons.CarbonManager;
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.mam.MamManager;
import org.jivesoftware.smackx.muc.MultiUserChat;
@ -79,7 +77,6 @@ import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
import org.jivesoftware.smackx.omemo.internal.OmemoSession;
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
import org.jivesoftware.smackx.pep.PEPManager;
import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.PubSubException;
@ -228,7 +225,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
publishDeviceIdIfNeeded(omemoManager, false, mustPublishId);
publishBundle(omemoManager);
subscribeToDeviceLists(omemoManager);
registerOmemoMessageStanzaListeners(omemoManager); //Wait for new OMEMO messages
getOmemoStoreBackend().initializeOmemoSessions(omemoManager); //Preload existing OMEMO sessions
}
@ -588,16 +584,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
return new OmemoDeviceListVAxolotlElement(emptySet);
}
/**
* Subscribe to the device lists of our contacts using PEP.
*
* @param omemoManager omemoManager we want to subscribe with
*/
private static void subscribeToDeviceLists(OmemoManager omemoManager) {
registerDeviceListListener(omemoManager);
ServiceDiscoveryManager.getInstanceFor(omemoManager.getConnection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY);
}
/**
* Build sessions for all devices of the contact that we do not have a session with yet.
*
@ -694,16 +680,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
*/
protected abstract void processBundle(OmemoManager omemoManager, T_Bundle bundle, OmemoDevice device) throws CorruptedOmemoKeyException;
/**
* Register a PEPListener that listens for deviceList updates.
*
* @param omemoManager omemoManager we want to register with.
*/
private static void registerDeviceListListener(final OmemoManager omemoManager) {
PEPManager.getInstanceFor(omemoManager.getConnection()).removePEPListener(omemoManager.deviceListUpdateListener);
PEPManager.getInstanceFor(omemoManager.getConnection()).addPEPListener(omemoManager.deviceListUpdateListener);
}
/**
* Process a received message. Try to decrypt it in case we are a recipient device. If we are not a recipient
* device, return null.

View File

@ -19,6 +19,7 @@ package org.jivesoftware.smack.util.dns.dnsjava;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.initializer.SmackInitializer;
@ -73,7 +74,13 @@ public class DNSJavaResolver extends DNSResolver implements SmackInitializer {
int weight = srvRecord.getWeight();
List<InetAddress> hostAddresses = lookupHostAddress0(host, failedAddresses, dnssecMode);
if (hostAddresses == null) {
if (hostAddresses == null || hostAddresses.isEmpty()) {
// If hostAddresses is not null but empty, then the DNS resolution was successful but the domain did not
// have any A or AAAA resource records.
if (hostAddresses.isEmpty()) {
LOGGER.log(Level.INFO, "The DNS name " + name + ", points to a hostname (" + host
+ ") which has neither A or AAAA resource records. This is an indication of a broken DNS setup.");
}
continue;
}

View File

@ -111,7 +111,13 @@ public class JavaxResolver extends DNSResolver implements SmackInitializer {
String host = srvRecordEntries[srvRecordEntries.length - 1];
List<InetAddress> hostAddresses = lookupHostAddress0(host, failedAddresses, dnssecMode);
if (hostAddresses == null) {
if (hostAddresses == null || hostAddresses.isEmpty()) {
// If hostAddresses is not null but empty, then the DNS resolution was successful but the domain did not
// have any A or AAAA resource records.
if (hostAddresses.isEmpty()) {
LOGGER.log(Level.INFO, "The DNS name " + name + ", points to a hostname (" + host
+ ") which has neither A or AAAA resource records. This is an indication of a broken DNS setup.");
}
continue;
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.initializer.SmackInitializer;
@ -90,7 +91,13 @@ public class MiniDnsResolver extends DNSResolver implements SmackInitializer {
for (SRV srv : result.getAnswers()) {
String hostname = srv.name.ace;
List<InetAddress> hostAddresses = lookupHostAddress0(hostname, failedAddresses, dnssecMode);
if (hostAddresses == null) {
if (hostAddresses == null || hostAddresses.isEmpty()) {
// If hostAddresses is not null but empty, then the DNS resolution was successful but the domain did not
// have any A or AAAA resource records.
if (hostAddresses.isEmpty()) {
LOGGER.log(Level.INFO, "The DNS name " + name + ", points to a hostname (" + hostname
+ ") which has neither A or AAAA resource records. This is an indication of a broken DNS setup.");
}
continue;
}