From 2d31d9d4706d6fea870f9081a1451448f032473d Mon Sep 17 00:00:00 2001 From: fiaxh Date: Thu, 3 Sep 2020 22:52:07 +0200 Subject: Cleanup --- libdino/src/service/entity_info.vala | 3 +- xmpp-vala/CMakeLists.txt | 2 +- .../module/xep/0030_service_discovery/flag.vala | 6 - .../module/xep/0030_service_discovery/module.vala | 1 - .../src/module/xep/0115_entitiy_capabilities.vala | 175 --------------------- .../src/module/xep/0115_entity_capabilities.vala | 175 +++++++++++++++++++++ 6 files changed, 178 insertions(+), 184 deletions(-) delete mode 100644 xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala create mode 100644 xmpp-vala/src/module/xep/0115_entity_capabilities.vala diff --git a/libdino/src/service/entity_info.vala b/libdino/src/service/entity_info.vala index 7a78b154..7eed2583 100644 --- a/libdino/src/service/entity_info.vala +++ b/libdino/src/service/entity_info.vala @@ -97,7 +97,8 @@ public class EntityInfo : StreamInteractionModule, Object { string? caps_hash = EntityCapabilities.get_caps_hash(presence); if (caps_hash == null) return; - /*db.entity.upsert() + /* TODO check might_be_groupchat before storing + db.entity.upsert() .value(db.entity.account_id, account.id, true) .value(db.entity.jid_id, db.get_jid_id(presence.from), true) .value(db.entity.resource, presence.from.resourcepart, true) diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt index f2e89b4e..94c7f75b 100644 --- a/xmpp-vala/CMakeLists.txt +++ b/xmpp-vala/CMakeLists.txt @@ -74,7 +74,7 @@ SOURCES "src/module/xep/0082_date_time_profiles.vala" "src/module/xep/0084_user_avatars.vala" "src/module/xep/0085_chat_state_notifications.vala" - "src/module/xep/0115_entitiy_capabilities.vala" + "src/module/xep/0115_entity_capabilities.vala" "src/module/xep/0166_jingle.vala" "src/module/xep/0184_message_delivery_receipts.vala" "src/module/xep/0191_blocking_command.vala" diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala index ab311727..67729f5d 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala @@ -5,8 +5,6 @@ namespace Xmpp.Xep.ServiceDiscovery { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "service_discovery"); - private HashMap?> entity_items = new HashMap?>(Jid.hash_func, Jid.equals_func); - private Gee.Set own_features_ = new HashSet(); public Gee.List own_features { owned get { @@ -21,10 +19,6 @@ public class Flag : XmppStreamFlag { owned get { return own_identities_.read_only_view; } } - public void set_entity_items(Jid jid, Gee.List? features) { - entity_items[jid] = features; - } - public void add_own_feature(string feature) { if (own_features_.contains(feature)) { warning("Tried to add the feature %s a second time".printf(feature)); diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala index c61d3ab0..0b573a13 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala @@ -80,7 +80,6 @@ public class Module : XmppStreamModule, Iq.Handler { Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); ItemsResult? result = ItemsResult.create_from_iq(iq_result); - stream.get_flag(Flag.IDENTITY).set_entity_items(iq_result.from, result != null ? result.items : null); return result; } diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala deleted file mode 100644 index 8e15e0c2..00000000 --- a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala +++ /dev/null @@ -1,175 +0,0 @@ -using Gee; - -namespace Xmpp.Xep.EntityCapabilities { - private const string NS_URI = "http://jabber.org/protocol/caps"; - - private Regex? sha1_base64_regex = null; - - private Regex get_sha1_base64_regex() { - if (sha1_base64_regex == null) { - sha1_base64_regex = /^[A-Za-z0-9+\/]{27}=$/; - } - return sha1_base64_regex; - } - - public string? get_caps_hash(Presence.Stanza presence) { - Regex sha1_base64_regex = get_sha1_base64_regex(); - StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); - if (c_node == null) return null; - string? ver_attribute = c_node.get_attribute("ver", NS_URI); - if (ver_attribute == null || !sha1_base64_regex.match(ver_attribute)) return null; - return ver_attribute; - } - - public string? get_server_caps_hash(XmppStream stream) { - StanzaNode? node = stream.features.get_subnode("c", NS_URI); - if (node == null) return null; - - string? ver_attribute = node.get_attribute("ver", NS_URI); - if (ver_attribute == null) return null; - - return ver_attribute; - } - - public class Module : XmppStreamModule { - public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "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(stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_identities, stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_features, new ArrayList()); - } - return own_ver_hash; - } - - public override void attach(XmppStream stream) { - stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); - stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); - } - - public override void detach(XmppStream stream) { - stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); - stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return IDENTITY.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", "https://dino.im") - .put_attribute("ver", get_own_hash(stream))); - } - } - - public static string compute_hash_for_info_result(ServiceDiscovery.InfoResult info_result) { - Gee.List data_forms = new ArrayList(); - foreach (StanzaNode node in info_result.iq.stanza.get_deep_subnodes(ServiceDiscovery.NS_URI_INFO + ":query", DataForms.NS_URI + ":x")) { - data_forms.add(DataForms.DataForm.create_from_node(node)); - } - - return compute_hash(info_result.identities, info_result.features, data_forms); - } - - public static string compute_hash(Gee.Set identities_set, Gee.List features, Gee.List data_forms) { - var identities = new ArrayList(); - foreach (var identity in identities_set) identities.add(identity); - - identities.sort(compare_identities); - features.sort(); - - StringBuilder sb = new StringBuilder(); - foreach (ServiceDiscovery.Identity identity in identities) { - sb.append(sanitize(identity.category)) - .append("/") - .append(sanitize(identity.type_)) - .append("//"); - if (identity.name != null) { - sb.append(sanitize(identity.name)); - } - sb.append("<"); - } - foreach (string feature in features) { - sb.append(sanitize(feature)) - .append("<"); - } - - data_forms.sort(compare_data_forms); - foreach (DataForms.DataForm data_form in data_forms) { - if (data_form.form_type == null) { - // If [..] the FORM_TYPE field is not of type "hidden" or the form does not include a FORM_TYPE field, ignore the form but continue processing. (XEP-0115 5.4) - continue; - } - sb.append(sanitize(data_form.form_type)) - .append("<"); - - data_form.fields.sort(compare_data_fields); - foreach (DataForms.DataForm.Field field in data_form.fields) { - sb.append(sanitize(field.var)) - .append("<"); - Gee.List values = field.get_values(); - values.sort(); - foreach (string value in values) { - sb.append(sanitize(value ?? "")) - .append("<"); - } - } - } - - Checksum c = new Checksum(ChecksumType.SHA1); - c.update(sb.str.data, -1); - size_t size = 20; - uint8[] buf = new uint8[size]; - c.get_digest(buf, ref size); - - return Base64.encode(buf); - } - - /* - * If the four characters '&', 'l', 't', ';' appear consecutively in any of the factors of the verification - * string S [...] then that string of characters MUST be treated as literally '<' and MUST NOT be converted to - * the character '<', because completing such a conversion would open the protocol to trivial attacks. (XEP-0115 5.1) - */ - private static string sanitize(string s) { - return s.replace("<", "<"); - } - - 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; - } - - private static int compare_data_forms(DataForms.DataForm a, DataForms.DataForm b) { - if (a.form_type != null && b.form_type != null) { - return a.form_type.collate(b.form_type); - } - return 0; - } - - private static int compare_data_fields(DataForms.DataForm.Field a, DataForms.DataForm.Field b) { - if (a.var != null && b.var != null) { - return a.var.collate(b.var); - } - return 0; - } - } - - public interface Storage : Object { - public abstract void store_identities(string entity, Gee.Set identities); - public abstract void store_features(string entity, Gee.List capabilities); - public abstract ServiceDiscovery.Identity? get_identities(string entity); - public abstract Gee.List get_features(string entity); - } -} diff --git a/xmpp-vala/src/module/xep/0115_entity_capabilities.vala b/xmpp-vala/src/module/xep/0115_entity_capabilities.vala new file mode 100644 index 00000000..8e15e0c2 --- /dev/null +++ b/xmpp-vala/src/module/xep/0115_entity_capabilities.vala @@ -0,0 +1,175 @@ +using Gee; + +namespace Xmpp.Xep.EntityCapabilities { + private const string NS_URI = "http://jabber.org/protocol/caps"; + + private Regex? sha1_base64_regex = null; + + private Regex get_sha1_base64_regex() { + if (sha1_base64_regex == null) { + sha1_base64_regex = /^[A-Za-z0-9+\/]{27}=$/; + } + return sha1_base64_regex; + } + + public string? get_caps_hash(Presence.Stanza presence) { + Regex sha1_base64_regex = get_sha1_base64_regex(); + StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); + if (c_node == null) return null; + string? ver_attribute = c_node.get_attribute("ver", NS_URI); + if (ver_attribute == null || !sha1_base64_regex.match(ver_attribute)) return null; + return ver_attribute; + } + + public string? get_server_caps_hash(XmppStream stream) { + StanzaNode? node = stream.features.get_subnode("c", NS_URI); + if (node == null) return null; + + string? ver_attribute = node.get_attribute("ver", NS_URI); + if (ver_attribute == null) return null; + + return ver_attribute; + } + + public class Module : XmppStreamModule { + public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "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(stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_identities, stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_features, new ArrayList()); + } + return own_ver_hash; + } + + public override void attach(XmppStream stream) { + stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); + stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); + } + + public override void detach(XmppStream stream) { + stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); + stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return IDENTITY.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", "https://dino.im") + .put_attribute("ver", get_own_hash(stream))); + } + } + + public static string compute_hash_for_info_result(ServiceDiscovery.InfoResult info_result) { + Gee.List data_forms = new ArrayList(); + foreach (StanzaNode node in info_result.iq.stanza.get_deep_subnodes(ServiceDiscovery.NS_URI_INFO + ":query", DataForms.NS_URI + ":x")) { + data_forms.add(DataForms.DataForm.create_from_node(node)); + } + + return compute_hash(info_result.identities, info_result.features, data_forms); + } + + public static string compute_hash(Gee.Set identities_set, Gee.List features, Gee.List data_forms) { + var identities = new ArrayList(); + foreach (var identity in identities_set) identities.add(identity); + + identities.sort(compare_identities); + features.sort(); + + StringBuilder sb = new StringBuilder(); + foreach (ServiceDiscovery.Identity identity in identities) { + sb.append(sanitize(identity.category)) + .append("/") + .append(sanitize(identity.type_)) + .append("//"); + if (identity.name != null) { + sb.append(sanitize(identity.name)); + } + sb.append("<"); + } + foreach (string feature in features) { + sb.append(sanitize(feature)) + .append("<"); + } + + data_forms.sort(compare_data_forms); + foreach (DataForms.DataForm data_form in data_forms) { + if (data_form.form_type == null) { + // If [..] the FORM_TYPE field is not of type "hidden" or the form does not include a FORM_TYPE field, ignore the form but continue processing. (XEP-0115 5.4) + continue; + } + sb.append(sanitize(data_form.form_type)) + .append("<"); + + data_form.fields.sort(compare_data_fields); + foreach (DataForms.DataForm.Field field in data_form.fields) { + sb.append(sanitize(field.var)) + .append("<"); + Gee.List values = field.get_values(); + values.sort(); + foreach (string value in values) { + sb.append(sanitize(value ?? "")) + .append("<"); + } + } + } + + Checksum c = new Checksum(ChecksumType.SHA1); + c.update(sb.str.data, -1); + size_t size = 20; + uint8[] buf = new uint8[size]; + c.get_digest(buf, ref size); + + return Base64.encode(buf); + } + + /* + * If the four characters '&', 'l', 't', ';' appear consecutively in any of the factors of the verification + * string S [...] then that string of characters MUST be treated as literally '<' and MUST NOT be converted to + * the character '<', because completing such a conversion would open the protocol to trivial attacks. (XEP-0115 5.1) + */ + private static string sanitize(string s) { + return s.replace("<", "<"); + } + + 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; + } + + private static int compare_data_forms(DataForms.DataForm a, DataForms.DataForm b) { + if (a.form_type != null && b.form_type != null) { + return a.form_type.collate(b.form_type); + } + return 0; + } + + private static int compare_data_fields(DataForms.DataForm.Field a, DataForms.DataForm.Field b) { + if (a.var != null && b.var != null) { + return a.var.collate(b.var); + } + return 0; + } + } + + public interface Storage : Object { + public abstract void store_identities(string entity, Gee.Set identities); + public abstract void store_features(string entity, Gee.List capabilities); + public abstract ServiceDiscovery.Identity? get_identities(string entity); + public abstract Gee.List get_features(string entity); + } +} -- cgit v1.2.3-54-g00ecf