# HG changeset patch # User Da Risk # Date 1279834476 -7200 # Node ID 4fb9df09ffdf88ff56be6a7b4cf1bf04056b4592 # Parent 300eddc3be4bd4a8319c1f77a9b31a82f12f33b3 #286 : Initial implementation of XEP-0115 : Entity Capabilities diff -r 300eddc3be4b -r 4fb9df09ffdf doc/asmack-beem/beem_patches/50-public-info-features.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/asmack-beem/beem_patches/50-public-info-features.patch Thu Jul 22 23:34:36 2010 +0200 @@ -0,0 +1,18 @@ +--- org/jivesoftware/smackx/packet/DiscoverInfo.java 2010-07-22 22:16:27.000000000 +0200 ++++ org/jivesoftware/smackx/packet/DiscoverInfo.java 2010-07-22 22:58:43.000000000 +0200 +@@ -62,7 +62,7 @@ + * + * @return an Iterator on the discovered features of an XMPP entity + */ +- Iterator getFeatures() { ++ public Iterator getFeatures() { + synchronized (features) { + return Collections.unmodifiableList(features).iterator(); + } +@@ -266,4 +266,4 @@ + return buf.toString(); + } + } +-} +\ Pas de fin de ligne à la fin du fichier. ++} diff -r 300eddc3be4b -r 4fb9df09ffdf libs/asmack-android-7-beem.jar Binary file libs/asmack-android-7-beem.jar has changed diff -r 300eddc3be4b -r 4fb9df09ffdf src/com/beem/project/beem/BeemService.java --- a/src/com/beem/project/beem/BeemService.java Wed Jul 21 23:29:41 2010 +0200 +++ b/src/com/beem/project/beem/BeemService.java Thu Jul 22 23:34:36 2010 +0200 @@ -51,6 +51,8 @@ import org.jivesoftware.smack.provider.PrivacyProvider; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smackx.provider.DelayInfoProvider; +import org.jivesoftware.smackx.provider.DiscoverInfoProvider; +import org.jivesoftware.smackx.provider.DiscoverItemsProvider; import org.jivesoftware.smackx.packet.ChatStateExtension; import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType; @@ -78,6 +80,7 @@ import com.beem.project.beem.utils.BeemBroadcastReceiver; import com.beem.project.beem.utils.BeemConnectivity; import com.beem.project.beem.utils.Status; +import com.beem.project.beem.smack.caps.CapsProvider; /** * This class is for the Beem service. @@ -319,6 +322,11 @@ // Delayed Delivery only the new version pm.addExtensionProvider("delay", "urn:xmpp:delay", new DelayInfoProvider()); + // Service Discovery # Items + pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider()); + // Service Discovery # Info + pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider()); + // Chat State ChatStateExtension.Provider chatState = new ChatStateExtension.Provider(); pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", chatState); @@ -327,6 +335,7 @@ pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", chatState); pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", chatState); pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", chatState); + pm.addExtensionProvider("c", "http://jabber.org/protocol/caps", new CapsProvider()); /* // Private Data Storage pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider()); @@ -344,10 +353,6 @@ pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider()); // Group Chat Invitations pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider()); - // Service Discovery # Items - pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider()); - // Service Discovery # Info - pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider()); // Data Forms pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider()); // MUC User diff -r 300eddc3be4b -r 4fb9df09ffdf src/com/beem/project/beem/service/XmppConnectionAdapter.java --- a/src/com/beem/project/beem/service/XmppConnectionAdapter.java Wed Jul 21 23:29:41 2010 +0200 +++ b/src/com/beem/project/beem/service/XmppConnectionAdapter.java Thu Jul 22 23:34:36 2010 +0200 @@ -76,6 +76,7 @@ import com.beem.project.beem.ui.Subscription; import com.beem.project.beem.utils.BeemBroadcastReceiver; import com.beem.project.beem.utils.Status; +import com.beem.project.beem.smack.caps.CapsManager; /** * This class implements an adapter for XMPPConnection. @@ -205,9 +206,6 @@ if (!mAdaptee.isConnected()) return false; try { - mAdaptee.login(mLogin, mPassword, mResource); - mChatManager = new BeemChatManager(mAdaptee.getChatManager(), mService); - mPrivacyListManager = new PrivacyListManagerAdapter(PrivacyListManager.getInstanceFor(mAdaptee)); this.initFeatures(); // pour declarer les features xmpp qu'on // supporte @@ -227,6 +225,9 @@ mAdaptee.addPacketListener(mSubscribePacketListener, filter); + mAdaptee.login(mLogin, mPassword, mResource); + mChatManager = new BeemChatManager(mAdaptee.getChatManager(), mService); + mPrivacyListManager = new PrivacyListManagerAdapter(PrivacyListManager.getInstanceFor(mAdaptee)); mService.resetStatus(); mService.initJingle(mAdaptee); @@ -392,7 +393,9 @@ sdm = new ServiceDiscoveryManager(mAdaptee); sdm.addFeature("http://jabber.org/protocol/disco#info"); sdm.addFeature("jabber:iq:privacy"); + sdm.addFeature("http://jabber.org/protocol/caps"); mChatStateManager = ChatStateManager.getInstance(mAdaptee); + CapsManager caps = new CapsManager(sdm, mAdaptee); } /** diff -r 300eddc3be4b -r 4fb9df09ffdf src/com/beem/project/beem/smack/caps/CapsExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/smack/caps/CapsExtension.java Thu Jul 22 23:34:36 2010 +0200 @@ -0,0 +1,78 @@ +package com.beem.project.beem.smack.caps; + +import org.jivesoftware.smack.packet.PacketExtension; + +public class CapsExtension implements PacketExtension { + + private String mVer; + private String mHash; + private String mNode; + private String mExt; + + + public CapsExtension(String hash, String node, String ver) { + mHash = hash; + mNode = node; + mVer = ver; + } + + public String getVer(){ + return mVer; + } + + public String getHash(){ + return mHash; + } + + public String getNode(){ + return mNode; + } + + public String getExt(){ + return mExt; + } + + public void setHash(String hash) { + mHash = hash; + } + + public void setVer(String ver) { + mVer = ver; + } + + public void setNode(String node) { + mNode = node; + } + + public void setExt(String ext) { + mExt = ext; + } + + @Override + public String getElementName() { + return "c"; + } + + @Override + public String getNamespace() { + return "http://jabber.org/protocol/caps"; + } + + @Override + public String toXML(){ + StringBuilder b = new StringBuilder("<"); + b.append(getElementName()); + b.append(" xmlns=\"").append(getNamespace()).append("\" "); + if (mHash != null){ + b.append("hash=\"").append(mHash).append("\" "); + } + if (mNode != null) + b.append("node=\"").append(mNode).append("\" "); + if (mVer != null) + b.append("ver=\"").append(mVer).append("\" "); + if (mExt!= null) + b.append("ext=\"").append(mExt).append("\" "); + b.append("/>"); + return b.toString(); + } +} diff -r 300eddc3be4b -r 4fb9df09ffdf src/com/beem/project/beem/smack/caps/CapsManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/smack/caps/CapsManager.java Thu Jul 22 23:34:36 2010 +0200 @@ -0,0 +1,183 @@ + +package com.beem.project.beem.smack.caps; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smack.util.collections.ReferenceMap; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.filter.PacketExtensionFilter; + +import java.util.Map; +import java.util.Iterator; +import java.util.Comparator; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.security.NoSuchAlgorithmException; +import java.security.MessageDigest; + +import org.jivesoftware.smack.util.StringUtils; + +public class CapsManager { + // the verCache should be stored on disk + Map mVerCache = new ReferenceMap(); + Map mJidCache = new ReferenceMap(); + + private ServiceDiscoveryManager mSdm; + private Connection mConnection; + private List mSupportedAlgorithm = new ArrayList(); + + public CapsManager(ServiceDiscoveryManager sdm, Connection conn) { + mSdm = sdm; + mConnection = conn; + init(); + } + + private void init() { + initSupportedAlgorithm(); + PacketFilter filter = new PacketExtensionFilter("c", "http://jabber.org/protocol/caps"); + mConnection.addPacketListener( new PacketListener() { + public void processPacket(Packet packet) { + PacketExtension p = packet.getExtension("c", "http://jabber.org/protocol/caps"); + CapsExtension caps = (CapsExtension) p; + if (!mVerCache.containsKey(caps.getVer())) + validate(packet.getFrom(), caps.getVer(), caps.getHash()); + } + }, filter); + } + + public DiscoverInfo getDiscoverInfo(String ver) { + return mVerCache.get(ver); + } + + public DiscoverInfo getDiscoverInfo(String jid, String ver) { + DiscoverInfo info = mVerCache.get(ver); + if (info == null) + info = mJidCache.get(jid); + return info; + } + + /** + * + * + * @param jid + * @param ver + * @param hashMethod + * @return + */ + private boolean validate(String jid, String ver, String hashMethod) { + try { + DiscoverInfo info = mSdm.discoverInfo(jid); + if (!mSupportedAlgorithm.contains(hashMethod)) { + mJidCache.put(jid, info); + return false; + } + String v = calculateVer(info, hashMethod); + boolean res = v.equals(ver); + if (res) + mVerCache.put(ver, info); + return res; + } catch (XMPPException e) { + e.printStackTrace(); + return false; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return false; + } + } + + private String calculateVer(DiscoverInfo info, String hashMethod) throws NoSuchAlgorithmException { + StringBuilder S = new StringBuilder(); + for(DiscoverInfo.Identity identity : getSortedIdentity(info)) { + String c = identity.getCategory(); + if (c != null) + S.append(c); + S.append('/'); + c = identity.getType(); + if (c != null) + S.append(c); + S.append('/'); + // Should add lang but it is not available +// c = identity.getType(); +// if (c != null) +// S.append(c); + S.append('/'); + c = identity.getName(); + if (c != null) + S.append(c); + S.append('<'); + } + for (String f : getSortedFeature(info)) { + S.append(f); + S.append('<'); + } + // Should add data form (XEP 0128) but it is not available + byte[] hash = getHash(hashMethod, S.toString().getBytes()); + return StringUtils.encodeBase64(hash); + } + + private List getSortedIdentity(DiscoverInfo info) { + List result = new ArrayList(); + Iterator it = info.getIdentities(); + while (it.hasNext()) { + DiscoverInfo.Identity id = it.next(); + result.add(id); + } + Collections.sort(result, new Comparator() { + 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; + } + + private List getSortedFeature(DiscoverInfo info) { + List result = new ArrayList(); + Iterator it = info.getFeatures(); + while (it.hasNext()) { + DiscoverInfo.Feature feat = it.next(); + result.add(feat.getVar()); + } + Collections.sort(result); + return result; + } + + private byte[] getHash(String algo, byte[] data) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance(algo); + return md.digest(data); + } + + private void initSupportedAlgorithm() { + String algo[] = new String[] {"md2", "md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512" }; + for (String a : algo) { + try { + MessageDigest md = MessageDigest.getInstance(a); + mSupportedAlgorithm.add(a); + } catch(NoSuchAlgorithmException e) { + System.err.println("Hash algorithm " + a + " not supported"); + } + } + } + +} diff -r 300eddc3be4b -r 4fb9df09ffdf src/com/beem/project/beem/smack/caps/CapsProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/smack/caps/CapsProvider.java Thu Jul 22 23:34:36 2010 +0200 @@ -0,0 +1,21 @@ + +package com.beem.project.beem.smack.caps; + +import org.xmlpull.v1.XmlPullParser; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.packet.PacketExtension; + +public class CapsProvider implements PacketExtensionProvider { + + @Override + public PacketExtension parseExtension(XmlPullParser parser) { + String ver = parser.getAttributeValue("", "ver"); + String hash = parser.getAttributeValue("", "hash"); + String node = parser.getAttributeValue("", "node"); + String ext = parser.getAttributeValue("", "ext"); + CapsExtension e = new CapsExtension(hash, node, ver); + e.setExt(ext); + return e; + } + +}