From e159fd2492c28c1ef4ab64828ca0e8c2de877b41 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Fri, 3 Jul 2020 21:14:39 +0200 Subject: [PATCH] Only query entity caps when we need them --- libdino/src/service/entity_info.vala | 161 +++++++++++++++++- libdino/src/service/file_manager.vala | 20 +-- .../src/service/jingle_file_transfers.vala | 30 ++-- libdino/src/service/message_processor.vala | 10 +- .../conversation_selector_row.vala | 80 ++++----- main/src/ui/conversation_view_controller.vala | 47 ++--- plugins/http-files/src/file_sender.vala | 10 +- plugins/omemo/src/jingle/jet_omemo.vala | 6 +- plugins/omemo/src/jingle/jingle_helper.vala | 6 +- .../xep/0030_service_discovery/flag.vala | 29 ---- .../xep/0030_service_discovery/module.vala | 28 +-- .../module/xep/0115_entitiy_capabilities.vala | 57 ++----- xmpp-vala/src/module/xep/0166_jingle.vala | 28 +-- .../module/xep/0234_jingle_file_transfer.vala | 8 +- .../xep/0260_jingle_socks5_bytestreams.vala | 5 +- .../xep/0261_jingle_in_band_bytestreams.vala | 5 +- .../xep/0391_jingle_encrypted_transports.vala | 5 +- 17 files changed, 312 insertions(+), 223 deletions(-) diff --git a/libdino/src/service/entity_info.vala b/libdino/src/service/entity_info.vala index 8efea7e5..7a78b154 100644 --- a/libdino/src/service/entity_info.vala +++ b/libdino/src/service/entity_info.vala @@ -1,5 +1,6 @@ using Gee; using Dino.Entities; +using Qlite; using Xmpp; using Xmpp.Xep; using Xmpp.Xep.ServiceDiscovery; @@ -15,6 +16,10 @@ public class EntityInfo : StreamInteractionModule, Object { private HashMap entity_caps_hashes = new HashMap(Jid.hash_func, Jid.equals_func); + private HashMap> entity_features = new HashMap>(); + private HashMap> jid_features = new HashMap>(Jid.hash_func, Jid.equals_func); + private HashMap> entity_identity = new HashMap>(); + private HashMap> jid_identity = new HashMap>(Jid.hash_func, Jid.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { EntityInfo m = new EntityInfo(stream_interactor, db); @@ -27,13 +32,62 @@ public class EntityInfo : StreamInteractionModule, Object { this.entity_capabilities_storage = new EntityCapabilitiesStorage(db); stream_interactor.account_added.connect(on_account_added); + stream_interactor.stream_negotiated.connect((account, stream) => { + var cache = new CapsCacheImpl(account, this); + stream.get_module(ServiceDiscovery.Module.IDENTITY).cache = cache; + + string? hash = EntityCapabilities.get_server_caps_hash(stream); + entity_caps_hashes[new Jid(account.domainpart)] = hash; + }); 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); + public async Identity? get_identity(Account account, Jid jid) { + Gee.Set? identities = null; + + if (jid_identity.has_key(jid)) { + identities = jid_identity[jid]; + } + + if (identities == null) { + string? hash = entity_caps_hashes[jid]; + if (hash != null) { + identities = get_identities(hash); + } + + if (identities == null) { + ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, hash); + identities = info_result.identities; + } + } + + if (identities != null) { + foreach (var identity in identities) { + if (identity.category == Identity.CATEGORY_CLIENT) { + return identity; + } + } + } + return null; + } + + public async bool has_feature(Account account, Jid jid, string feature) { + if (jid_features.has_key(jid)) { + return jid_features[jid].contains(feature); + } + + string? hash = entity_caps_hashes[jid]; + if (hash != null) { + Gee.List? features = get_features(hash); + if (features != null) { + return features.contains(feature); + } + } + + ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, hash); + if (info_result == null) return false; + + return info_result.features.contains(feature); } private void on_received_available_presence(Account account, Presence.Stanza presence) { @@ -43,19 +97,93 @@ public class EntityInfo : StreamInteractionModule, Object { string? caps_hash = EntityCapabilities.get_caps_hash(presence); if (caps_hash == null) return; - db.entity.upsert() + /*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(); + .perform();*/ if (caps_hash != null) { entity_caps_hashes[presence.from] = caps_hash; } } + private void store_features(string entity, Gee.List features) { + if (entity_features.has_key(entity)) return; + + foreach (string feature in features) { + db.entity_feature.insert() + .value(db.entity_feature.entity, entity) + .value(db.entity_feature.feature, feature) + .perform(); + } + entity_features[entity] = features; + } + + private void store_identities(string entity, Gee.Set identities) { + foreach (Identity identity in identities) { + 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.entity_name, identity.name) + .perform(); + } + } + + private Gee.List? get_features(string entity) { + Gee.List? features = entity_features[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]); + } + + if (features.size == 0) { + return null; + } + entity_features[entity] = features; + return features; + } + + private Gee.Set get_identities(string entity) { + Gee.Set? identities = entity_identity[entity]; + if (identities != null) { + return identities; + } + + var qry = db.entity_identity.select().with(db.entity_identity.entity, "=", entity); + foreach (Row row in qry) { + if (identities == null) identities = new HashSet(Identity.hash_func, Identity.equals_func); + var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]); + identities.add(identity); + } + return identities; + } + + private async ServiceDiscovery.InfoResult? get_info_result(Account account, Jid jid, string? hash = null) { + XmppStream? stream = stream_interactor.get_stream(account); + if (stream == null) return null; + + ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid); + if (info_result == null) return null; + + if (hash != null && EntityCapabilities.Module.compute_hash_for_info_result(info_result) == hash) { + store_features(hash, info_result.features); + store_identities(hash, info_result.identities); + } else { + jid_features[jid] = info_result.features; + jid_identity[jid] = info_result.identities; + } + + return info_result; + } + 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)); } @@ -64,4 +192,25 @@ public class EntityInfo : StreamInteractionModule, Object { modules.add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); } } + +public class CapsCacheImpl : CapsCache, Object { + + private Account account; + private EntityInfo entity_info; + + public CapsCacheImpl(Account account, EntityInfo entity_info) { + this.account = account; + this.entity_info = entity_info; + } + + public async bool has_entity_feature(Jid jid, string feature) { + return yield entity_info.has_feature(account, jid, feature); + } + + public async Gee.Set get_entity_identities(Jid jid) { + var ret = new HashSet(Identity.hash_func, Identity.equals_func); + ret.add(yield entity_info.get_identity(account, jid)); + return ret; + } +} } diff --git a/libdino/src/service/file_manager.vala b/libdino/src/service/file_manager.vala index abeaabc7..fe9cba73 100644 --- a/libdino/src/service/file_manager.vala +++ b/libdino/src/service/file_manager.vala @@ -38,10 +38,10 @@ public class FileManager : StreamInteractionModule, Object { this.add_sender(new JingleFileSender(stream_interactor)); } - public HashMap get_file_size_limits(Conversation conversation) { + public async HashMap get_file_size_limits(Conversation conversation) { HashMap ret = new HashMap(); foreach (FileSender sender in file_senders) { - ret[sender.get_id()] = sender.get_file_size_limit(conversation); + ret[sender.get_id()] = yield sender.get_file_size_limit(conversation); } return ret; } @@ -86,8 +86,8 @@ public class FileManager : StreamInteractionModule, Object { FileSender file_sender = null; FileEncryptor file_encryptor = null; foreach (FileSender sender in file_senders) { - if (sender.can_send(conversation, file_transfer)) { - if (file_transfer.encryption == Encryption.NONE || sender.can_encrypt(conversation, file_transfer)) { + if (yield sender.can_send(conversation, file_transfer)) { + if (file_transfer.encryption == Encryption.NONE || yield sender.can_encrypt(conversation, file_transfer)) { file_sender = sender; break; } else { @@ -140,11 +140,11 @@ public class FileManager : StreamInteractionModule, Object { yield download_file_internal(file_provider, file_transfer, conversation); } - public bool is_upload_available(Conversation? conversation) { + public async bool is_upload_available(Conversation? conversation) { if (conversation == null) return false; foreach (FileSender file_sender in file_senders) { - if (file_sender.is_upload_available(conversation)) return true; + if (yield file_sender.is_upload_available(conversation)) return true; } return false; } @@ -401,12 +401,12 @@ public interface FileProvider : Object { public interface FileSender : Object { public signal void upload_available(Account account); - public abstract bool is_upload_available(Conversation conversation); - public abstract long get_file_size_limit(Conversation conversation); - public abstract bool can_send(Conversation conversation, FileTransfer file_transfer); + public abstract async bool is_upload_available(Conversation conversation); + public abstract async long get_file_size_limit(Conversation conversation); + public abstract async bool can_send(Conversation conversation, FileTransfer file_transfer); public abstract async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError; public abstract async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError; - public abstract bool can_encrypt(Conversation conversation, FileTransfer file_transfer); + public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer); public abstract int get_id(); public abstract float get_priority(); diff --git a/libdino/src/service/jingle_file_transfers.vala b/libdino/src/service/jingle_file_transfers.vala index 4c3646b3..6482e2e7 100644 --- a/libdino/src/service/jingle_file_transfers.vala +++ b/libdino/src/service/jingle_file_transfers.vala @@ -8,7 +8,7 @@ namespace Dino { public interface JingleFileEncryptionHelper : Object { public abstract bool can_transfer(Conversation conversation); - public abstract bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid = null); + public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid = null); public abstract string? get_precondition_name(Conversation conversation, FileTransfer file_transfer); public abstract Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer); public abstract FileMeta complete_meta(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta, Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer); @@ -18,7 +18,7 @@ public class JingleFileEncryptionHelperTransferOnly : JingleFileEncryptionHelper public bool can_transfer(Conversation conversation) { return true; } - public bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { + public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { return false; } public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) { @@ -143,7 +143,7 @@ public class JingleFileSender : FileSender, Object { this.stream_interactor = stream_interactor; } - public bool is_upload_available(Conversation conversation) { + public async bool is_upload_available(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(conversation.encryption); @@ -157,35 +157,35 @@ public class JingleFileSender : FileSender, Object { if (resources == null) return false; foreach (Jid full_jid in resources) { - if (stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { + if (yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { return true; } } return false; } - public long get_file_size_limit(Conversation conversation) { - if (can_send_(conversation)) { + public async long get_file_size_limit(Conversation conversation) { + if (yield can_send_conv(conversation)) { return int.MAX; } return -1; } - public bool can_send(Conversation conversation, FileTransfer file_transfer) { - return can_send_(conversation); + public async bool can_send(Conversation conversation, FileTransfer file_transfer) { + return yield can_send_conv(conversation); } - private bool can_send_(Conversation conversation) { + private async bool can_send_conv(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; // No file specific restrictions apply to Jingle file transfers - return is_upload_available(conversation); + return yield is_upload_available(conversation); } - public bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { + public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); if (helper == null) return false; - return helper.can_encrypt(conversation, file_transfer); + return yield helper.can_encrypt(conversation, file_transfer); } public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError { @@ -199,13 +199,13 @@ public class JingleFileSender : FileSender, Object { XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream available"); JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); - bool must_encrypt = helper != null && helper.can_encrypt(conversation, file_transfer); + bool must_encrypt = helper != null && yield helper.can_encrypt(conversation, file_transfer); foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) { // TODO(hrxi): Prioritization of transports (and resources?). - if (!stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { + if (!yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { continue; } - if (must_encrypt && !helper.can_encrypt(conversation, file_transfer, full_jid)) { + if (must_encrypt && !yield helper.can_encrypt(conversation, file_transfer, full_jid)) { continue; } string? precondition_name = null; diff --git a/libdino/src/service/message_processor.vala b/libdino/src/service/message_processor.vala index 6c415deb..c0fde767 100644 --- a/libdino/src/service/message_processor.vala +++ b/libdino/src/service/message_processor.vala @@ -354,18 +354,18 @@ public class MessageProcessor : StreamInteractionModule, Object { XmppStream? stream = stream_interactor.get_stream(account); Xep.MessageArchiveManagement.MessageFlag? mam_message_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message); Xep.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xep.MessageArchiveManagement.Flag.IDENTITY) : null; - Xep.ServiceDiscovery.Module disco_module = stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY); + EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); if (mam_message_flag != null && mam_flag != null && mam_flag.ns_ver == Xep.MessageArchiveManagement.NS_URI_2 && mam_message_flag.mam_id != null) { new_message.server_id = mam_message_flag.mam_id; } else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { - bool server_supports_sid = (yield disco_module.has_entity_feature(stream, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || - (yield disco_module.has_entity_feature(stream, new_message.counterpart.bare_jid, Xep.MessageArchiveManagement.NS_URI_2)); + bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || + (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.MessageArchiveManagement.NS_URI_2)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid); } } else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) { - bool server_supports_sid = (yield disco_module.has_entity_feature(stream, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || - (yield disco_module.has_entity_feature(stream, account.bare_jid, Xep.MessageArchiveManagement.NS_URI_2)); + bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || + (yield entity_info.has_feature(account, account.bare_jid, Xep.MessageArchiveManagement.NS_URI_2)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid); } diff --git a/main/src/ui/conversation_selector/conversation_selector_row.vala b/main/src/ui/conversation_selector/conversation_selector_row.vala index 3e0c1349..07e3f831 100644 --- a/main/src/ui/conversation_selector/conversation_selector_row.vala +++ b/main/src/ui/conversation_selector/conversation_selector_row.vala @@ -271,50 +271,54 @@ public class ConversationSelectorRow : ListBoxRow { Jid full_jid = full_jids[i]; string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); if (show == null) continue; - Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity(conversation.account, full_jid); - Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true }; - 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); - } + int i_cache = i; + stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => { + Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res); - if (show == Presence.Stanza.SHOW_AWAY) { - Util.force_color(image, "#FF9800"); - } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) { - Util.force_color(image, "#FF5722"); - } else { - Util.force_color(image, "#4CAF50"); - } + Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true }; + 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); + } - string? status = null; - if (show == Presence.Stanza.SHOW_AWAY) { - status = "away"; - } else if (show == Presence.Stanza.SHOW_XA) { - status = "not available"; - } else if (show == Presence.Stanza.SHOW_DND) { - status = "do not disturb"; - } + if (show == Presence.Stanza.SHOW_AWAY) { + Util.force_color(image, "#FF9800"); + } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) { + Util.force_color(image, "#FF5722"); + } else { + Util.force_color(image, "#4CAF50"); + } - var sb = new StringBuilder(); - if (identity != null && identity.name != null) { - sb.append(identity.name); - } else if (full_jid.resourcepart != null && dino_resource_regex.match(full_jid.resourcepart)) { - sb.append("Dino"); - } else if (full_jid.resourcepart != null) { - sb.append(full_jid.resourcepart); - } else { - continue; - } - if (status != null) { - sb.append(" (").append(status).append(")"); - } + string? status = null; + if (show == Presence.Stanza.SHOW_AWAY) { + status = "away"; + } else if (show == Presence.Stanza.SHOW_XA) { + status = "not available"; + } else if (show == Presence.Stanza.SHOW_DND) { + status = "do not disturb"; + } - Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true }; + var sb = new StringBuilder(); + if (identity != null && identity.name != null) { + sb.append(identity.name); + } else if (full_jid.resourcepart != null && dino_resource_regex.match(full_jid.resourcepart)) { + sb.append("Dino"); + } else if (full_jid.resourcepart != null) { + sb.append(full_jid.resourcepart); + } else { + return; + } + if (status != null) { + sb.append(" (").append(status).append(")"); + } - grid.attach(image, 0, i + 1, 1, 1); - grid.attach(resource, 1, i + 1, 1, 1); + Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true }; + + grid.attach(image, 0, i_cache + 1, 1, 1); + grid.attach(resource, 1, i_cache + 1, 1, 1); + }); } return grid; } diff --git a/main/src/ui/conversation_view_controller.vala b/main/src/ui/conversation_view_controller.vala index 831aae73..b2be8ed8 100644 --- a/main/src/ui/conversation_view_controller.vala +++ b/main/src/ui/conversation_view_controller.vala @@ -93,9 +93,12 @@ public class ConversationViewController : Object { AccelGroup accel_group = new AccelGroup(); accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { - if (conversation != null && stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation)) { - open_file_picker(); - } + if (conversation == null) return false; + stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { + if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { + open_file_picker(); + } + }); return false; }); ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group); @@ -138,13 +141,15 @@ public class ConversationViewController : Object { } private void update_file_upload_status() { - bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation); - chat_input_controller.set_file_upload_active(upload_available); - if (upload_available && overlay_dialog == null) { - Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY); - } else { - Gtk.drag_dest_unset(view); - } + stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { + bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res); + chat_input_controller.set_file_upload_active(upload_available); + if (upload_available && overlay_dialog == null) { + Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY); + } else { + Gtk.drag_dest_unset(view); + } + }); } private void update_conversation_display_name() { @@ -212,18 +217,20 @@ public class ConversationViewController : Object { FileSendOverlay overlay = new FileSendOverlay(file, file_info); overlay.send_file.connect(() => send_file(file)); - HashMap limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits(conversation); - bool something_works = false; - foreach (var limit in limits.values) { - if (limit >= file_info.get_size()) { - something_works = true; + stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.begin(conversation, (_, res) => { + HashMap limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.end(res); + bool something_works = false; + foreach (var limit in limits.values) { + if (limit >= file_info.get_size()) { + something_works = true; + } } - } - if (!something_works && limits.has_key(0)) { - if (!something_works && file_info.get_size() > limits[0]) { - overlay.set_file_too_large(); + if (!something_works && limits.has_key(0)) { + if (!something_works && file_info.get_size() > limits[0]) { + overlay.set_file_too_large(); + } } - } + }); overlay.close.connect(() => { // We don't want drag'n'drop to be active while the overlay is active diff --git a/plugins/http-files/src/file_sender.vala b/plugins/http-files/src/file_sender.vala index cb8839f3..25db49b9 100644 --- a/plugins/http-files/src/file_sender.vala +++ b/plugins/http-files/src/file_sender.vala @@ -57,13 +57,13 @@ public class HttpFileSender : FileSender, Object { } } - public bool can_send(Conversation conversation, FileTransfer file_transfer) { + public async bool can_send(Conversation conversation, FileTransfer file_transfer) { if (!max_file_sizes.has_key(conversation.account)) return false; return file_transfer.size < max_file_sizes[conversation.account]; } - public long get_file_size_limit(Conversation conversation) { + public async long get_file_size_limit(Conversation conversation) { long? max_size = max_file_sizes[conversation.account]; if (max_size != null) { return max_size; @@ -71,17 +71,17 @@ public class HttpFileSender : FileSender, Object { return -1; } - public bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { + public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { return false; } - public bool is_upload_available(Conversation conversation) { + public async bool is_upload_available(Conversation conversation) { lock (max_file_sizes) { return max_file_sizes.has_key(conversation.account); } } - public long get_max_file_size(Account account) { + public async long get_max_file_size(Account account) { lock (max_file_sizes) { return max_file_sizes[account]; } diff --git a/plugins/omemo/src/jingle/jet_omemo.vala b/plugins/omemo/src/jingle/jet_omemo.vala index edca809c..b3343855 100644 --- a/plugins/omemo/src/jingle/jet_omemo.vala +++ b/plugins/omemo/src/jingle/jet_omemo.vala @@ -29,12 +29,12 @@ public class Module : XmppStreamModule, Jet.EnvelopEncoding { stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); } - public bool is_available(XmppStream stream, Jid full_jid) { - bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); + public async bool is_available(XmppStream stream, Jid full_jid) { + bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); if (has_feature == null || !(!)has_feature) { return false; } - return stream.get_module(Xep.Jet.Module.IDENTITY).is_available(stream, full_jid); + return yield stream.get_module(Xep.Jet.Module.IDENTITY).is_available(stream, full_jid); } public string get_type_uri() { diff --git a/plugins/omemo/src/jingle/jingle_helper.vala b/plugins/omemo/src/jingle/jingle_helper.vala index 6814fd00..4b6ed646 100644 --- a/plugins/omemo/src/jingle/jingle_helper.vala +++ b/plugins/omemo/src/jingle/jingle_helper.vala @@ -13,7 +13,7 @@ public class EncryptionHelper : JingleFileEncryptionHelper, Object { return true; } - public bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { + public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; @@ -22,12 +22,12 @@ public class EncryptionHelper : JingleFileEncryptionHelper, Object { if (full_jid == null) { foreach (Jid test_jid in resources) { - if (stream.get_module(Module.IDENTITY).is_available(stream, test_jid)) { + if (yield stream.get_module(Module.IDENTITY).is_available(stream, test_jid)) { return true; } } } else { - if (stream.get_module(Module.IDENTITY).is_available(stream, full_jid)) { + if (yield stream.get_module(Module.IDENTITY).is_available(stream, full_jid)) { return true; } } 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 7716ceff..ab311727 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"); - public HashMap?> entity_features = new HashMap?>(Jid.hash_func, Jid.equals_func); - private HashMap?> entity_identities = new HashMap?>(Jid.hash_func, Jid.equals_func); private HashMap?> entity_items = new HashMap?>(Jid.hash_func, Jid.equals_func); private Gee.Set own_features_ = new HashSet(); @@ -23,33 +21,6 @@ public class Flag : XmppStreamFlag { owned get { return own_identities_.read_only_view; } } - public Gee.Set? get_entity_identities(Jid jid) { - return entity_identities.has_key(jid) ? entity_identities[jid].read_only_view : null; // TODO isn’t this default for hashmap - } - - public bool? has_entity_identity(Jid jid, string category, string type) { - if (!entity_identities.has_key(jid)) return null; - if (entity_identities[jid] == null) return false; - foreach (Identity identity in entity_identities[jid]) { - if (identity.category == category && identity.type_ == type) return true; - } - return false; - } - - public bool? has_entity_feature(Jid jid, string feature) { - if (!entity_features.has_key(jid)) return null; - if (entity_features[jid] == null) return false; - return entity_features[jid].contains(feature); - } - - public void set_entity_identities(Jid jid, Gee.Set? identities) { - entity_identities[jid] = identities; - } - - public void set_entity_features(Jid jid, Gee.List? features) { - entity_features[jid] = features; - } - public void set_entity_items(Jid jid, Gee.List? features) { entity_items[jid] = features; } 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 f21146f1..c61d3ab0 100644 --- a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala +++ b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala @@ -12,9 +12,10 @@ public class Module : XmppStreamModule, Iq.Handler { private HashMap> active_info_requests = new HashMap>(Jid.hash_func, Jid.equals_func); public Identity own_identity; + public CapsCache cache; public Module.with_identity(string category, string type, string? name = null) { - own_identity = new Identity(category, type, name); + this.own_identity = new Identity(category, type, name); } public void add_feature(XmppStream stream, string feature) { @@ -42,27 +43,11 @@ public class Module : XmppStreamModule, Iq.Handler { } public async bool has_entity_feature(XmppStream stream, Jid jid, string feature) { - Flag flag = stream.get_flag(Flag.IDENTITY); - - if (flag.has_entity_feature(jid, feature) == null) { - InfoResult? info_result = yield request_info(stream, jid); - stream.get_flag(Flag.IDENTITY).set_entity_features(jid, info_result != null ? info_result.features : null); - stream.get_flag(Flag.IDENTITY).set_entity_identities(jid, info_result != null ? info_result.identities : null); - } - - return flag.has_entity_feature(jid, feature) ?? false; + return yield this.cache.has_entity_feature(jid, feature); } public async Gee.Set? get_entity_identities(XmppStream stream, Jid jid) { - Flag flag = stream.get_flag(Flag.IDENTITY); - - if (flag.get_entity_identities(jid) == null) { - InfoResult? info_result = yield request_info(stream, jid); - stream.get_flag(Flag.IDENTITY).set_entity_features(info_result.iq.from, info_result != null ? info_result.features : null); - stream.get_flag(Flag.IDENTITY).set_entity_identities(info_result.iq.from, info_result != null ? info_result.identities : null); - } - - return flag.get_entity_identities(jid); + return yield this.cache.get_entity_identities(jid); } public async InfoResult? request_info(XmppStream stream, Jid jid) { @@ -137,4 +122,9 @@ public class Module : XmppStreamModule, Iq.Handler { } } +public interface CapsCache : Object { + public abstract async bool has_entity_feature(Jid jid, string feature); + public abstract async Gee.Set get_entity_identities(Jid jid); +} + } diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala index be7db509..8e15e0c2 100644 --- a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala +++ b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala @@ -21,6 +21,16 @@ namespace Xmpp.Xep.EntityCapabilities { 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"); @@ -40,15 +50,11 @@ namespace Xmpp.Xep.EntityCapabilities { 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(Presence.Module.IDENTITY).received_presence.connect(on_received_presence); stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); - - check_features_node_ver(stream); } 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(Presence.Module.IDENTITY).received_presence.disconnect(on_received_presence); stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); } @@ -64,51 +70,16 @@ namespace Xmpp.Xep.EntityCapabilities { } } - private void on_received_presence(XmppStream stream, Presence.Stanza presence) { - string? caps_hash = get_caps_hash(presence); - if (caps_hash == null) return; - - process_hash.begin(stream, presence.from, caps_hash); - } - - private void check_features_node_ver(XmppStream stream) { - StanzaNode? node = stream.features.get_subnode("c", NS_URI); - if (node == null) return; - - string? ver_attribute = node.get_attribute("ver", NS_URI); - if (ver_attribute == null) return; - - process_hash.begin(stream, stream.remote_name, ver_attribute); - } - - private async void process_hash(XmppStream stream, Jid jid_from, string caps_hash) { - Gee.List capabilities = storage.get_features(caps_hash); - ServiceDiscovery.Identity identity = storage.get_identities(caps_hash); - if (identity == null) { - ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid_from); - if (info_result == null) return; - store_entity_result(stream, caps_hash, info_result); - } else { - stream.get_flag(ServiceDiscovery.Flag.IDENTITY).set_entity_features(jid_from, capabilities); - } - } - - private void store_entity_result(XmppStream stream, string entity, ServiceDiscovery.InfoResult? query_result) { - if (query_result == null) return; - + public static string compute_hash_for_info_result(ServiceDiscovery.InfoResult info_result) { Gee.List data_forms = new ArrayList(); - foreach (StanzaNode node in query_result.iq.stanza.get_deep_subnodes(ServiceDiscovery.NS_URI_INFO + ":query", DataForms.NS_URI + ":x")) { + 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)); } - 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); - } + return compute_hash(info_result.identities, info_result.features, data_forms); } - private static string compute_hash(Gee.Set identities_set, Gee.List features, Gee.List 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); diff --git a/xmpp-vala/src/module/xep/0166_jingle.vala b/xmpp-vala/src/module/xep/0166_jingle.vala index 15c310bd..3a634222 100644 --- a/xmpp-vala/src/module/xep/0166_jingle.vala +++ b/xmpp-vala/src/module/xep/0166_jingle.vala @@ -149,7 +149,7 @@ public class Module : XmppStreamModule, Iq.Handler { } return transports[ns_uri]; } - public Transport? select_transport(XmppStream stream, TransportType type, Jid receiver_full_jid, Set blacklist) { + public async Transport? select_transport(XmppStream stream, TransportType type, Jid receiver_full_jid, Set blacklist) { Transport? result = null; foreach (Transport transport in transports.values) { if (transport.transport_type() != type) { @@ -158,7 +158,7 @@ public class Module : XmppStreamModule, Iq.Handler { if (transport.transport_ns_uri() in blacklist) { continue; } - if (transport.is_transport_available(stream, receiver_full_jid)) { + if (yield transport.is_transport_available(stream, receiver_full_jid)) { if (result != null) { if (result.transport_priority() >= transport.transport_priority()) { continue; @@ -180,20 +180,20 @@ public class Module : XmppStreamModule, Iq.Handler { return security_preconditions[ns_uri]; } - private bool is_jingle_available(XmppStream stream, Jid full_jid) { - bool? has_jingle = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); + private async bool is_jingle_available(XmppStream stream, Jid full_jid) { + bool? has_jingle = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); return has_jingle != null && has_jingle; } - public bool is_available(XmppStream stream, TransportType type, Jid full_jid) { - return is_jingle_available(stream, full_jid) && select_transport(stream, type, full_jid, Set.empty()) != null; + public async bool is_available(XmppStream stream, TransportType type, Jid full_jid) { + return (yield is_jingle_available(stream, full_jid)) && (yield select_transport(stream, type, full_jid, Set.empty())) != null; } - public Session create_session(XmppStream stream, TransportType type, Jid receiver_full_jid, Senders senders, string content_name, StanzaNode description, string? precondition_name = null, Object? precondation_options = null) throws Error { - if (!is_jingle_available(stream, receiver_full_jid)) { + public async Session create_session(XmppStream stream, TransportType type, Jid receiver_full_jid, Senders senders, string content_name, StanzaNode description, string? precondition_name = null, Object? precondation_options = null) throws Error { + if (!yield is_jingle_available(stream, receiver_full_jid)) { throw new Error.NO_SHARED_PROTOCOLS("No Jingle support"); } - Transport? transport = select_transport(stream, type, receiver_full_jid, Set.empty()); + Transport? transport = yield select_transport(stream, type, receiver_full_jid, Set.empty()); if (transport == null) { throw new Error.NO_SHARED_PROTOCOLS("No suitable transports"); } @@ -358,7 +358,7 @@ public delegate void SessionTerminate(Jid to, string sid, StanzaNode reason); public interface Transport : Object { public abstract string transport_ns_uri(); - public abstract bool is_transport_available(XmppStream stream, Jid full_jid); + public async abstract bool is_transport_available(XmppStream stream, Jid full_jid); public abstract TransportType transport_type(); public abstract int transport_priority(); public abstract TransportParameters create_transport_parameters(XmppStream stream, Jid local_full_jid, Jid peer_full_jid) throws Error; @@ -613,7 +613,7 @@ public class Session { transport = null; } else { if (role == Role.INITIATOR) { - select_new_transport(stream); + select_new_transport.begin(stream); } else { state = State.WAITING_FOR_TRANSPORT_REPLACE; } @@ -640,8 +640,8 @@ public class Session { } content_type.handle_content_session_info(stream, this, info, iq); } - void select_new_transport(XmppStream stream) { - Transport? new_transport = stream.get_module(Module.IDENTITY).select_transport(stream, type_, peer_full_jid, tried_transport_methods); + async void select_new_transport(XmppStream stream) { + Transport? new_transport = yield stream.get_module(Module.IDENTITY).select_transport(stream, type_, peer_full_jid, tried_transport_methods); if (new_transport == null) { StanzaNode reason = new StanzaNode.build("reason", NS_URI) .put_node(new StanzaNode.build("failed-transport", NS_URI)); @@ -680,7 +680,7 @@ public class Session { throw new IqError.OUT_OF_ORDER("no outstanding transport-replace request"); } stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq)); - select_new_transport(stream); + select_new_transport.begin(stream); } void handle_transport_replace(XmppStream stream, StanzaNode transport_node, StanzaNode jingle, Iq.Stanza iq) throws IqError { Transport? transport = stream.get_module(Module.IDENTITY).get_transport(transport_node.ns_uri); diff --git a/xmpp-vala/src/module/xep/0234_jingle_file_transfer.vala b/xmpp-vala/src/module/xep/0234_jingle_file_transfer.vala index d3a1f4bf..1c0323be 100644 --- a/xmpp-vala/src/module/xep/0234_jingle_file_transfer.vala +++ b/xmpp-vala/src/module/xep/0234_jingle_file_transfer.vala @@ -42,12 +42,12 @@ public class Module : Jingle.ContentType, XmppStreamModule { public signal void file_incoming(XmppStream stream, FileTransfer file_transfer); - public bool is_available(XmppStream stream, Jid full_jid) { - bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); + public async bool is_available(XmppStream stream, Jid full_jid) { + bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); if (has_feature == null || !(!)has_feature) { return false; } - return stream.get_module(Jingle.Module.IDENTITY).is_available(stream, Jingle.TransportType.STREAMING, full_jid); + return yield stream.get_module(Jingle.Module.IDENTITY).is_available(stream, Jingle.TransportType.STREAMING, full_jid); } public async void offer_file_stream(XmppStream stream, Jid receiver_full_jid, InputStream input_stream, string basename, int64 size, string? precondition_name = null, Object? precondition_options = null) throws IOError { @@ -66,7 +66,7 @@ public class Module : Jingle.ContentType, XmppStreamModule { Jingle.Session session; try { - session = stream.get_module(Jingle.Module.IDENTITY) + session = yield stream.get_module(Jingle.Module.IDENTITY) .create_session(stream, Jingle.TransportType.STREAMING, receiver_full_jid, Jingle.Senders.INITIATOR, "a-file-offer", description, precondition_name, precondition_options); // TODO(hrxi): Why "a-file-offer"? } catch (Jingle.Error e) { throw new IOError.FAILED(@"couldn't create Jingle session: $(e.message)"); diff --git a/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala b/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala index 2ae2881e..db2cd6a0 100644 --- a/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala +++ b/xmpp-vala/src/module/xep/0260_jingle_socks5_bytestreams.vala @@ -20,9 +20,8 @@ public class Module : Jingle.Transport, XmppStreamModule { public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } - public bool is_transport_available(XmppStream stream, Jid full_jid) { - bool? result = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); - return result != null && result; + public async bool is_transport_available(XmppStream stream, Jid full_jid) { + return yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); } public string transport_ns_uri() { diff --git a/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala b/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala index fc6bc057..e26d63b7 100644 --- a/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala +++ b/xmpp-vala/src/module/xep/0261_jingle_in_band_bytestreams.vala @@ -21,9 +21,8 @@ public class Module : Jingle.Transport, XmppStreamModule { public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } - public bool is_transport_available(XmppStream stream, Jid full_jid) { - bool? result = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); - return result != null && result; + public async bool is_transport_available(XmppStream stream, Jid full_jid) { + return yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); } public string transport_ns_uri() { diff --git a/xmpp-vala/src/module/xep/0391_jingle_encrypted_transports.vala b/xmpp-vala/src/module/xep/0391_jingle_encrypted_transports.vala index 1161ec96..ff25d9b3 100644 --- a/xmpp-vala/src/module/xep/0391_jingle_encrypted_transports.vala +++ b/xmpp-vala/src/module/xep/0391_jingle_encrypted_transports.vala @@ -18,9 +18,8 @@ public class Module : XmppStreamModule, SecurityPrecondition { stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); } - public bool is_available(XmppStream stream, Jid full_jid) { - bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI); - return has_feature != null && (!)has_feature; + public async bool is_available(XmppStream stream, Jid full_jid) { + return yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); } public void register_envelop_encoding(EnvelopEncoding encoding) {