diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/CapsVersionAndHash.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/CapsVersionAndHash.java new file mode 100644 index 0000000000..0df925dc30 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/CapsVersionAndHash.java @@ -0,0 +1,27 @@ +/** + * + * 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.smackx.caps; + +public class CapsVersionAndHash { + public final String version; + public final String hash; + + public CapsVersionAndHash(String version, String hash) { + this.version = version; + this.hash = hash; + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index 3e3c013ccb..4a753e242d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -34,6 +34,7 @@ import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.filter.PacketExtensionFilter; +import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache; import org.jivesoftware.smackx.caps.packet.CapsExtension; @@ -76,6 +77,12 @@ public class EntityCapsManager extends Manager { public static final String ELEMENT = CapsExtension.ELEMENT; private static final Map SUPPORTED_HASHES = new HashMap(); + + /** + * The default hash. Currently 'sha-1'. + */ + private static final String DEFAULT_HASH = StringUtils.SHA1; + private static String DEFAULT_ENTITY_NODE = "http://www.igniterealtime.org/projects/smack"; protected static EntityCapsPersistentCache persistentCache; @@ -92,7 +99,7 @@ public class EntityCapsManager extends Manager { private static final PacketFilter PRESENCES = PacketTypeFilter.PRESENCE; /** - * Map of (node + '#" + hash algorithm) to DiscoverInfo data + * Map of "node + '#' + hash" to DiscoverInfo data */ private static final LruCache CAPS_CACHE = new LruCache(1000); @@ -112,8 +119,8 @@ public void connectionCreated(XMPPConnection connection) { }); try { - MessageDigest sha1MessageDigest = MessageDigest.getInstance("SHA-1"); - SUPPORTED_HASHES.put("sha-1", sha1MessageDigest); + MessageDigest sha1MessageDigest = MessageDigest.getInstance(DEFAULT_HASH); + SUPPORTED_HASHES.put(DEFAULT_HASH, sha1MessageDigest); } catch (NoSuchAlgorithmException e) { // Ignore } @@ -238,9 +245,12 @@ public static void clearMemoryCache() { } private static void addCapsExtensionInfo(String from, CapsExtension capsExtension) { - String hash = capsExtension.getHash().toLowerCase(Locale.US); - if (!SUPPORTED_HASHES.containsKey(hash)) + String capsExtensionHash = capsExtension.getHash(); + String hashInUppercase = capsExtensionHash.toUpperCase(Locale.US); + // SUPPORTED_HASHES uses the format of MessageDigest, which is uppercase, e.g. "SHA-1" instead of "sha-1" + if (!SUPPORTED_HASHES.containsKey(hashInUppercase)) return; + String hash = capsExtensionHash.toLowerCase(Locale.US); String node = capsExtension.getNode(); String ver = capsExtension.getVer(); @@ -248,12 +258,12 @@ private static void addCapsExtensionInfo(String from, CapsExtension capsExtensio JID_TO_NODEVER_CACHE.put(from, new NodeVerHash(node, ver, hash)); } - private final Queue lastLocalCapsVersions = new ConcurrentLinkedQueue(); + private final Queue lastLocalCapsVersions = new ConcurrentLinkedQueue<>(); private final ServiceDiscoveryManager sdm; private boolean entityCapsEnabled; - private String currentCapsVersion; + private CapsVersionAndHash currentCapsVersion; private boolean presenceSend = false; /** @@ -346,8 +356,8 @@ public void processPacket(Packet packet) { public void processPacket(Packet packet) { if (!entityCapsEnabled) return; - - CapsExtension caps = new CapsExtension(entityNode, getCapsVersion(), "sha-1"); + CapsVersionAndHash capsVersionAndHash = getCapsVersion(); + CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash); packet.addExtension(caps); } }; @@ -407,7 +417,7 @@ public void removeUserCapsNode(String user) { * * @return our own caps version */ - public String getCapsVersion() { + public CapsVersionAndHash getCapsVersion() { return currentCapsVersion; } @@ -464,17 +474,16 @@ public void updateLocalEntityCaps() { discoverInfo.setFrom(connection.getUser()); sdm.addDiscoverInfoTo(discoverInfo); - currentCapsVersion = generateVerificationString(discoverInfo, "sha-1"); + currentCapsVersion = generateVerificationString(discoverInfo); addDiscoverInfoByNode(entityNode + '#' + currentCapsVersion, discoverInfo); if (lastLocalCapsVersions.size() > 10) { - String oldCapsVersion = lastLocalCapsVersions.poll(); - sdm.removeNodeInformationProvider(entityNode + '#' + oldCapsVersion); + CapsVersionAndHash oldCapsVersion = lastLocalCapsVersions.poll(); + sdm.removeNodeInformationProvider(entityNode + '#' + oldCapsVersion.version); } lastLocalCapsVersions.add(currentCapsVersion); - CAPS_CACHE.put(currentCapsVersion, discoverInfo); if (connection != null) - JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion, "sha-1")); + JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion)); final List identities = new LinkedList(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities()); sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new AbstractNodeInformationProvider() { @@ -535,7 +544,7 @@ public static boolean verifyDiscoverInfoVersion(String ver, String hash, Discove if (verifyPacketExtensions(info)) return false; - String calculatedVer = generateVerificationString(info, hash); + String calculatedVer = generateVerificationString(info, hash).version; if (!ver.equals(calculatedVer)) return false; @@ -567,6 +576,10 @@ protected static boolean verifyPacketExtensions(DiscoverInfo info) { return false; } + protected static CapsVersionAndHash generateVerificationString(DiscoverInfo discoverInfo) { + return generateVerificationString(discoverInfo, null); + } + /** * Generates a XEP-115 Verification String * @@ -575,12 +588,15 @@ protected static boolean verifyPacketExtensions(DiscoverInfo info) { * * @param discoverInfo * @param hash - * the used hash function + * the used hash function, if null, default hash will be used * @return The generated verification String or null if the hash is not * supported */ - protected static String generateVerificationString(DiscoverInfo discoverInfo, String hash) { - MessageDigest md = SUPPORTED_HASHES.get(hash.toLowerCase(Locale.US)); + protected static CapsVersionAndHash generateVerificationString(DiscoverInfo discoverInfo, String hash) { + if (hash == null) { + hash = DEFAULT_HASH; + } + MessageDigest md = SUPPORTED_HASHES.get(hash.toUpperCase(Locale.US)); if (md == null) return null; @@ -682,7 +698,8 @@ public int compare(FormField f1, FormField f2) { synchronized(md) { digest = md.digest(sb.toString().getBytes()); } - return Base64.encodeToString(digest); + String version = Base64.encodeToString(digest); + return new CapsVersionAndHash(version, hash); } private static void formFieldValuesToCaps(List i, StringBuilder sb) { @@ -702,6 +719,10 @@ public static class NodeVerHash { private String ver; private String nodeVer; + NodeVerHash(String node, CapsVersionAndHash capsVersionAndHash) { + this(node, capsVersionAndHash.version, capsVersionAndHash.hash); + } + NodeVerHash(String node, String ver, String hash) { this.node = node; this.ver = ver; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java index 8da42f9e13..bfa00f9ab6 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/caps/EntityCapsManagerTest.java @@ -27,6 +27,7 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.Base32; import org.jivesoftware.smack.util.stringencoder.StringEncoder; import org.jivesoftware.smackx.InitExtensions; @@ -54,8 +55,8 @@ public void initSmackTestSuite() { public void testComplexGenerationExample() { DiscoverInfo di = createComplexSamplePacket(); - String ver = EntityCapsManager.generateVerificationString(di, "sha-1"); - assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", ver); + CapsVersionAndHash versionAndHash = EntityCapsManager.generateVerificationString(di, StringUtils.SHA1); + assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", versionAndHash.version); } @Test @@ -82,7 +83,8 @@ private void testSimpleDirectoryCache(StringEncoder stringEncoder) throws IOExce EntityCapsManager.setPersistentCache(cache); DiscoverInfo di = createComplexSamplePacket(); - String nodeVer = di.getNode() + "#" + EntityCapsManager.generateVerificationString(di, "sha-1"); + CapsVersionAndHash versionAndHash = EntityCapsManager.generateVerificationString(di, StringUtils.SHA1); + String nodeVer = di.getNode() + "#" + versionAndHash.version; // Save the data in EntityCapsManager EntityCapsManager.addDiscoverInfoByNode(nodeVer, di);