diff options
Diffstat (limited to 'libdino')
-rw-r--r-- | libdino/src/service/entity_info.vala | 161 | ||||
-rw-r--r-- | libdino/src/service/file_manager.vala | 20 | ||||
-rw-r--r-- | libdino/src/service/jingle_file_transfers.vala | 30 | ||||
-rw-r--r-- | libdino/src/service/message_processor.vala | 10 |
4 files changed, 185 insertions, 36 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<Jid, string> entity_caps_hashes = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func); + private HashMap<string, Gee.List<string>> entity_features = new HashMap<string, Gee.List<string>>(); + private HashMap<Jid, Gee.List<string>> jid_features = new HashMap<Jid, Gee.List<string>>(Jid.hash_func, Jid.equals_func); + private HashMap<string, Gee.Set<Identity>> entity_identity = new HashMap<string, Gee.Set<Identity>>(); + private HashMap<Jid, Gee.Set<Identity>> jid_identity = new HashMap<Jid, Gee.Set<Identity>>(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<ServiceDiscovery.Identity>? 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<string>? 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<string> 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<Identity> 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<string>? get_features(string entity) { + Gee.List<string>? features = entity_features[entity]; + if (features != null) { + return features; + } + + features = new ArrayList<string>(); + 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<Identity> get_identities(string entity) { + Gee.Set<Identity>? 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>(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<Identity> get_entity_identities(Jid jid) { + var ret = new HashSet<Identity>(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<int, long> get_file_size_limits(Conversation conversation) { + public async HashMap<int, long> get_file_size_limits(Conversation conversation) { HashMap<int, long> ret = new HashMap<int, long>(); 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); } |