aboutsummaryrefslogtreecommitdiff
path: root/libdino/src/service
diff options
context:
space:
mode:
Diffstat (limited to 'libdino/src/service')
-rw-r--r--libdino/src/service/entity_info.vala161
-rw-r--r--libdino/src/service/file_manager.vala20
-rw-r--r--libdino/src/service/jingle_file_transfers.vala30
-rw-r--r--libdino/src/service/message_processor.vala10
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);
}