From 12cd56612dd6edd056e2cd8aae59ea3ae8f05d1e Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sun, 29 Mar 2020 20:23:47 +0200 Subject: [PATCH] Store entity identity info, use it in conversation list tooltips --- libdino/CMakeLists.txt | 1 + libdino/src/application.vala | 1 + libdino/src/service/database.vala | 55 +++++++---- .../service/entity_capabilities_storage.vala | 52 ++++++++++- libdino/src/service/entity_info.vala | 67 ++++++++++++++ libdino/src/service/module_manager.vala | 9 +- libdino/src/service/stream_interactor.vala | 2 +- main/CMakeLists.txt | 6 +- .../conversation_row.ui | 0 .../conversation_selector/chat_row_tooltip.ui | 22 ----- .../icons/dino-device-desktop-symbolic.svg | 5 + .../data/icons/dino-device-phone-symbolic.svg | 6 ++ .../conversation_selector_row.vala | 92 ++++++++++++------- .../xep/0030_service_discovery/identity.vala | 6 +- .../module/xep/0115_entitiy_capabilities.vala | 40 +++++--- 15 files changed, 260 insertions(+), 104 deletions(-) create mode 100644 libdino/src/service/entity_info.vala rename main/data/{conversation_selector => }/conversation_row.ui (100%) delete mode 100644 main/data/conversation_selector/chat_row_tooltip.ui create mode 100644 main/data/icons/dino-device-desktop-symbolic.svg create mode 100644 main/data/icons/dino-device-phone-symbolic.svg diff --git a/libdino/CMakeLists.txt b/libdino/CMakeLists.txt index 31ba69d3..7a05b8ab 100644 --- a/libdino/CMakeLists.txt +++ b/libdino/CMakeLists.txt @@ -34,6 +34,7 @@ SOURCES src/service/counterpart_interaction_manager.vala src/service/database.vala src/service/entity_capabilities_storage.vala + src/service/entity_info.vala src/service/file_manager.vala src/service/jingle_file_transfers.vala src/service/message_processor.vala diff --git a/libdino/src/application.vala b/libdino/src/application.vala index e7e02be7..ac9a4e4b 100644 --- a/libdino/src/application.vala +++ b/libdino/src/application.vala @@ -44,6 +44,7 @@ public interface Application : GLib.Application { NotificationEvents.start(stream_interactor); SearchProcessor.start(stream_interactor, db); Register.start(stream_interactor, db); + EntityInfo.start(stream_interactor, db); create_actions(); diff --git a/libdino/src/service/database.vala b/libdino/src/service/database.vala index 34bbea7a..ebf05637 100644 --- a/libdino/src/service/database.vala +++ b/libdino/src/service/database.vala @@ -7,7 +7,7 @@ using Dino.Entities; namespace Dino { public class Database : Qlite.Database { - private const int VERSION = 12; + private const int VERSION = 13; public class AccountTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; @@ -35,6 +35,21 @@ public class Database : Qlite.Database { } } + public class EntityTable : Table { + public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; + public Column account_id = new Column.Integer("account_id"); + public Column jid_id = new Column.Integer("jid_id"); + public Column resource = new Column.Text("resource"); + public Column caps_hash = new Column.Text("caps_hash"); + public Column last_seen = new Column.Long("last_seen"); + + internal EntityTable(Database db) { + base(db, "entity"); + init({id, account_id, jid_id, resource, caps_hash, last_seen}); + unique({account_id, jid_id, resource}, "IGNORE"); + } + } + public class ContentItemTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column conversation_id = new Column.Integer("conversation_id") { not_null = true }; @@ -162,6 +177,20 @@ public class Database : Qlite.Database { } } + public class EntityIdentityTable : Table { + public Column entity = new Column.Text("entity"); + public Column category = new Column.Text("category"); + public Column type = new Column.Text("type"); + public Column name = new Column.Text("name"); + + internal EntityIdentityTable(Database db) { + base(db, "entity_identity"); + init({entity, category, name, type}); + unique({entity, category, type}, "IGNORE"); + index("entity_identity_idx", {entity}); + } + } + public class EntityFeatureTable : Table { public Column entity = new Column.Text("entity"); public Column feature = new Column.Text("feature"); @@ -215,12 +244,14 @@ public class Database : Qlite.Database { public AccountTable account { get; private set; } public JidTable jid { get; private set; } + public EntityTable entity { get; private set; } public ContentItemTable content_item { get; private set; } public MessageTable message { get; private set; } public RealJidTable real_jid { get; private set; } public FileTransferTable file_transfer { get; private set; } public ConversationTable conversation { get; private set; } public AvatarTable avatar { get; private set; } + public EntityIdentityTable entity_identity { get; private set; } public EntityFeatureTable entity_feature { get; private set; } public RosterTable roster { get; private set; } public MamCatchupTable mam_catchup { get; private set; } @@ -234,17 +265,19 @@ public class Database : Qlite.Database { base(fileName, VERSION); account = new AccountTable(this); jid = new JidTable(this); + entity = new EntityTable(this); content_item = new ContentItemTable(this); message = new MessageTable(this); real_jid = new RealJidTable(this); file_transfer = new FileTransferTable(this); conversation = new ConversationTable(this); avatar = new AvatarTable(this); + entity_identity = new EntityIdentityTable(this); entity_feature = new EntityFeatureTable(this); roster = new RosterTable(this); mam_catchup = new MamCatchupTable(this); settings = new SettingsTable(this); - init({ account, jid, content_item, message, real_jid, file_transfer, conversation, avatar, entity_feature, roster, mam_catchup, settings }); + init({ account, jid, entity, content_item, message, real_jid, file_transfer, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, settings }); try { exec("PRAGMA synchronous=0"); } catch (Error e) { } @@ -433,24 +466,6 @@ public class Database : Qlite.Database { return ret; } - public void add_entity_features(string entity, Gee.List features) { - foreach (string feature in features) { - entity_feature.insert() - .value(entity_feature.entity, entity) - .value(entity_feature.feature, feature) - .perform(); - } - } - - public Gee.List get_entity_features(string entity) { - ArrayList ret = new ArrayList(); - foreach (Row row in entity_feature.select({entity_feature.feature}).with(entity_feature.entity, "=", entity)) { - ret.add(row[entity_feature.feature]); - } - return ret; - } - - public int get_jid_id(Jid jid_obj) { var bare_jid = jid_obj.bare_jid; if (jid_table_reverse.has_key(bare_jid)) { diff --git a/libdino/src/service/entity_capabilities_storage.vala b/libdino/src/service/entity_capabilities_storage.vala index 94d9d88e..d9f66913 100644 --- a/libdino/src/service/entity_capabilities_storage.vala +++ b/libdino/src/service/entity_capabilities_storage.vala @@ -1,23 +1,69 @@ using Gee; - +using Qlite; using Xmpp; +using Xmpp.Xep.ServiceDiscovery; namespace Dino { public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object { private Database db; + private HashMap> features_cache = new HashMap>(); + private HashMap identity_cache = new HashMap(); public EntityCapabilitiesStorage(Database db) { this.db = db; } public void store_features(string entity, Gee.List features) { - db.add_entity_features(entity, features); + foreach (string feature in features) { + db.entity_feature.insert() + .value(db.entity_feature.entity, entity) + .value(db.entity_feature.feature, feature) + .perform(); + } + } + + public void store_identities(string entity, Gee.List identities) { + foreach (Identity identity in identities) { + if (identity.category == Identity.CATEGORY_CLIENT) { + db.entity_identity.insert() + .value(db.entity_identity.entity, entity) + .value(db.entity_identity.category, identity.category) + .value(db.entity_identity.type, identity.type_) + .value(db.entity_identity.name, identity.name) + .perform(); + return; + } + } } public Gee.List get_features(string entity) { - return db.get_entity_features(entity); + Gee.List? features = features_cache[entity]; + if (features != null) { + return features; + } + + features = new ArrayList(); + foreach (Row row in db.entity_feature.select({db.entity_feature.feature}).with(db.entity_feature.entity, "=", entity)) { + features.add(row[db.entity_feature.feature]); + } + features_cache[entity] = features; + return features; + } + + public Identity? get_identities(string entity) { + Identity? identity = identity_cache[entity]; + if (identity != null) { + return identity; + } + + RowOption row = db.entity_identity.select().with(db.entity_identity.entity, "=", entity).single().row(); + if (row.is_present()) { + identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.name]); + } + identity_cache[entity] = identity; + return identity; } } } diff --git a/libdino/src/service/entity_info.vala b/libdino/src/service/entity_info.vala new file mode 100644 index 00000000..8efea7e5 --- /dev/null +++ b/libdino/src/service/entity_info.vala @@ -0,0 +1,67 @@ +using Gee; +using Dino.Entities; +using Xmpp; +using Xmpp.Xep; +using Xmpp.Xep.ServiceDiscovery; + +namespace Dino { +public class EntityInfo : StreamInteractionModule, Object { + public static ModuleIdentity IDENTITY = new ModuleIdentity("entity_info"); + public string id { get { return IDENTITY.id; } } + + private StreamInteractor stream_interactor; + private Database db; + private EntityCapabilitiesStorage entity_capabilities_storage; + + + private HashMap entity_caps_hashes = new HashMap(Jid.hash_func, Jid.equals_func); + + public static void start(StreamInteractor stream_interactor, Database db) { + EntityInfo m = new EntityInfo(stream_interactor, db); + stream_interactor.add_module(m); + } + + private EntityInfo(StreamInteractor stream_interactor, Database db) { + this.stream_interactor = stream_interactor; + this.db = db; + this.entity_capabilities_storage = new EntityCapabilitiesStorage(db); + + stream_interactor.account_added.connect(on_account_added); + stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules); + } + + public Identity? get_identity(Account account, Jid jid) { + string? caps_hash = entity_caps_hashes[jid]; + if (caps_hash == null) return null; + return entity_capabilities_storage.get_identities(caps_hash); + } + + private void on_received_available_presence(Account account, Presence.Stanza presence) { + bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(presence.from.bare_jid, account); + if (is_gc) return; + + string? caps_hash = EntityCapabilities.get_caps_hash(presence); + if (caps_hash == null) return; + + 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) + .value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix()) + .value(db.entity.caps_hash, caps_hash) + .perform(); + + if (caps_hash != null) { + entity_caps_hashes[presence.from] = caps_hash; + } + } + + private void on_account_added(Account account) { + stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available.connect((stream, presence) => on_received_available_presence(account, presence)); + } + + private void initialize_modules(Account account, ArrayList modules) { + modules.add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); + } +} +} diff --git a/libdino/src/service/module_manager.vala b/libdino/src/service/module_manager.vala index a3f56652..0cd76a14 100644 --- a/libdino/src/service/module_manager.vala +++ b/libdino/src/service/module_manager.vala @@ -8,14 +8,8 @@ namespace Dino { public class ModuleManager { private HashMap> module_map = new HashMap>(Account.hash_func, Account.equals_func); - private EntityCapabilitiesStorage entity_capabilities_storage; - public signal void initialize_account_modules(Account account, ArrayList modules); - public ModuleManager(Database db) { - entity_capabilities_storage = new EntityCapabilitiesStorage(db); - } - public T? get_module(Account account, Xmpp.ModuleIdentity identity) { if (identity == null) return null; lock (module_map) { @@ -59,7 +53,7 @@ public class ModuleManager { module_map[account].add(new Bind.Module(account.resourcepart)); module_map[account].add(new Session.Module()); module_map[account].add(new Roster.Module()); - module_map[account].add(new Xep.ServiceDiscovery.Module.with_identity("client", "pc")); + module_map[account].add(new Xep.ServiceDiscovery.Module.with_identity("client", "pc", "Dino")); module_map[account].add(new Xep.PrivateXmlStorage.Module()); module_map[account].add(new Xep.Bookmarks.Module()); module_map[account].add(new Xep.Bookmarks2.Module()); @@ -69,7 +63,6 @@ public class ModuleManager { module_map[account].add(new Xep.MessageCarbons.Module()); module_map[account].add(new Xep.Muc.Module()); module_map[account].add(new Xep.Pubsub.Module()); - module_map[account].add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); module_map[account].add(new Xep.MessageDeliveryReceipts.Module()); module_map[account].add(new Xep.BlockingCommand.Module()); module_map[account].add(new Xep.ChatStateNotifications.Module()); diff --git a/libdino/src/service/stream_interactor.vala b/libdino/src/service/stream_interactor.vala index a1770bb8..1ace195d 100644 --- a/libdino/src/service/stream_interactor.vala +++ b/libdino/src/service/stream_interactor.vala @@ -17,7 +17,7 @@ public class StreamInteractor : Object { private ArrayList modules = new ArrayList(); public StreamInteractor(Database db) { - module_manager = new ModuleManager(db); + module_manager = new ModuleManager(); connection_manager = new ConnectionManager(module_manager); connection_manager.stream_opened.connect(on_stream_opened); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 5f5b7831..5dc69dcb 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -28,6 +28,9 @@ set(RESOURCE_LIST icons/im.dino.Dino-symbolic.svg icons/dino-tick-symbolic.svg + icons/dino-device-desktop-symbolic.svg + icons/dino-device-phone-symbolic.svg + icons/dino-file-document-symbolic.svg icons/dino-file-download-symbolic.svg icons/dino-file-image-symbolic.svg @@ -45,11 +48,10 @@ set(RESOURCE_LIST contact_details_dialog.ui conversation_list_titlebar.ui conversation_list_titlebar_csd.ui + conversation_row.ui conversation_view.ui emojichooser.ui global_search.ui - conversation_selector/chat_row_tooltip.ui - conversation_selector/conversation_row.ui conversation_content_view/image_toolbar.ui conversation_content_view/item_metadata_header.ui conversation_content_view/view.ui diff --git a/main/data/conversation_selector/conversation_row.ui b/main/data/conversation_row.ui similarity index 100% rename from main/data/conversation_selector/conversation_row.ui rename to main/data/conversation_row.ui diff --git a/main/data/conversation_selector/chat_row_tooltip.ui b/main/data/conversation_selector/chat_row_tooltip.ui deleted file mode 100644 index 6596cd62..00000000 --- a/main/data/conversation_selector/chat_row_tooltip.ui +++ /dev/null @@ -1,22 +0,0 @@ - - - - vertical - True - - - 0 - True - - - - - - - - vertical - True - - - - diff --git a/main/data/icons/dino-device-desktop-symbolic.svg b/main/data/icons/dino-device-desktop-symbolic.svg new file mode 100644 index 00000000..4e4e02ab --- /dev/null +++ b/main/data/icons/dino-device-desktop-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/main/data/icons/dino-device-phone-symbolic.svg b/main/data/icons/dino-device-phone-symbolic.svg new file mode 100644 index 00000000..64a69e17 --- /dev/null +++ b/main/data/icons/dino-device-phone-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/main/src/ui/conversation_selector/conversation_selector_row.vala b/main/src/ui/conversation_selector/conversation_selector_row.vala index 80257a6d..6234e9c8 100644 --- a/main/src/ui/conversation_selector/conversation_selector_row.vala +++ b/main/src/ui/conversation_selector/conversation_selector_row.vala @@ -9,7 +9,7 @@ using Xmpp; namespace Dino.Ui { -[GtkTemplate (ui = "/im/dino/Dino/conversation_selector/conversation_row.ui")] +[GtkTemplate (ui = "/im/dino/Dino/conversation_row.ui")] public class ConversationSelectorRow : ListBoxRow { [GtkChild] protected AvatarImage image; @@ -217,29 +217,6 @@ public class ConversationSelectorRow : ListBoxRow { message_label.label = message_label.label; } - protected Box get_fulljid_box(Jid full_jid) { - Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; - - Show show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); - Image image = new Image() { visible=true }; - if (show.as == Show.AWAY) { - image.set_from_icon_name("dino-status-away", IconSize.SMALL_TOOLBAR); - } else if (show.as == Show.XA || show.as == Show.DND) { - image.set_from_icon_name("dino-status-dnd", IconSize.SMALL_TOOLBAR); - } else if (show.as == Show.CHAT) { - image.set_from_icon_name("dino-status-chat", IconSize.SMALL_TOOLBAR); - } else { - image.set_from_icon_name("dino-status-online", IconSize.SMALL_TOOLBAR); - } - box.add(image); - - Label resource = new Label(full_jid.resourcepart) { visible=true }; - resource.xalign = 0; - box.add(resource); - box.show_all(); - return box; - } - public override void state_flags_changed(StateFlags flags) { StateFlags curr_flags = get_state_flags(); if ((curr_flags & StateFlags.PRELIGHT) != 0) { @@ -251,21 +228,68 @@ public class ConversationSelectorRow : ListBoxRow { } } - private Widget generate_tooltip() { - Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_selector/chat_row_tooltip.ui"); - Box main_box = builder.get_object("main_box") as Box; - Box inner_box = builder.get_object("inner_box") as Box; - Label jid_label = builder.get_object("jid_label") as Label; + private static Regex dino_resource_regex = /^dino\.[a-f0-9]{8}$/; - jid_label.label = conversation.counterpart.to_string(); + private Widget generate_tooltip() { + Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=2, margin_start=5, margin_end=5, margin_top=2, margin_bottom=2, visible=true }; + + Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0, visible=true }; + label.attributes = new AttrList(); + label.attributes.insert(attr_weight_new(Weight.BOLD)); + + grid.attach(label, 0, 0, 2, 1); Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account); - if (full_jids != null) { - for (int i = 0; i < full_jids.size; i++) { - inner_box.add(get_fulljid_box(full_jids[i])); + if (full_jids == null) return grid; + + for (int i = 0; i < full_jids.size; i++) { + Jid full_jid = full_jids[i]; + Show show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); + Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity(conversation.account, full_jid); + + Image image = new Image() { hexpand=false, valign=Align.START, visible=true }; + grid.attach(image, 0, i + 1, 1, 1); + if (identity != null && identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET) { + image.set_from_icon_name("dino-device-phone-symbolic", IconSize.SMALL_TOOLBAR); + } else { + image.set_from_icon_name("dino-device-desktop-symbolic", IconSize.SMALL_TOOLBAR); } + + if (show.as == Show.AWAY) { + Util.force_color(image, "#FF9800"); + } else if (show.as == Show.XA || show.as == Show.DND) { + Util.force_color(image, "#FF5722"); + } else { + Util.force_color(image, "#4CAF50"); + } + + string? status = null; + if (show.as == Show.AWAY) { + status = "away"; + } else if (show.as == Show.XA) { + status = "not available"; + } else if (show.as == Show.DND) { + status = "do not disturb"; + } + + var sb = new StringBuilder(); + if (identity.name != null) { + sb.append(identity.name); + } else if (dino_resource_regex.match(full_jid.resourcepart)) { + sb.append("Dino"); + } else { + sb.append(full_jid.resourcepart); + } + if (status != null) { + sb.append(" ("); + sb.append(status); + sb.append(")"); + } + + Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true }; + grid.attach(resource, 1, i + 1, 1, 1); } - return main_box; + return grid; } private static string get_relative_time(DateTime datetime) { diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/identity.vala b/xmpp-vala/src/module/xep/0030_service_discovery/identity.vala index e29eb111..43aecfb2 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/identity.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/identity.vala @@ -4,6 +4,10 @@ public class Identity { public const string CATEGORY_CLIENT = "client"; public const string CATEGORY_CONFERENCE = "conference"; + public const string TYPE_PC = "pc"; + public const string TYPE_PHONE = "phone"; + public const string TYPE_TABLET = "tablet"; + public string category { get; set; } public string type_ { get; set; } public string? name { get; set; } @@ -15,4 +19,4 @@ public class Identity { } } -} \ No newline at end of file +} diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala index 18c5a660..22946d4b 100644 --- a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala +++ b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala @@ -3,12 +3,24 @@ using Gee; namespace Xmpp.Xep.EntityCapabilities { private const string NS_URI = "http://jabber.org/protocol/caps"; + private Regex? sha1_base64_regex = null; + + public string? get_caps_hash(Presence.Stanza presence) { + if (sha1_base64_regex == null) { + sha1_base64_regex = /^[A-Za-z0-9+\/]{27}=$/; + } + 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 class Module : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "0115_entity_capabilities"); private string own_ver_hash; private Storage storage; - private Regex sha1_base64_regex = /^[A-Za-z0-9+\/]{27}=$/; public Module(Storage storage) { this.storage = storage; @@ -45,18 +57,17 @@ namespace Xmpp.Xep.EntityCapabilities { } 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); - if (ver_attribute == null || !sha1_base64_regex.match(ver_attribute)) return; - Gee.List capabilities = storage.get_features(ver_attribute); - if (capabilities.size == 0) { - stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, presence.from, (stream, query_result) => { - store_entity_result(stream, ver_attribute, query_result); - }); - } else { - stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(presence.from, capabilities); - } + string? caps_hash = get_caps_hash(presence); + if (caps_hash == null) return; + + Gee.List capabilities = storage.get_features(caps_hash); + ServiceDiscovery.Identity identity = storage.get_identities(caps_hash); + if (identity == null) { + stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, presence.from, (stream, query_result) => { + store_entity_result(stream, caps_hash, query_result); + }); + } else { + stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(presence.from, capabilities); } } @@ -69,6 +80,7 @@ namespace Xmpp.Xep.EntityCapabilities { } if (compute_hash(query_result.identities, query_result.features, data_forms) == entity) { + storage.store_identities(entity, query_result.identities); storage.store_features(entity, query_result.features); stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(query_result.iq.from, query_result.features); } @@ -142,7 +154,9 @@ namespace Xmpp.Xep.EntityCapabilities { } public interface Storage : Object { + public abstract void store_identities(string entity, Gee.List 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); } }