From 2fe8489d368a371aefbfbe66e74621a8df14cdc2 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Fri, 10 Mar 2017 17:01:45 +0100 Subject: Rename vala-xmpp library to xmpp-vala --- .../src/module/xep/0115_entitiy_capabilities.vala | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala (limited to 'xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala') diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala new file mode 100644 index 00000000..472eb9bd --- /dev/null +++ b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala @@ -0,0 +1,125 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.EntityCapabilities { + private const string NS_URI = "http://jabber.org/protocol/caps"; + + public class Module : XmppStreamModule { + public const string ID = "0115_entity_capabilities"; + + private string own_ver_hash; + private Storage storage; + + public Module(Storage storage) { + this.storage = storage; + } + + private string get_own_hash(XmppStream stream) { + if (own_ver_hash == null) { + own_ver_hash = compute_hash(ServiceDiscovery.Module.get_module(stream).identities, ServiceDiscovery.Flag.get_flag(stream).features); + } + return own_ver_hash; + } + + public override void attach(XmppStream stream) { + ServiceDiscovery.Module.require(stream); + Presence.Module.require(stream); + Presence.Module.get_module(stream).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); + Presence.Module.get_module(stream).received_presence.connect(on_received_presence); + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + } + + public override void detach(XmppStream stream) { + Presence.Module.get_module(stream).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); + Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf("EntityCapabilitiesModule required but not attached!\n"); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { + if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE) { + presence.stanza.put_node(new StanzaNode.build("c", NS_URI).add_self_xmlns() + .put_attribute("hash", "sha-1") + .put_attribute("node", "http://dino-im.org") + .put_attribute("ver", get_own_hash(stream))); + } + } + + private void on_received_presence(XmppStream stream, Presence.Stanza presence) { + StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); + if (c_node != null) { + string ver_attribute = c_node.get_attribute("ver", NS_URI); + ArrayList capabilities = storage.get_features(ver_attribute); + if (capabilities.size == 0) { + ServiceDiscovery.Module.get_module(stream) + .request_info(stream, presence.from, new ServiceDiscoveryInfoResponseListenerImpl(storage, ver_attribute)); + } else { + ServiceDiscovery.Flag.get_flag(stream).set_entitiy_features(presence.from, capabilities); + } + } + } + + private class ServiceDiscoveryInfoResponseListenerImpl : ServiceDiscovery.InfoResponseListener, Object { + private Storage storage; + private string entity; + + public ServiceDiscoveryInfoResponseListenerImpl(Storage storage, string entity) { + this.storage = storage; + this.entity = entity; + } + public void on_result(XmppStream stream, ServiceDiscovery.InfoResult query_result) { + if (compute_hash(query_result.identities, query_result.features) == entity) { + storage.store_features(entity, query_result.features); + } + } + } + + private static string compute_hash(ArrayList identities, ArrayList features) { + identities.sort(compare_identities); + features.sort(); + + string s = ""; + foreach (ServiceDiscovery.Identity identity in identities) { + string s_identity = identity.category + "/" + identity.type_ + "//"; + if (identity.name != null) s_identity += identity.name; + s_identity += "<"; + s += s_identity; + } + foreach (string feature in features) { + s += feature + "<"; + } + + Checksum c = new Checksum(ChecksumType.SHA1); + c.update(s.data, -1); + size_t size = 20; + uint8[] buf = new uint8[size]; + c.get_digest(buf, ref size); + + return Base64.encode(buf); + } + + private static int compare_identities(ServiceDiscovery.Identity a, ServiceDiscovery.Identity b) { + int category_comp = a.category.collate(b.category); + if (category_comp != 0) return category_comp; + int type_comp = a.type_.collate(b.type_); + if (type_comp != 0) return type_comp; + // TODO lang + return 0; + } + } + + public interface Storage : Object { + public abstract void store_features(string entitiy, ArrayList capabilities); + public abstract ArrayList get_features(string entitiy); + } +} -- cgit v1.2.3-54-g00ecf