⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 entitycapabilitiesmanager.java

📁 openfire 服务器源码下载
💻 JAVA
字号:
/** * $RCSfile: $ * $Revision: $ * $Date: $ * * Copyright (C) 2008 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution, or a commercial license * agreement with Jive. */package org.jivesoftware.openfire.entitycaps;import org.dom4j.Element;import org.jivesoftware.openfire.IQResultListener;import org.jivesoftware.openfire.IQRouter;import org.jivesoftware.openfire.XMPPServer;import org.jivesoftware.openfire.event.UserEventListener;import org.jivesoftware.openfire.user.User;import org.jivesoftware.util.StringUtils;import org.jivesoftware.util.cache.Cache;import org.jivesoftware.util.cache.CacheFactory;import org.xmpp.packet.IQ;import org.xmpp.packet.JID;import org.xmpp.packet.Presence;import java.util.*;/** * Implements server side mechanics for XEP-0115: "Entity Capabilities" * Version 1.4 *  * In particular, EntityCapabilitiesManager is useful for processing * "filtered-notifications" for use with Pubsub (XEP-0060) for contacts that * may not want to receive notifications for all payload types. *  * The server's role in managing Entity Capabilities is to cache previously * encountered entity capabilities for XMPP clients supporting the same * identities and features. If the server has not seen a particular * combination of identities and features, a Discover Information query is * sent to that client and its reply is cached for future use by clients * sharing those same entity capabilities. *  * @author Armando Jagucki * */public class EntityCapabilitiesManager implements IQResultListener, UserEventListener {    private static final EntityCapabilitiesManager instance = new EntityCapabilitiesManager();    /**     * Entity Capabilities cache map. This cache stores entity capabilities     * that may be shared among users.     *      * When we want to look up the entity capabilities for a user, we first     * find their most recently advertised 'ver' hash using the     * {@link #entityCapabilitiesUserMap}. Then we use that 'ver' hash as a     * key into this map.     *      * Key:   The 'ver' hash string that encapsulates identities+features.     * Value: EntityCapabilities object representing the encapsulated values.     */    private Cache<String, EntityCapabilities> entityCapabilitiesMap;    /**     * Entity Capabilities user cache map. This map is used to determine which     * entity capabilities are in use for a particular user.     *      * When we want to look up the entity capabilities for a user, we first     * find their most recently advertised 'ver' hash using this map. Then we     * use this 'ver' hash as a key into the {@link #entityCapabilitiesMap}.     *      * Key:   The JID of the user.     * Value: The 'ver' hash string that encapsulates identities+features.     */    private Cache<JID, String> entityCapabilitiesUserMap;    /**     * Ver attributes are the hash strings that correspond to a certain     * combination of entity capabilities. This hash string, representing a     * particular identities+features combination, is found in the 'ver'     * attribute of the caps element in a presence packet (caps packet).     *      * Each unrecognized caps packet that is encountered has its verAttribute     * added to this map. Since results to our disco#info queries can be     * received in any order, the map is used by {@link #isValid(IQ)} so the     * method can be sure it is comparing its generated 'ver' hash to the     * correct 'ver' hash in the map, that was previously encountered in the     * caps packet.     *      * We use a cache for this map so it is cluster safe for remote users     * whose disco#info replies are handled by new nodes in the cluster (after     * an s2s disconnection for example).     *      * Key:   Packet ID of our disco#info request.     * Value: The 'ver' hash string from the original caps packet.     */    private Cache<String, String> verAttributes;    private EntityCapabilitiesManager() {        entityCapabilitiesMap = CacheFactory.createCache("Entity Capabilities");        entityCapabilitiesUserMap = CacheFactory.createCache("Entity Capabilities Users");        verAttributes = CacheFactory.createCache("Entity Capabilities Pending Hashes");    }    /**     * Returns the unique instance of this class.     *     * @return the unique instance of this class.     */    public static EntityCapabilitiesManager getInstance() {        return instance;    }    public void process(Presence packet) {        // Ignore unavailable presences        if (Presence.Type.unavailable == packet.getType()) {            return;        }        // Examine the packet and check if it has caps info and a 'ver' hash,        // if not -- do nothing by returning.        Element capsElement = packet.getChildElement("c", "http://jabber.org/protocol/caps");        if (capsElement == null) {            return;        }        String newVerAttribute = capsElement.attributeValue("ver");        if (newVerAttribute == null) {            return;        }        // Check to see if the 'ver' hash is already in our cache.        if (isInCapsCache(newVerAttribute)) {            // The 'ver' hash is in the cache already, so let's update the            // entityCapabilitiesUserMap for the user that sent the caps            // packet.            entityCapabilitiesUserMap.put(packet.getFrom(), newVerAttribute);        }        else {            // The 'ver' hash is not in the cache so send out a disco#info query            // so that we may begin recognizing this 'ver' hash.            IQ iq = new IQ(IQ.Type.get);            iq.setTo(packet.getFrom());            String serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();            iq.setFrom(serverName);            iq.setChildElement("query", "http://jabber.org/protocol/disco#info");            String packetId = iq.getID();            verAttributes.put(packetId, newVerAttribute);            IQRouter iqRouter = XMPPServer.getInstance().getIQRouter();            iqRouter.addIQResultListener(packetId, this);            iqRouter.route(iq);        }    }    /**     * Determines whether or not a particular 'ver' attribute is stored in the     * {@link #entityCapabilitiesMap} cache.     *      * @param verAttribute the 'ver' hash to check for.     * @return true if the caps cache contains the 'ver' hash already, false if not.     */    private boolean isInCapsCache(String verAttribute) {        return entityCapabilitiesMap.containsKey(verAttribute);    }    /**     * Determines whether or not the packet received from a disco#info result     * was valid by comparing its 'ver' hash (identites+features encapsulated     * hash) with the 'ver' hash of the original caps packet that the     * disco#info query was sent on behalf of.     *      * @param packet the disco#info result packet.     * @return true if the packet's generated 'ver' hash matches the 'ver'     *         hash of the original caps packet.     */    private boolean isValid(IQ packet) {        String newVerHash = generateVerHash(packet);        String originalVerAttribute = verAttributes.get(packet.getID());        return originalVerAttribute.equals(newVerHash);    }    /**     * Generates a 'ver' hash attribute used in validation to help prevent     * poisoning of entity capabilities information.     *      * @see #isValid(IQ)     *      * The value of the 'ver' attribute is generated according to the method     * outlined in XEP-0115.     *      * @param packet IQ reply to the entity cap request.     * @return the generated 'ver' hash     */    private String generateVerHash(IQ packet) {        // Initialize an empty string S.        String S = "";        // Sort the service discovery identities by category and then by type        // (if it exists), formatted as 'category' '/' 'type'.        List<String> discoIdentities = getIdentitiesFrom(packet);        Collections.sort(discoIdentities);        // For each identity, append the 'category/type' to S, followed by the        // '<' character.        for (String discoIdentity : discoIdentities) {            S += discoIdentity;            S += '<';        }        // Sort the supported features.        List<String> discoFeatures = getFeaturesFrom(packet);        Collections.sort(discoFeatures);        // For each feature, append the feature to S, followed by the '<'        // character.        for (String discoFeature : discoFeatures) {            S += discoFeature;            S += '<';        }        // Compute ver by hashing S using the SHA-1 algorithm as specified in        // RFC 3174 (with binary output) and encoding the hash using Base64 as        // specified in Section 4 of RFC 4648 (note: the Base64 output        // MUST NOT include whitespace and MUST set padding bits to zero).        S = StringUtils.hash(S, "SHA-1");        S = StringUtils.encodeBase64(StringUtils.decodeHex(S));        return S;    }    public void answerTimeout(String packetId) {        // If we never received an answer, we can discard the cached        // 'ver' attribute.        verAttributes.remove(packetId);    }    public void receivedAnswer(IQ packet) {        String packetId = packet.getID();        if (isValid(packet)) {            // The packet was validated, so it can be added to the Entity            // Capabilities cache map.            // Create the entity capabilities object and add it to the cache map...            EntityCapabilities entityCapabilities = new EntityCapabilities();            // Store identities.            List<String> identities = getIdentitiesFrom(packet);            for (String identity : identities) {                entityCapabilities.addIdentity(identity);            }            // Store features.            List<String> features = getFeaturesFrom(packet);            for (String feature : features) {                entityCapabilities.addFeature(feature);            }            String originalVerAttribute = verAttributes.get(packetId);            entityCapabilities.setVerAttribute(originalVerAttribute);            entityCapabilitiesMap.put(originalVerAttribute, entityCapabilities);            entityCapabilitiesUserMap.put(packet.getFrom(), originalVerAttribute);        }        // Remove cached 'ver' attribute.        verAttributes.remove(packetId);    }    /**     * Returns the entity capabilities for a specific JID. The specified JID     * should be a full JID that identitied the entity's connection.     *      * @param jid the full JID of entity     * @return the entity capabilities of jid.     */    public EntityCapabilities getEntityCapabilities(JID jid) {        String verAttribute = entityCapabilitiesUserMap.get(jid);        return entityCapabilitiesMap.get(verAttribute);    }    /**     * Extracts a list of identities from an IQ packet.     *      * @param packet the packet     * @return a list of identities     */    private List<String> getIdentitiesFrom(IQ packet) {        List<String> discoIdentities = new ArrayList<String>();        Element query = packet.getChildElement();        Iterator identitiesIterator = query.elementIterator("identity");        if (identitiesIterator != null) {            while (identitiesIterator.hasNext()) {                Element identityElement = (Element) identitiesIterator.next();                String discoIdentity = identityElement.attributeValue("category");                discoIdentity += '/';                discoIdentity += identityElement.attributeValue("type");                discoIdentities.add(discoIdentity);            }        }        return discoIdentities;    }    /**     * Extracts a list of features from an IQ packet.     *      * @param packet the packet     * @return a list of features     */    private List<String> getFeaturesFrom(IQ packet) {        List<String> discoFeatures = new ArrayList<String>();        Element query = packet.getChildElement();        Iterator featuresIterator = query.elementIterator("feature");        if (featuresIterator != null) {            while (featuresIterator.hasNext()) {                Element featureElement = (Element) featuresIterator.next();                String discoFeature = featureElement.attributeValue("var");                discoFeatures.add(discoFeature);            }        }        return discoFeatures;    }    public void userDeleting(User user, Map<String, Object> params) {        // Delete this user's association in entityCapabilitiesUserMap.        JID jid = XMPPServer.getInstance().createJID(user.getUsername(), null, true);        String verHashOfUser = entityCapabilitiesUserMap.remove(jid);        // If there are no other references to the deleted user's 'ver' hash,        // it is safe to remove that 'ver' hash's associated entity        // capabilities from the entityCapabilitiesMap cache.        for (String verHash : entityCapabilitiesUserMap.values()) {            if (verHash.equals(verHashOfUser)) {                // A different user is making use of the deleted user's same                // 'ver' hash, so let's not remove the associated entity                // capabilities from the entityCapabilitiesMap.                return;            }        }        entityCapabilitiesMap.remove(verHashOfUser);    }    public void userCreated(User user, Map<String, Object> params) {        // Do nothing.    }    public void userModified(User user, Map<String, Object> params) {        // Do nothing.    }}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -