mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 08:12:05 +01:00
Introduce CapsVersionAndHash
Entity Capability versions are useless without the information which hash algorithm was used to calculate those. Right now, only 'sha-1' is used, but this may change in the feature. This commit makes the first steps preparing for such a feature. Also fixes a minor bug: - CAPS_CACHE.put(currentCapsVersion, discoverInfo); currentCapsVersion is not a valid key for the cache, as it does cache "node + '#' + ver" to disco infos.
This commit is contained in:
parent
96f8bee78e
commit
5dd97a363c
3 changed files with 73 additions and 23 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import org.jivesoftware.smack.filter.PacketFilter;
|
||||||
import org.jivesoftware.smack.filter.AndFilter;
|
import org.jivesoftware.smack.filter.AndFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketExtensionFilter;
|
import org.jivesoftware.smack.filter.PacketExtensionFilter;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
||||||
import org.jivesoftware.smackx.caps.packet.CapsExtension;
|
import org.jivesoftware.smackx.caps.packet.CapsExtension;
|
||||||
|
@ -76,6 +77,12 @@ public class EntityCapsManager extends Manager {
|
||||||
public static final String ELEMENT = CapsExtension.ELEMENT;
|
public static final String ELEMENT = CapsExtension.ELEMENT;
|
||||||
|
|
||||||
private static final Map<String, MessageDigest> SUPPORTED_HASHES = new HashMap<String, MessageDigest>();
|
private static final Map<String, MessageDigest> SUPPORTED_HASHES = new HashMap<String, MessageDigest>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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";
|
private static String DEFAULT_ENTITY_NODE = "http://www.igniterealtime.org/projects/smack";
|
||||||
|
|
||||||
protected static EntityCapsPersistentCache persistentCache;
|
protected static EntityCapsPersistentCache persistentCache;
|
||||||
|
@ -92,7 +99,7 @@ public class EntityCapsManager extends Manager {
|
||||||
private static final PacketFilter PRESENCES = PacketTypeFilter.PRESENCE;
|
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<String, DiscoverInfo> CAPS_CACHE = new LruCache<String, DiscoverInfo>(1000);
|
private static final LruCache<String, DiscoverInfo> CAPS_CACHE = new LruCache<String, DiscoverInfo>(1000);
|
||||||
|
|
||||||
|
@ -112,8 +119,8 @@ public class EntityCapsManager extends Manager {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MessageDigest sha1MessageDigest = MessageDigest.getInstance("SHA-1");
|
MessageDigest sha1MessageDigest = MessageDigest.getInstance(DEFAULT_HASH);
|
||||||
SUPPORTED_HASHES.put("sha-1", sha1MessageDigest);
|
SUPPORTED_HASHES.put(DEFAULT_HASH, sha1MessageDigest);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
|
@ -238,9 +245,12 @@ public class EntityCapsManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addCapsExtensionInfo(String from, CapsExtension capsExtension) {
|
private static void addCapsExtensionInfo(String from, CapsExtension capsExtension) {
|
||||||
String hash = capsExtension.getHash().toLowerCase(Locale.US);
|
String capsExtensionHash = capsExtension.getHash();
|
||||||
if (!SUPPORTED_HASHES.containsKey(hash))
|
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;
|
return;
|
||||||
|
String hash = capsExtensionHash.toLowerCase(Locale.US);
|
||||||
|
|
||||||
String node = capsExtension.getNode();
|
String node = capsExtension.getNode();
|
||||||
String ver = capsExtension.getVer();
|
String ver = capsExtension.getVer();
|
||||||
|
@ -248,12 +258,12 @@ public class EntityCapsManager extends Manager {
|
||||||
JID_TO_NODEVER_CACHE.put(from, new NodeVerHash(node, ver, hash));
|
JID_TO_NODEVER_CACHE.put(from, new NodeVerHash(node, ver, hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Queue<String> lastLocalCapsVersions = new ConcurrentLinkedQueue<String>();
|
private final Queue<CapsVersionAndHash> lastLocalCapsVersions = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
private final ServiceDiscoveryManager sdm;
|
private final ServiceDiscoveryManager sdm;
|
||||||
|
|
||||||
private boolean entityCapsEnabled;
|
private boolean entityCapsEnabled;
|
||||||
private String currentCapsVersion;
|
private CapsVersionAndHash currentCapsVersion;
|
||||||
private boolean presenceSend = false;
|
private boolean presenceSend = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,8 +356,8 @@ public class EntityCapsManager extends Manager {
|
||||||
public void processPacket(Packet packet) {
|
public void processPacket(Packet packet) {
|
||||||
if (!entityCapsEnabled)
|
if (!entityCapsEnabled)
|
||||||
return;
|
return;
|
||||||
|
CapsVersionAndHash capsVersionAndHash = getCapsVersion();
|
||||||
CapsExtension caps = new CapsExtension(entityNode, getCapsVersion(), "sha-1");
|
CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash);
|
||||||
packet.addExtension(caps);
|
packet.addExtension(caps);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -407,7 +417,7 @@ public class EntityCapsManager extends Manager {
|
||||||
*
|
*
|
||||||
* @return our own caps version
|
* @return our own caps version
|
||||||
*/
|
*/
|
||||||
public String getCapsVersion() {
|
public CapsVersionAndHash getCapsVersion() {
|
||||||
return currentCapsVersion;
|
return currentCapsVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,17 +474,16 @@ public class EntityCapsManager extends Manager {
|
||||||
discoverInfo.setFrom(connection.getUser());
|
discoverInfo.setFrom(connection.getUser());
|
||||||
sdm.addDiscoverInfoTo(discoverInfo);
|
sdm.addDiscoverInfoTo(discoverInfo);
|
||||||
|
|
||||||
currentCapsVersion = generateVerificationString(discoverInfo, "sha-1");
|
currentCapsVersion = generateVerificationString(discoverInfo);
|
||||||
addDiscoverInfoByNode(entityNode + '#' + currentCapsVersion, discoverInfo);
|
addDiscoverInfoByNode(entityNode + '#' + currentCapsVersion, discoverInfo);
|
||||||
if (lastLocalCapsVersions.size() > 10) {
|
if (lastLocalCapsVersions.size() > 10) {
|
||||||
String oldCapsVersion = lastLocalCapsVersions.poll();
|
CapsVersionAndHash oldCapsVersion = lastLocalCapsVersions.poll();
|
||||||
sdm.removeNodeInformationProvider(entityNode + '#' + oldCapsVersion);
|
sdm.removeNodeInformationProvider(entityNode + '#' + oldCapsVersion.version);
|
||||||
}
|
}
|
||||||
lastLocalCapsVersions.add(currentCapsVersion);
|
lastLocalCapsVersions.add(currentCapsVersion);
|
||||||
|
|
||||||
CAPS_CACHE.put(currentCapsVersion, discoverInfo);
|
|
||||||
if (connection != null)
|
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<Identity> identities = new LinkedList<Identity>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities());
|
final List<Identity> identities = new LinkedList<Identity>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities());
|
||||||
sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new AbstractNodeInformationProvider() {
|
sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new AbstractNodeInformationProvider() {
|
||||||
|
@ -535,7 +544,7 @@ public class EntityCapsManager extends Manager {
|
||||||
if (verifyPacketExtensions(info))
|
if (verifyPacketExtensions(info))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
String calculatedVer = generateVerificationString(info, hash);
|
String calculatedVer = generateVerificationString(info, hash).version;
|
||||||
|
|
||||||
if (!ver.equals(calculatedVer))
|
if (!ver.equals(calculatedVer))
|
||||||
return false;
|
return false;
|
||||||
|
@ -567,6 +576,10 @@ public class EntityCapsManager extends Manager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static CapsVersionAndHash generateVerificationString(DiscoverInfo discoverInfo) {
|
||||||
|
return generateVerificationString(discoverInfo, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a XEP-115 Verification String
|
* Generates a XEP-115 Verification String
|
||||||
*
|
*
|
||||||
|
@ -575,12 +588,15 @@ public class EntityCapsManager extends Manager {
|
||||||
*
|
*
|
||||||
* @param discoverInfo
|
* @param discoverInfo
|
||||||
* @param hash
|
* @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
|
* @return The generated verification String or null if the hash is not
|
||||||
* supported
|
* supported
|
||||||
*/
|
*/
|
||||||
protected static String generateVerificationString(DiscoverInfo discoverInfo, String hash) {
|
protected static CapsVersionAndHash generateVerificationString(DiscoverInfo discoverInfo, String hash) {
|
||||||
MessageDigest md = SUPPORTED_HASHES.get(hash.toLowerCase(Locale.US));
|
if (hash == null) {
|
||||||
|
hash = DEFAULT_HASH;
|
||||||
|
}
|
||||||
|
MessageDigest md = SUPPORTED_HASHES.get(hash.toUpperCase(Locale.US));
|
||||||
if (md == null)
|
if (md == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -682,7 +698,8 @@ public class EntityCapsManager extends Manager {
|
||||||
synchronized(md) {
|
synchronized(md) {
|
||||||
digest = md.digest(sb.toString().getBytes());
|
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<String> i, StringBuilder sb) {
|
private static void formFieldValuesToCaps(List<String> i, StringBuilder sb) {
|
||||||
|
@ -702,6 +719,10 @@ public class EntityCapsManager extends Manager {
|
||||||
private String ver;
|
private String ver;
|
||||||
private String nodeVer;
|
private String nodeVer;
|
||||||
|
|
||||||
|
NodeVerHash(String node, CapsVersionAndHash capsVersionAndHash) {
|
||||||
|
this(node, capsVersionAndHash.version, capsVersionAndHash.hash);
|
||||||
|
}
|
||||||
|
|
||||||
NodeVerHash(String node, String ver, String hash) {
|
NodeVerHash(String node, String ver, String hash) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.ver = ver;
|
this.ver = ver;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.LinkedList;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
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.Base32;
|
||||||
import org.jivesoftware.smack.util.stringencoder.StringEncoder;
|
import org.jivesoftware.smack.util.stringencoder.StringEncoder;
|
||||||
import org.jivesoftware.smackx.InitExtensions;
|
import org.jivesoftware.smackx.InitExtensions;
|
||||||
|
@ -54,8 +55,8 @@ public class EntityCapsManagerTest extends InitExtensions {
|
||||||
public void testComplexGenerationExample() {
|
public void testComplexGenerationExample() {
|
||||||
DiscoverInfo di = createComplexSamplePacket();
|
DiscoverInfo di = createComplexSamplePacket();
|
||||||
|
|
||||||
String ver = EntityCapsManager.generateVerificationString(di, "sha-1");
|
CapsVersionAndHash versionAndHash = EntityCapsManager.generateVerificationString(di, StringUtils.SHA1);
|
||||||
assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", ver);
|
assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", versionAndHash.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -82,7 +83,8 @@ public class EntityCapsManagerTest extends InitExtensions {
|
||||||
EntityCapsManager.setPersistentCache(cache);
|
EntityCapsManager.setPersistentCache(cache);
|
||||||
|
|
||||||
DiscoverInfo di = createComplexSamplePacket();
|
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
|
// Save the data in EntityCapsManager
|
||||||
EntityCapsManager.addDiscoverInfoByNode(nodeVer, di);
|
EntityCapsManager.addDiscoverInfoByNode(nodeVer, di);
|
||||||
|
|
Loading…
Reference in a new issue