public PubSubNodeInfo getNodeDetails(String nodeId) throws SoxLibException { PubSubNodeInfo result = new PubSubNodeInfo(); DiscoverInfo di = null; try { Node node = mPubSubManager.getNode(nodeId); if (!(node instanceof LeafNode)) { throw new SoxLibException("Expecting leaf node, but got something else:" + nodeId); } LeafNode leaf = (LeafNode) node; di = leaf.discoverInfo(); } catch (XMPPException e) { throw new SoxLibException("Problem accessing node list:" + nodeId); } // Expect one and only one identity. Iterator<DiscoverInfo.Identity> iter = di.getIdentities(); if (iter.hasNext()) { DiscoverInfo.Identity info = iter.next(); // logger.log(Level.FINER, "type:" + info.getType()); // logger.log(Level.FINER, "cat:" + info.getCategory()); result.nodeType = info.getType(); // Assert iter.hasNext == false } // If no form data, returns null for this field. result.nodeForm = (DataForm) di.getExtension("jabber:x:data"); return result; }
/** * Add {@link DiscoverInfo} to our caps database. * * <p><b>Warning</b>: The specified <tt>DiscoverInfo</tt> is trusted to be valid with respect to * the specified <tt>Caps</tt> for performance reasons because the <tt>DiscoverInfo</tt> should * have already been validated in order to be used elsewhere anyway. * * @param caps the <tt>Caps<tt/> i.e. the node, the hash and the ver for which a * <tt>DiscoverInfo</tt> is to be added to our caps database. * @param info {@link DiscoverInfo} for the specified <tt>Caps</tt>. */ public static void addDiscoverInfoByCaps(Caps caps, DiscoverInfo info) { cleanupDiscoverInfo(info); /* * DiscoverInfo carries the node we're now associating it with a * specific node so we'd better keep them in sync. */ info.setNode(caps.getNodeVer()); synchronized (caps2discoverInfo) { DiscoverInfo oldInfo = caps2discoverInfo.put(caps, info); /* * If the specified info is a new association for the specified * node, remember it across application instances in order to not * query for it over the network. */ if ((oldInfo == null) || !oldInfo.equals(info)) { String xml = info.getChildElementXML(); if ((xml != null) && (xml.length() != 0)) { getConfigService().setProperty(getCapsPropertyName(caps), xml); } } } }
public void testLocalEntityCaps() throws InterruptedException { DiscoverInfo info = EntityCapsManager.getDiscoveryInfoByNodeVer(ecm1.getLocalNodeVer()); assertFalse(info.containsFeature(DISCOVER_TEST_FEATURE)); dropWholeEntityCapsCache(); // This should cause a new presence stanza from con1 with and updated // 'ver' String sdm1.addFeature(DISCOVER_TEST_FEATURE); // Give the server some time to handle the stanza and send it to con0 Thread.sleep(2000); // The presence stanza should get received by con0 and the data should // be recorded in the map // Note that while both connections use the same static Entity Caps // cache, // it's assured that *not* con1 added the data to the Entity Caps cache. // Every time the entities features // and identities change only a new caps 'ver' is calculated and send // with the presence stanza // The other connection has to receive this stanza and record the // information in order for this test to succeed. info = EntityCapsManager.getDiscoveryInfoByNodeVer(ecm1.getLocalNodeVer()); assertNotNull(info); assertTrue(info.containsFeature(DISCOVER_TEST_FEATURE)); }
/** * Checks if the given {@linkplain JID} supports the requested feature. The JID may be non * resource qualified in which case all presences belonging to that JID are checked. * * <p>This method does <b>not</b> perform any I/O operation and will return immediately. * * <p><b>Please note the return value: <code>if(isFeatureSupported(foo, bar)) ... </code> is * likely to produce a {@link NullPointerException NPE}.</b> * * @param jid {@link JID} to query support for * @param namespace the namespace of the feature * @return <code>true</code> if the given feature is supported, <code>false</code> if it is not * supported or <b><code>null</code> </b> if no information is available * @see #queryFeatureSupport(JID, String, boolean) */ public Boolean isFeatureSupported(final JID jid, final String namespace) { checkJID(jid); Boolean supported = null; final List<JID> jidsToQuery = new ArrayList<JID>(); if (jid.isBareJID()) jidsToQuery.addAll(rosterTracker.getAvailablePresences(jid)); else jidsToQuery.add(jid); for (JID rqJID : jidsToQuery) { DiscoverInfoWrapper info = cache.get(rqJID.toString()); if (info == null) continue; DiscoverInfo disco = info.item; if (disco == null) continue; supported = disco.containsFeature(namespace); if (supported) break; } return supported; }
/** * Test if entity caps actually prevent a disco info request and reply * * @throws XMPPException */ public void testPreventDiscoInfo() throws XMPPException { con0.addPacketSendingListener( new PacketListener() { @Override public void processPacket(Packet packet) { discoInfoSend = true; } }, new AndFilter(new PacketTypeFilter(DiscoverInfo.class), new IQTypeFilter(IQ.Type.get))); // add a bogus feature so that con1 ver won't match con0's sdm1.addFeature(DISCOVER_TEST_FEATURE); dropCapsCache(); // discover that DiscoverInfo info = sdm0.discoverInfo(con1.getUser()); // that discovery should cause a disco#info assertTrue(discoInfoSend); assertTrue(info.containsFeature(DISCOVER_TEST_FEATURE)); discoInfoSend = false; // discover that info = sdm0.discoverInfo(con1.getUser()); // that discovery shouldn't cause a disco#info assertFalse(discoInfoSend); assertTrue(info.containsFeature(DISCOVER_TEST_FEATURE)); }
/** * Returns the discovered information of a given XMPP entity addressed by its JID and note * attribute. Use this message only when trying to query information which is not directly * addressable. * * @param entityID the address of the XMPP entity. * @param node the attribute that supplements the 'jid' attribute. * @return the discovered information. * @throws XMPPException if the operation failed for some reason. */ public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException { // Discover the entity's info DiscoverInfo disco = new DiscoverInfo(); disco.setType(IQ.Type.GET); disco.setTo(entityID); disco.setNode(node); // Create a packet collector to listen for a response. PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(disco.getPacketID())); connection.sendPacket(disco); // Wait up to 5 seconds for a result. IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); // Stop queuing results collector.cancel(); if (result == null) { throw new XMPPException("No response from the server."); } if (result.getType() == IQ.Type.ERROR) { throw new XMPPException(result.getError()); } return (DiscoverInfo) result; }
/** * Returns true if the specified user handles XHTML messages. * * @param connection the connection to use to perform the service discovery * @param userID the user to check. A fully qualified xmpp ID, e.g. [email protected] * @return a boolean indicating whether the specified user handles XHTML messages */ public static boolean isServiceEnabled(Connection connection, String userID) { try { DiscoverInfo result = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(userID); return result.containsFeature(namespace); } catch (XMPPException e) { e.printStackTrace(); return false; } }
/** * Get a DiscoverInfo for the current entity caps node. * * @return a DiscoverInfo for the current entity caps node */ public DiscoverInfo getOwnDiscoverInfo() { DiscoverInfo di = new DiscoverInfo(); di.setType(IQ.Type.RESULT); di.setNode(capsManager.getNode() + "#" + getEntityCapsVersion()); // Add discover info addDiscoverInfoTo(di); return di; }
/** * Perform a service discovery and check if the given feature is among the features supported by * the given recipient. All registered listeners will be notified about the result. * * @param jid A RQ-JID (user@host/resource) of the user to query support for or a non RQ-JID to * query all presences for this JID. * @blocking This method blocks until the ServiceDiscovery returns. * @reentrant This method can be called concurrently. * @caching If results are available in the cache, they are used instead of querying the server. */ private Boolean queryFeatureSupport(JID jid, String namespace) { Boolean supported = null; checkJID(jid); DiscoverInfoWrapper wrapper; final List<JID> jidsToQuery = new ArrayList<JID>(); if (jid.isBareJID()) jidsToQuery.addAll(rosterTracker.getAvailablePresences(jid)); else jidsToQuery.add(jid); for (JID rqJID : jidsToQuery) { // add dummy synchronized (cache) { wrapper = cache.get(rqJID.toString()); if (wrapper == null) { wrapper = new DiscoverInfoWrapper(); cache.put(rqJID.toString(), wrapper); } } DiscoverInfo disco = null; // wait if there is already a discovery for the JID in progress synchronized (wrapper) { if (wrapper.isAvailable()) disco = wrapper.item; else { disco = wrapper.item = performServiceDiscovery(rqJID); if (disco != null) LOG.debug("Inserted DiscoveryInfo into Cache for: " + rqJID); } } // Null means that the discovery failed if (disco == null) { // and so we do not know if the feature is supported // notifyFeatureSupportUpdated(jid, namespace, false); continue; } notifyFeatureSupportUpdated(rqJID, namespace, disco.containsFeature(namespace)); /* * loop through all presence regardless if we already know that the * feature is supported to notify the listener for every current * presence */ if (supported != null) supported |= disco.containsFeature(namespace); else supported = disco.containsFeature(namespace); } return supported; }
/** * Returns the discovered information of a given XMPP entity addressed by its JID if it's known by * the entity caps manager. * * @param entityID the address of the XMPP entity * @return the disovered info or null if no such info is available from the entity caps manager. * @throws XMPPException if the operation failed for some reason. */ public DiscoverInfo discoverInfoByCaps(String entityID) throws XMPPException { DiscoverInfo info = capsManager.getDiscoverInfoByUser(entityID); if (info != null) { DiscoverInfo newInfo = cloneDiscoverInfo(info); newInfo.setFrom(entityID); return newInfo; } else { return null; } }
/** * Get the Discover Information send by your own connection. * * @return your own DiscoverInfo */ private DiscoverInfo getOwnInformation() { DiscoverInfo result = new DiscoverInfo(); DiscoverInfo.Identity id = new DiscoverInfo.Identity("client", ServiceDiscoveryManager.getIdentityName()); id.setType(ServiceDiscoveryManager.getIdentityType()); result.addIdentity(id); Iterator<String> it = mSdm.getFeatures(); while (it.hasNext()) { result.addFeature(it.next()); } return result; }
/** * Retrieves the requested node, if it exists. It will throw an exception if it does not. * * @param id - The unique id of the node * @return the node * @throws XMPPException The node does not exist */ public Node getNode(String id) throws XMPPException { Node node = nodeMap.get(id); if (node == null) { DiscoverInfo info = new DiscoverInfo(); info.setTo(to); info.setNode(id); SyncPacketSend.getReply(con, info); node = new Node(con, id); node.setTo(to); nodeMap.put(id, node); } return node; }
/** * Get the identities sorted correctly to calculate the ver attribute. * * @param info the DiscoverInfo containing the identities * @return the sorted list of identities. */ private List<DiscoverInfo.Identity> getSortedIdentity(DiscoverInfo info) { List<DiscoverInfo.Identity> result = new ArrayList<DiscoverInfo.Identity>(); Iterator<DiscoverInfo.Identity> it = info.getIdentities(); while (it.hasNext()) { DiscoverInfo.Identity id = it.next(); result.add(id); } Collections.sort( result, new Comparator<DiscoverInfo.Identity>() { public int compare(DiscoverInfo.Identity o1, DiscoverInfo.Identity o2) { String cat1 = o1.getCategory(); if (cat1 == null) cat1 = ""; String cat2 = o2.getCategory(); if (cat2 == null) cat2 = ""; int res = cat1.compareTo(cat2); if (res != 0) return res; String type1 = o1.getType(); if (type1 == null) type1 = ""; String type2 = o2.getCategory(); if (type2 == null) type2 = ""; res = type1.compareTo(type2); if (res != 0) return res; // should compare lang but not avalaible return 0; } }); return result; }
/** Discover the features provided by the server. */ private void discoverServerFeatures() { try { // jid et server ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(mAdaptee); DiscoverInfo info = sdm.discoverInfo(mAdaptee.getServiceName()); Iterator<DiscoverInfo.Identity> it = info.getIdentities(); while (it.hasNext()) { DiscoverInfo.Identity identity = it.next(); if ("pubsub".equals(identity.getCategory()) && "pep".equals(identity.getType())) { initPEP(); } } } catch (XMPPException e) { Log.w(TAG, "Unable to discover server features", e); } }
/** * Tries to discover if the server of the given XMPPConnection provides also a MUC component. Will * return the MUCs Component ID if one is found otherwise returns null * * @param connection * @return * @throws XMPPException */ public static String disocverMUC(XMPPConnection connection) throws XMPPException { ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); DiscoverItems ditem = sdm.discoverItems(connection.getServiceName()); Iterator<Item> it = ditem.getItems(); while (it.hasNext()) { Item i = it.next(); DiscoverInfo dinfo = sdm.discoverInfo(i.getEntityID()); Iterator<Feature> itFeatures = dinfo.getFeatures(); while (itFeatures.hasNext()) { Feature f = itFeatures.next(); if (f.getVar().equals(MUC_FEATURE)) { return i.getEntityID(); } } } return null; }
public void testEntityCaps() throws XMPPException, InterruptedException { dropWholeEntityCapsCache(); sdm1.addFeature(DISCOVER_TEST_FEATURE); Thread.sleep(3000); DiscoverInfo info = sdm0.discoverInfo(con1.getUser()); assertTrue(info.containsFeature(DISCOVER_TEST_FEATURE)); String u1ver = EntityCapsManager.getNodeVersionByJid(con1.getUser()); assertNotNull(u1ver); DiscoverInfo entityInfo = EntityCapsManager.caps.get(u1ver); assertNotNull(entityInfo); assertEquals(info.toXML(), entityInfo.toXML()); }
/** * Checks the service discovery item returned from a server component to verify if it is a File * Transfer proxy or not. * * @param manager the service discovery manager which will be used to query the component * @param item the discovered item on the server relating * @return returns the JID of the proxy if it is a proxy or null if the item is not a proxy. */ private String checkIsProxy(ServiceDiscoveryManager manager, DiscoverItems.Item item) { DiscoverInfo info; try { info = manager.discoverInfo(item.getEntityID()); } catch (XMPPException e) { return null; } Iterator itx = info.getIdentities(); while (itx.hasNext()) { DiscoverInfo.Identity identity = (DiscoverInfo.Identity) itx.next(); if ("proxy".equalsIgnoreCase(identity.getCategory()) && "bytestreams".equalsIgnoreCase(identity.getType())) { return info.getFrom(); } } return null; }
/** * Add discover info response data. * * @param response the discover info response packet */ public void addDiscoverInfoTo(DiscoverInfo response) { // Set this client identity DiscoverInfo.Identity identity = new DiscoverInfo.Identity("client", getIdentityName()); identity.setType(getIdentityType()); response.addIdentity(identity); // Add the registered features to the response synchronized (features) { // Add Entity Capabilities (XEP-0115) feature node. response.addFeature("http://jabber.org/protocol/caps"); for (Iterator<String> it = getFeatures(); it.hasNext(); ) { response.addFeature(it.next()); } if (extendedInfo != null) { response.addExtension(extendedInfo); } } }
/** * Retrieves the requested node, if it exists. It will throw an exception if it does not. * * @param id - The unique id of the node * @return the node * @throws XMPPException The node does not exist */ public Node getNode(String id) throws XMPPException { Node node = nodeMap.get(id); if (node == null) { DiscoverInfo info = new DiscoverInfo(); info.setTo(to); info.setNode(id); DiscoverInfo infoReply = (DiscoverInfo) SyncPacketSend.getReply(con, info); if (infoReply.getIdentities().next().getType().equals(NodeType.leaf.toString())) node = new LeafNode(con, id); else node = new CollectionNode(con, id); node.setTo(to); nodeMap.put(id, node); } return node; }
/** * Determines whether a specific <tt>DiscoverInfo</tt> is valid according to this <tt>Caps</tt> * i.e. whether the <tt>discoverInfo</tt> has the node and the ver of this <tt>Caps</tt> and the * ver calculated from the <tt>discoverInfo</tt> using the hash (algorithm) of this * <tt>Caps</tt> is equal to the ver of this <tt>Caps</tt>. * * @param discoverInfo the <tt>DiscoverInfo</tt> to be validated by this <tt>Caps</tt> * @return <tt>true</tt> if the specified <tt>DiscoverInfo</tt> has the node and the ver of this * <tt>Caps</tt> and the ver calculated from the <tt>discoverInfo</tt> using the hash * (algorithm) of this <tt>Caps</tt> is equal to the ver of this <tt>Caps</tt>; otherwise, * <tt>false</tt> */ public boolean isValid(DiscoverInfo discoverInfo) { if (discoverInfo != null) { // The "node" attribute is not necessary in the query element. // For example, Swift does not send back the "node" attribute in // the Disco#info response. Thus, if the node of the IQ response // is null, then we set it to the request one. if (discoverInfo.getNode() == null) { discoverInfo.setNode(getNodeVer()); } if (getNodeVer().equals(discoverInfo.getNode()) && !hash.equals("") && ver.equals(capsToHash(hash, calculateEntityCapsString(discoverInfo)))) { return true; } } return false; }
/** * Get the features sorted correctly to calculate the ver attribute. * * @param info the DiscoverInfo containing the features * @return the sorted list of features. */ private List<String> getSortedFeature(DiscoverInfo info) { List<String> result = new ArrayList<String>(); Iterator<DiscoverInfo.Feature> it = info.getFeatures(); while (it.hasNext()) { DiscoverInfo.Feature feat = it.next(); result.add(feat.getVar()); } Collections.sort(result); return result; }
private boolean checkIfPrivacyIsSupported(XMPPConnection conn) { ServiceDiscoveryManager servDisc = ServiceDiscoveryManager.getInstanceFor(conn); DiscoverInfo info = null; try { String xmppHost = DNSUtil.resolveXMPPDomain(conn.getServiceName()).getHost(); info = servDisc.discoverInfo(xmppHost); } catch (XMPPException e) { // We could not query the server return false; } if (info != null) { for (Iterator<Feature> i = info.getFeatures(); i.hasNext(); ) { String s = i.next().getVar(); if (s.contains("jabber:iq:privacy")) { return true; } } } return false; }
/** * Set our own caps version. * * @param discoverInfo the {@link DiscoverInfo} that we'd like to map to the <tt>capsVersion</tt>. * @param capsVersion the new caps version */ public void setCurrentCapsVersion(DiscoverInfo discoverInfo, String capsVersion) { Caps caps = new Caps(getNode(), CapsPacketExtension.HASH_METHOD, capsVersion, null); /* * DiscoverInfo carries the node and the ver and we're now setting a new * ver so we should update the DiscoveryInfo. */ discoverInfo.setNode(caps.getNodeVer()); if (!caps.isValid(discoverInfo)) { throw new IllegalArgumentException( "The specified discoverInfo must be valid with respect" + " to the specified capsVersion"); } currentCapsVersion = capsVersion; addDiscoverInfoByCaps(caps, discoverInfo); fireCapsVerChanged(); }
RoomInfo(DiscoverInfo info) { super(); this.room = info.getFrom(); // Get the information based on the discovered features this.membersOnly = info.containsFeature("muc_membersonly"); this.moderated = info.containsFeature("muc_moderated"); this.nonanonymous = info.containsFeature("muc_nonanonymous"); this.passwordProtected = info.containsFeature("muc_passwordprotected"); this.persistent = info.containsFeature("muc_persistent"); // Get the information based on the discovered extended information Form form = Form.getFormFrom(info); if (form != null) { this.description = form.getField("muc#roominfo_description").getValues().next(); Iterator<String> values = form.getField("muc#roominfo_subject").getValues(); if (values.hasNext()) { this.subject = values.next(); } else { this.subject = ""; } this.occupantsCount = Integer.parseInt(form.getField("muc#roominfo_occupants").getValues().next()); } }
public static boolean containsFeature(String feature) { if (featureInfo == null) return true; return featureInfo.containsFeature(feature); }
/** * Returns true if the server supports publishing of items. A client may wish to publish items to * the server so that the server can provide items associated to the client. These items will be * returned by the server whenever the server receives a disco request targeted to the bare * address of the client (i.e. [email protected]). * * @param entityID the address of the XMPP entity. * @return true if the server supports publishing of items. * @throws XMPPException if the operation failed for some reason. */ public boolean canPublishItems(String entityID) throws XMPPException { DiscoverInfo info = discoverInfo(entityID); return info.containsFeature("http://jabber.org/protocol/disco#publish"); }
/** * Returns true if the server supports Flexible Offline Message Retrieval. When the server * supports Flexible Offline Message Retrieval it is possible to get the header of the offline * messages, get specific messages, delete specific messages, etc. * * @return a boolean indicating if the server supports Flexible Offline Message Retrieval. * @throws XMPPException If the user is not allowed to make this request. */ public boolean supportsFlexibleRetrieval() throws XMPPException { DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null); return info.containsFeature(namespace); }
/** * Calculates the <tt>String</tt> for a specific <tt>DiscoverInfo</tt> which is to be hashed in * order to compute the ver string for that <tt>DiscoverInfo</tt>. * * @param discoverInfo the <tt>DiscoverInfo</tt> for which the <tt>String</tt> to be hashed in * order to compute its ver string is to be calculated * @return the <tt>String</tt> for <tt>discoverInfo</tt> which is to be hashed in order to compute * its ver string */ private static String calculateEntityCapsString(DiscoverInfo discoverInfo) { StringBuilder bldr = new StringBuilder(); // Add identities { Iterator<DiscoverInfo.Identity> identities = discoverInfo.getIdentities(); SortedSet<DiscoverInfo.Identity> is = new TreeSet<DiscoverInfo.Identity>( new Comparator<DiscoverInfo.Identity>() { public int compare(DiscoverInfo.Identity i1, DiscoverInfo.Identity i2) { int category = i1.getCategory().compareTo(i2.getCategory()); if (category != 0) return category; int type = i1.getType().compareTo(i2.getType()); if (type != 0) return type; /* * TODO Sort by xml:lang. * * Since sort by xml:lang is currently missing, * use the last supported sort criterion i.e. * type. */ return type; } }); if (identities != null) while (identities.hasNext()) is.add(identities.next()); for (DiscoverInfo.Identity i : is) { bldr.append(i.getCategory()) .append('/') .append(i.getType()) .append("//") .append(i.getName()) .append('<'); } } // Add features { Iterator<DiscoverInfo.Feature> features = getDiscoverInfoFeatures(discoverInfo); SortedSet<String> fs = new TreeSet<String>(); if (features != null) while (features.hasNext()) fs.add(features.next().getVar()); for (String f : fs) bldr.append(f).append('<'); } DataForm extendedInfo = (DataForm) discoverInfo.getExtension("x", "jabber:x:data"); if (extendedInfo != null) { synchronized (extendedInfo) { SortedSet<FormField> fs = new TreeSet<FormField>( new Comparator<FormField>() { public int compare(FormField f1, FormField f2) { return f1.getVariable().compareTo(f2.getVariable()); } }); FormField formType = null; for (Iterator<FormField> fieldsIter = extendedInfo.getFields(); fieldsIter.hasNext(); ) { FormField f = fieldsIter.next(); if (!f.getVariable().equals("FORM_TYPE")) fs.add(f); else formType = f; } // Add FORM_TYPE values if (formType != null) formFieldValuesToCaps(formType.getValues(), bldr); // Add the other values for (FormField f : fs) { bldr.append(f.getVariable()).append('<'); formFieldValuesToCaps(f.getValues(), bldr); } } } return bldr.toString(); }
/** * Returns true if the server supports publishing of items. A client may wish to publish items to * the server so that the server can provide items associated to the client. These items will be * returned by the server whenever the server receives a disco request targeted to the bare * address of the client (i.e. [email protected]). * * @param DiscoverInfo the discover info packet to check. * @return true if the server supports publishing of items. */ public static boolean canPublishItems(DiscoverInfo info) { return info.containsFeature("http://jabber.org/protocol/disco#publish"); }
private DiscoverInfo cloneDiscoverInfo(DiscoverInfo disco) { return disco.clone(); }