1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2009 Jonas Ådahl. 3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2011-2013 Florian Schmaus 4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License. 7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at 8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * http://www.apache.org/licenses/LICENSE-2.0 10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software 12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS, 13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and 15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License. 16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smackx.entitycaps; 19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.Connection; 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.ConnectionCreationListener; 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.ConnectionListener; 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.PacketInterceptor; 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.PacketListener; 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.SmackConfiguration; 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPConnection; 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException; 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.IQ; 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Packet; 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.PacketExtension; 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Presence; 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.NotFilter; 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketFilter; 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.AndFilter; 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketTypeFilter; 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketExtensionFilter; 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.Base64; 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.Cache; 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.Form; 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.FormField; 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.NodeInformationProvider; 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.ServiceDiscoveryManager; 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.entitycaps.cache.EntityCapsPersistentCache; 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.entitycaps.packet.CapsExtension; 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo; 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DataForm; 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo.Feature; 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo.Identity; 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverItems.Item; 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Collections; 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Comparator; 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.HashMap; 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Iterator; 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.LinkedList; 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.List; 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Map; 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Queue; 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.SortedSet; 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.TreeSet; 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.WeakHashMap; 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ConcurrentLinkedQueue; 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException; 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.lang.ref.WeakReference; 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.security.MessageDigest; 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.security.NoSuchAlgorithmException; 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Keeps track of entity capabilities. 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Florian Schmaus 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class EntityCapsManager { 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static final String NAMESPACE = "http://jabber.org/protocol/caps"; 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static final String ELEMENT = "c"; 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private static final String ENTITY_NODE = "http://www.igniterealtime.org/projects/smack"; 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private static final Map<String, MessageDigest> SUPPORTED_HASHES = new HashMap<String, MessageDigest>(); 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected static EntityCapsPersistentCache persistentCache; 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private static Map<Connection, EntityCapsManager> instances = Collections 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen .synchronizedMap(new WeakHashMap<Connection, EntityCapsManager>()); 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Map of (node + '#" + hash algorithm) to DiscoverInfo data 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected static Map<String, DiscoverInfo> caps = new Cache<String, DiscoverInfo>(1000, -1); 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Map of Full JID -> DiscoverInfo/null. In case of c2s connection the 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * key is formed as user@server/resource (resource is required) In case of 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * link-local connection the key is formed as user@host (no resource) In 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * case of a server or component the key is formed as domain 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected static Map<String, NodeVerHash> jidCaps = new Cache<String, NodeVerHash>(10000, -1); 98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen static { 100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Connection.addConnectionCreationListener(new ConnectionCreationListener() { 101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionCreated(Connection connection) { 102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (connection instanceof XMPPConnection) 103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen new EntityCapsManager(connection); 104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen MessageDigest sha1MessageDigest = MessageDigest.getInstance("SHA-1"); 109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SUPPORTED_HASHES.put("sha-1", sha1MessageDigest); 110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } catch (NoSuchAlgorithmException e) { 111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Ignore 112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private WeakReference<Connection> weakRefConnection; 116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private ServiceDiscoveryManager sdm; 117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean entityCapsEnabled; 118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String currentCapsVersion; 119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean presenceSend = false; 120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Queue<String> lastLocalCapsVersions = new ConcurrentLinkedQueue<String>(); 121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Add DiscoverInfo to the database. 124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param nodeVer 126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The node and verification String (e.g. 127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * "http://psi-im.org#q07IKJEyjvHSyhy//CH0CxmKi8w="). 128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param info 129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * DiscoverInfo for the specified node. 130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static void addDiscoverInfoByNode(String nodeVer, DiscoverInfo info) { 132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen caps.put(nodeVer, info); 133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (persistentCache != null) 135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen persistentCache.addDiscoverInfoByNodePersistent(nodeVer, info); 136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Get the Node version (node#ver) of a JID. Returns a String or null if 140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * EntiyCapsManager does not have any information. 141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param user 143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the user (Full JID) 144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the node version (node#ver) or null 145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static String getNodeVersionByJid(String jid) { 147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen NodeVerHash nvh = jidCaps.get(jid); 148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (nvh != null) { 149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return nvh.nodeVer; 150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } else { 151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static NodeVerHash getNodeVerHashByJid(String jid) { 156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return jidCaps.get(jid); 157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Get the discover info given a user name. The discover info is returned if 161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the user has a node#ver associated with it and the node#ver has a 162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * discover info associated with it. 163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param user 165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * user name (Full JID) 166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the discovered info 167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static DiscoverInfo getDiscoverInfoByUser(String user) { 169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen NodeVerHash nvh = jidCaps.get(user); 170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (nvh == null) 171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return getDiscoveryInfoByNodeVer(nvh.nodeVer); 174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Retrieve DiscoverInfo for a specific node. 178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param nodeVer 180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The node name (e.g. 181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * "http://psi-im.org#q07IKJEyjvHSyhy//CH0CxmKi8w="). 182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return The corresponding DiscoverInfo or null if none is known. 183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static DiscoverInfo getDiscoveryInfoByNodeVer(String nodeVer) { 185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DiscoverInfo info = caps.get(nodeVer); 186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (info != null) 187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen info = new DiscoverInfo(info); 188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return info; 190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Set the persistent cache implementation 194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param cache 196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException 197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static void setPersistentCache(EntityCapsPersistentCache cache) throws IOException { 199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (persistentCache != null) 200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IllegalStateException("Entity Caps Persistent Cache was already set"); 201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen persistentCache = cache; 202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen persistentCache.replay(); 203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sets the maximum Cache size for the JID to nodeVer Cache 207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param maxCacheSize 209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @SuppressWarnings("rawtypes") 211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static void setJidCapsMaxCacheSize(int maxCacheSize) { 212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ((Cache) jidCaps).setMaxCacheSize(maxCacheSize); 213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Sets the maximum Cache size for the nodeVer to DiscoverInfo Cache 217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param maxCacheSize 219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @SuppressWarnings("rawtypes") 221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static void setCapsMaxCacheSize(int maxCacheSize) { 222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ((Cache) caps).setMaxCacheSize(maxCacheSize); 223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private EntityCapsManager(Connection connection) { 226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.weakRefConnection = new WeakReference<Connection>(connection); 227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.sdm = ServiceDiscoveryManager.getInstanceFor(connection); 228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen init(); 229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private void init() { 232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Connection connection = weakRefConnection.get(); 233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen instances.put(connection, this); 234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addConnectionListener(new ConnectionListener() { 236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionClosed() { 237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Unregister this instance since the connection has been closed 238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen presenceSend = false; 239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen instances.remove(weakRefConnection.get()); 240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionClosedOnError(Exception e) { 243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen presenceSend = false; 244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectionFailed(Exception e) { 247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectingIn(int seconds) { 251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectionSuccessful() { 255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // This calculates the local entity caps version 260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen updateLocalEntityCaps(); 261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (SmackConfiguration.autoEnableEntityCaps()) 263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen enableEntityCaps(); 264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen PacketFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), new PacketExtensionFilter( 266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ELEMENT, NAMESPACE)); 267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addPacketListener(new PacketListener() { 268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Listen for remote presence stanzas with the caps extension 269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // If we receive such a stanza, record the JID and nodeVer 270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void processPacket(Packet packet) { 272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!entityCapsEnabled()) 273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen CapsExtension ext = (CapsExtension) packet.getExtension(EntityCapsManager.ELEMENT, 276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen EntityCapsManager.NAMESPACE); 277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String hash = ext.getHash().toLowerCase(); 279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!SUPPORTED_HASHES.containsKey(hash)) 280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String from = packet.getFrom(); 283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String node = ext.getNode(); 284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String ver = ext.getVer(); 285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen jidCaps.put(from, new NodeVerHash(node, ver, hash)); 287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }, packetFilter); 290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), new NotFilter(new PacketExtensionFilter( 292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ELEMENT, NAMESPACE))); 293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addPacketListener(new PacketListener() { 294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void processPacket(Packet packet) { 296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // always remove the JID from the map, even if entityCaps are 297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // disabled 298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String from = packet.getFrom(); 299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen jidCaps.remove(from); 300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }, packetFilter); 302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen packetFilter = new PacketTypeFilter(Presence.class); 304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addPacketSendingListener(new PacketListener() { 305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void processPacket(Packet packet) { 307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen presenceSend = true; 308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }, packetFilter); 310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Intercept presence packages and add caps data when intended. 312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // XEP-0115 specifies that a client SHOULD include entity capabilities 313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // with every presence notification it sends. 314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen PacketFilter capsPacketFilter = new PacketTypeFilter(Presence.class); 315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen PacketInterceptor packetInterceptor = new PacketInterceptor() { 316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void interceptPacket(Packet packet) { 317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!entityCapsEnabled) 318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen CapsExtension caps = new CapsExtension(ENTITY_NODE, getCapsVersion(), "sha-1"); 321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen packet.addExtension(caps); 322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }; 324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addPacketInterceptor(packetInterceptor, capsPacketFilter); 325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // It's important to do this as last action. Since it changes the 326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // behavior of the SDM in some ways 327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.setEntityCapsManager(this); 328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static synchronized EntityCapsManager getInstanceFor(Connection connection) { 331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // For testing purposed forbid EntityCaps for non XMPPConnections 332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // it may work on BOSH connections too 333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!(connection instanceof XMPPConnection)) 334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (SUPPORTED_HASHES.size() <= 0) 337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen EntityCapsManager entityCapsManager = instances.get(connection); 340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (entityCapsManager == null) { 342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen entityCapsManager = new EntityCapsManager(connection); 343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return entityCapsManager; 346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void enableEntityCaps() { 349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Add Entity Capabilities (XEP-0115) feature node. 350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.addFeature(NAMESPACE); 351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen updateLocalEntityCaps(); 352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen entityCapsEnabled = true; 353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void disableEntityCaps() { 356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen entityCapsEnabled = false; 357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.removeFeature(NAMESPACE); 358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean entityCapsEnabled() { 361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return entityCapsEnabled; 362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Remove a record telling what entity caps node a user has. 366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param user 368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the user (Full JID) 369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void removeUserCapsNode(String user) { 371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen jidCaps.remove(user); 372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Get our own caps version. The version depends on the enabled features. A 376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * caps version looks like '66/0NaeaBKkwk85efJTGmU47vXI=' 377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return our own caps version 379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getCapsVersion() { 381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return currentCapsVersion; 382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the local entity's NodeVer (e.g. 386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * "http://www.igniterealtime.org/projects/smack/#66/0NaeaBKkwk85efJTGmU47vXI= 387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * ) 388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return 390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getLocalNodeVer() { 392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return ENTITY_NODE + '#' + getCapsVersion(); 393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns true if Entity Caps are supported by a given JID 397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param jid 399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return 400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean areEntityCapsSupported(String jid) { 402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (jid == null) 403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DiscoverInfo result = sdm.discoverInfo(jid); 407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return result.containsFeature(NAMESPACE); 408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } catch (XMPPException e) { 409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns true if Entity Caps are supported by the local service/server 415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return 417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public boolean areEntityCapsSupportedByServer() { 419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return areEntityCapsSupported(weakRefConnection.get().getServiceName()); 420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Updates the local user Entity Caps information with the data provided 424d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 425d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If we are connected and there was already a presence send, another 426d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * presence is send to inform others about your new Entity Caps node string. 427d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 428d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param discoverInfo 429d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the local users discover info (mostly the service discovery 430d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * features) 431d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param identityType 432d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the local users identity type 433d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param identityName 434d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the local users identity name 435d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param extendedInfo 436d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the local users extended info 437d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 438d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void updateLocalEntityCaps() { 439d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Connection connection = weakRefConnection.get(); 440d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 441d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DiscoverInfo discoverInfo = new DiscoverInfo(); 442d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen discoverInfo.setType(IQ.Type.RESULT); 443d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen discoverInfo.setNode(getLocalNodeVer()); 444d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (connection != null) 445d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen discoverInfo.setFrom(connection.getUser()); 446d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.addDiscoverInfoTo(discoverInfo); 447d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 448d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen currentCapsVersion = generateVerificationString(discoverInfo, "sha-1"); 449d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen addDiscoverInfoByNode(ENTITY_NODE + '#' + currentCapsVersion, discoverInfo); 450d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (lastLocalCapsVersions.size() > 10) { 451d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String oldCapsVersion = lastLocalCapsVersions.poll(); 452d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.removeNodeInformationProvider(ENTITY_NODE + '#' + oldCapsVersion); 453d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 454d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen lastLocalCapsVersions.add(currentCapsVersion); 455d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 456d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen caps.put(currentCapsVersion, discoverInfo); 457d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (connection != null) 458d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen jidCaps.put(connection.getUser(), new NodeVerHash(ENTITY_NODE, currentCapsVersion, "sha-1")); 459d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 460d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sdm.setNodeInformationProvider(ENTITY_NODE + '#' + currentCapsVersion, new NodeInformationProvider() { 461d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen List<String> features = sdm.getFeaturesList(); 462d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen List<Identity> identities = new LinkedList<Identity>(ServiceDiscoveryManager.getIdentities()); 463d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen List<PacketExtension> packetExtensions = sdm.getExtendedInfoAsList(); 464d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 465d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 466d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public List<Item> getNodeItems() { 467d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 468d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 469d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 470d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 471d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public List<String> getNodeFeatures() { 472d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return features; 473d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 474d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 475d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 476d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public List<Identity> getNodeIdentities() { 477d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return identities; 478d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 479d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 480d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen @Override 481d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public List<PacketExtension> getNodePacketExtensions() { 482d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return packetExtensions; 483d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 484d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 485d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 486d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Send an empty presence, and let the packet intercepter 487d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // add a <c/> node to it. 488d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // See http://xmpp.org/extensions/xep-0115.html#advertise 489d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // We only send a presence packet if there was already one send 490d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // to respect ConnectionConfiguration.isSendPresence() 491d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (connection != null && connection.isAuthenticated() && presenceSend) { 492d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Presence presence = new Presence(Presence.Type.available); 493d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.sendPacket(presence); 494d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 495d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 496d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 497d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 498d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Verify DisoverInfo and Caps Node as defined in XEP-0115 5.4 Processing 499d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Method 500d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 501d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see <a href="http://xmpp.org/extensions/xep-0115.html#ver-proc">XEP-0115 502d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 5.4 Processing Method</a> 503d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 504d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param capsNode 505d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the caps node (i.e. node#ver) 506d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param info 507d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return true if it's valid and should be cache, false if not 508d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 509d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static boolean verifyDiscoverInfoVersion(String ver, String hash, DiscoverInfo info) { 510d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // step 3.3 check for duplicate identities 511d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (info.containsDuplicateIdentities()) 512d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 513d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 514d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // step 3.4 check for duplicate features 515d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (info.containsDuplicateFeatures()) 516d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 517d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 518d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // step 3.5 check for well-formed packet extensions 519d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (verifyPacketExtensions(info)) 520d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 521d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 522d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String calculatedVer = generateVerificationString(info, hash); 523d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 524d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!ver.equals(calculatedVer)) 525d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 526d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 527d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return true; 528d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 529d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 530d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 531d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 532d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param info 533d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return true if the packet extensions is ill-formed 534d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 535d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected static boolean verifyPacketExtensions(DiscoverInfo info) { 536d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen List<FormField> foundFormTypes = new LinkedList<FormField>(); 537d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<PacketExtension> i = info.getExtensions().iterator(); i.hasNext();) { 538d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen PacketExtension pe = i.next(); 539d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (pe.getNamespace().equals(Form.NAMESPACE)) { 540d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataForm df = (DataForm) pe; 541d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<FormField> it = df.getFields(); it.hasNext();) { 542d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen FormField f = it.next(); 543d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (f.getVariable().equals("FORM_TYPE")) { 544d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (FormField fft : foundFormTypes) { 545d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (f.equals(fft)) 546d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return true; 547d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 548d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen foundFormTypes.add(f); 549d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 550d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 551d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 552d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 553d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 554d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 555d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 556d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 557d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Generates a XEP-115 Verification String 558d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 559d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see <a href="http://xmpp.org/extensions/xep-0115.html#ver">XEP-115 560d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Verification String</a> 561d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 562d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param discoverInfo 563d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param hash 564d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the used hash function 565d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return The generated verification String or null if the hash is not 566d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * supported 567d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 568d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected static String generateVerificationString(DiscoverInfo discoverInfo, String hash) { 569d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen MessageDigest md = SUPPORTED_HASHES.get(hash.toLowerCase()); 570d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (md == null) 571d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return null; 572d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 573d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataForm extendedInfo = (DataForm) discoverInfo.getExtension(Form.ELEMENT, Form.NAMESPACE); 574d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 575d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 1. Initialize an empty string S ('sb' in this method). 576d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen StringBuilder sb = new StringBuilder(); // Use StringBuilder as we don't 577d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // need thread-safe StringBuffer 578d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 579d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 2. Sort the service discovery identities by category and then by 580d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // type and then by xml:lang 581d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // (if it exists), formatted as CATEGORY '/' [TYPE] '/' [LANG] '/' 582d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // [NAME]. Note that each slash is included even if the LANG or 583d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // NAME is not included (in accordance with XEP-0030, the category and 584d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // type MUST be included. 585d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SortedSet<DiscoverInfo.Identity> sortedIdentities = new TreeSet<DiscoverInfo.Identity>(); 586d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 587d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<DiscoverInfo.Identity> it = discoverInfo.getIdentities(); it.hasNext();) 588d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sortedIdentities.add(it.next()); 589d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 590d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 3. For each identity, append the 'category/type/lang/name' to S, 591d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // followed by the '<' character. 592d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<DiscoverInfo.Identity> it = sortedIdentities.iterator(); it.hasNext();) { 593d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DiscoverInfo.Identity identity = it.next(); 594d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(identity.getCategory()); 595d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("/"); 596d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(identity.getType()); 597d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("/"); 598d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(identity.getLanguage() == null ? "" : identity.getLanguage()); 599d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("/"); 600d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(identity.getName() == null ? "" : identity.getName()); 601d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("<"); 602d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 603d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 604d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 4. Sort the supported service discovery features. 605d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SortedSet<String> features = new TreeSet<String>(); 606d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<Feature> it = discoverInfo.getFeatures(); it.hasNext();) 607d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen features.add(it.next().getVar()); 608d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 609d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 5. For each feature, append the feature to S, followed by the '<' 610d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // character 611d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (String f : features) { 612d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(f); 613d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("<"); 614d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 615d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 616d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // only use the data form for calculation is it has a hidden FORM_TYPE 617d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // field 618d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // see XEP-0115 5.4 step 3.6 619d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (extendedInfo != null && extendedInfo.hasHiddenFormTypeField()) { 620d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen synchronized (extendedInfo) { 621d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 6. If the service discovery information response includes 622d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e., 623d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // by the XML character data of the <value/> element). 624d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SortedSet<FormField> fs = new TreeSet<FormField>(new Comparator<FormField>() { 625d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public int compare(FormField f1, FormField f2) { 626d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return f1.getVariable().compareTo(f2.getVariable()); 627d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 628d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 629d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 630d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen FormField ft = null; 631d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 632d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (Iterator<FormField> i = extendedInfo.getFields(); i.hasNext();) { 633d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen FormField f = i.next(); 634d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!f.getVariable().equals("FORM_TYPE")) { 635d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen fs.add(f); 636d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } else { 637d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ft = f; 638d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 639d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 640d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 641d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Add FORM_TYPE values 642d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (ft != null) { 643d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen formFieldValuesToCaps(ft.getValues(), sb); 644d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 645d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 646d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 7. 3. For each field other than FORM_TYPE: 647d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 1. Append the value of the "var" attribute, followed by the 648d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // '<' character. 649d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 2. Sort values by the XML character data of the <value/> 650d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // element. 651d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 3. For each <value/> element, append the XML character data, 652d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // followed by the '<' character. 653d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (FormField f : fs) { 654d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(f.getVariable()); 655d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("<"); 656d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen formFieldValuesToCaps(f.getValues(), sb); 657d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 658d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 659d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 660d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 8. Ensure that S is encoded according to the UTF-8 encoding (RFC 661d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 3269). 662d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 9. Compute the verification string by hashing S using the algorithm 663d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // specified in the 'hash' attribute (e.g., SHA-1 as defined in RFC 664d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // 3174). 665d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // The hashed data MUST be generated with binary output and 666d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // encoded using Base64 as specified in Section 4 of RFC 4648 667d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // (note: the Base64 output MUST NOT include whitespace and MUST set 668d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // padding bits to zero). 669d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] digest = md.digest(sb.toString().getBytes()); 670d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return Base64.encodeBytes(digest); 671d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 672d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 673d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private static void formFieldValuesToCaps(Iterator<String> i, StringBuilder sb) { 674d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SortedSet<String> fvs = new TreeSet<String>(); 675d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (i.hasNext()) { 676d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen fvs.add(i.next()); 677d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 678d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (String fv : fvs) { 679d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append(fv); 680d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen sb.append("<"); 681d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 682d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 683d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 684d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public static class NodeVerHash { 685d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String node; 686d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String hash; 687d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String ver; 688d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private String nodeVer; 689d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 690d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen NodeVerHash(String node, String ver, String hash) { 691d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.node = node; 692d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.ver = ver; 693d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.hash = hash; 694d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen nodeVer = node + "#" + ver; 695d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 696d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 697d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getNodeVer() { 698d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return nodeVer; 699d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 700d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 701d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getNode() { 702d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return node; 703d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 704d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 705d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getHash() { 706d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return hash; 707d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 708d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 709d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public String getVer() { 710d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return ver; 711d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 712d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 713d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 714