Only query entity caps when we need them
This commit is contained in:
parent
74f7fa897f
commit
e159fd2492
|
@ -1,5 +1,6 @@
|
||||||
using Gee;
|
using Gee;
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
|
using Qlite;
|
||||||
using Xmpp;
|
using Xmpp;
|
||||||
using Xmpp.Xep;
|
using Xmpp.Xep;
|
||||||
using Xmpp.Xep.ServiceDiscovery;
|
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<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) {
|
public static void start(StreamInteractor stream_interactor, Database db) {
|
||||||
EntityInfo m = new EntityInfo(stream_interactor, db);
|
EntityInfo m = new EntityInfo(stream_interactor, db);
|
||||||
|
@ -27,13 +32,62 @@ public class EntityInfo : StreamInteractionModule, Object {
|
||||||
this.entity_capabilities_storage = new EntityCapabilitiesStorage(db);
|
this.entity_capabilities_storage = new EntityCapabilitiesStorage(db);
|
||||||
|
|
||||||
stream_interactor.account_added.connect(on_account_added);
|
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);
|
stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Identity? get_identity(Account account, Jid jid) {
|
public async Identity? get_identity(Account account, Jid jid) {
|
||||||
string? caps_hash = entity_caps_hashes[jid];
|
Gee.Set<ServiceDiscovery.Identity>? identities = null;
|
||||||
if (caps_hash == null) return null;
|
|
||||||
return entity_capabilities_storage.get_identities(caps_hash);
|
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) {
|
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);
|
string? caps_hash = EntityCapabilities.get_caps_hash(presence);
|
||||||
if (caps_hash == null) return;
|
if (caps_hash == null) return;
|
||||||
|
|
||||||
db.entity.upsert()
|
/*db.entity.upsert()
|
||||||
.value(db.entity.account_id, account.id, true)
|
.value(db.entity.account_id, account.id, true)
|
||||||
.value(db.entity.jid_id, db.get_jid_id(presence.from), true)
|
.value(db.entity.jid_id, db.get_jid_id(presence.from), true)
|
||||||
.value(db.entity.resource, presence.from.resourcepart, true)
|
.value(db.entity.resource, presence.from.resourcepart, true)
|
||||||
.value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix())
|
.value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix())
|
||||||
.value(db.entity.caps_hash, caps_hash)
|
.value(db.entity.caps_hash, caps_hash)
|
||||||
.perform();
|
.perform();*/
|
||||||
|
|
||||||
if (caps_hash != null) {
|
if (caps_hash != null) {
|
||||||
entity_caps_hashes[presence.from] = caps_hash;
|
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) {
|
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));
|
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));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,10 @@ public class FileManager : StreamInteractionModule, Object {
|
||||||
this.add_sender(new JingleFileSender(stream_interactor));
|
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>();
|
HashMap<int, long> ret = new HashMap<int, long>();
|
||||||
foreach (FileSender sender in file_senders) {
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ public class FileManager : StreamInteractionModule, Object {
|
||||||
FileSender file_sender = null;
|
FileSender file_sender = null;
|
||||||
FileEncryptor file_encryptor = null;
|
FileEncryptor file_encryptor = null;
|
||||||
foreach (FileSender sender in file_senders) {
|
foreach (FileSender sender in file_senders) {
|
||||||
if (sender.can_send(conversation, file_transfer)) {
|
if (yield sender.can_send(conversation, file_transfer)) {
|
||||||
if (file_transfer.encryption == Encryption.NONE || sender.can_encrypt(conversation, file_transfer)) {
|
if (file_transfer.encryption == Encryption.NONE || yield sender.can_encrypt(conversation, file_transfer)) {
|
||||||
file_sender = sender;
|
file_sender = sender;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,11 +140,11 @@ public class FileManager : StreamInteractionModule, Object {
|
||||||
yield download_file_internal(file_provider, file_transfer, conversation);
|
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;
|
if (conversation == null) return false;
|
||||||
|
|
||||||
foreach (FileSender file_sender in file_senders) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -401,12 +401,12 @@ public interface FileProvider : Object {
|
||||||
public interface FileSender : Object {
|
public interface FileSender : Object {
|
||||||
public signal void upload_available(Account account);
|
public signal void upload_available(Account account);
|
||||||
|
|
||||||
public abstract bool is_upload_available(Conversation conversation);
|
public abstract async bool is_upload_available(Conversation conversation);
|
||||||
public abstract long get_file_size_limit(Conversation conversation);
|
public abstract async long get_file_size_limit(Conversation conversation);
|
||||||
public abstract bool can_send(Conversation conversation, FileTransfer file_transfer);
|
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 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 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 int get_id();
|
||||||
public abstract float get_priority();
|
public abstract float get_priority();
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Dino {
|
||||||
|
|
||||||
public interface JingleFileEncryptionHelper : Object {
|
public interface JingleFileEncryptionHelper : Object {
|
||||||
public abstract bool can_transfer(Conversation conversation);
|
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 string? get_precondition_name(Conversation conversation, FileTransfer file_transfer);
|
||||||
public abstract Object? get_precondition_options(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);
|
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) {
|
public bool can_transfer(Conversation conversation) {
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) {
|
public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) {
|
||||||
|
@ -143,7 +143,7 @@ public class JingleFileSender : FileSender, Object {
|
||||||
this.stream_interactor = stream_interactor;
|
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;
|
if (conversation.type_ != Conversation.Type.CHAT) return false;
|
||||||
|
|
||||||
JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(conversation.encryption);
|
JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(conversation.encryption);
|
||||||
|
@ -157,35 +157,35 @@ public class JingleFileSender : FileSender, Object {
|
||||||
if (resources == null) return false;
|
if (resources == null) return false;
|
||||||
|
|
||||||
foreach (Jid full_jid in resources) {
|
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 true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long get_file_size_limit(Conversation conversation) {
|
public async long get_file_size_limit(Conversation conversation) {
|
||||||
if (can_send_(conversation)) {
|
if (yield can_send_conv(conversation)) {
|
||||||
return int.MAX;
|
return int.MAX;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool can_send(Conversation conversation, FileTransfer file_transfer) {
|
public async bool can_send(Conversation conversation, FileTransfer file_transfer) {
|
||||||
return can_send_(conversation);
|
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;
|
if (conversation.type_ != Conversation.Type.CHAT) return false;
|
||||||
|
|
||||||
// No file specific restrictions apply to Jingle file transfers
|
// 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);
|
JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption);
|
||||||
if (helper == null) return false;
|
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 {
|
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);
|
XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
|
||||||
if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream available");
|
if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream available");
|
||||||
JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption);
|
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)) {
|
foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
|
||||||
// TODO(hrxi): Prioritization of transports (and resources?).
|
// 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;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
string? precondition_name = null;
|
string? precondition_name = null;
|
||||||
|
|
|
@ -354,18 +354,18 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
||||||
XmppStream? stream = stream_interactor.get_stream(account);
|
XmppStream? stream = stream_interactor.get_stream(account);
|
||||||
Xep.MessageArchiveManagement.MessageFlag? mam_message_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message);
|
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.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) {
|
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;
|
new_message.server_id = mam_message_flag.mam_id;
|
||||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
|
} 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)) ||
|
bool server_supports_sid = (yield entity_info.has_feature(account, 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));
|
(yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.MessageArchiveManagement.NS_URI_2));
|
||||||
if (server_supports_sid) {
|
if (server_supports_sid) {
|
||||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
|
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
|
||||||
}
|
}
|
||||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
|
} 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)) ||
|
bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||||
(yield disco_module.has_entity_feature(stream, account.bare_jid, Xep.MessageArchiveManagement.NS_URI_2));
|
(yield entity_info.has_feature(account, account.bare_jid, Xep.MessageArchiveManagement.NS_URI_2));
|
||||||
if (server_supports_sid) {
|
if (server_supports_sid) {
|
||||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
|
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,50 +271,54 @@ public class ConversationSelectorRow : ListBoxRow {
|
||||||
Jid full_jid = full_jids[i];
|
Jid full_jid = full_jids[i];
|
||||||
string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account);
|
string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account);
|
||||||
if (show == null) continue;
|
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 };
|
int i_cache = i;
|
||||||
if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) {
|
stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => {
|
||||||
image.set_from_icon_name("dino-device-phone-symbolic", IconSize.SMALL_TOOLBAR);
|
Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res);
|
||||||
} else {
|
|
||||||
image.set_from_icon_name("dino-device-desktop-symbolic", IconSize.SMALL_TOOLBAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show == Presence.Stanza.SHOW_AWAY) {
|
Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true };
|
||||||
Util.force_color(image, "#FF9800");
|
if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) {
|
||||||
} else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) {
|
image.set_from_icon_name("dino-device-phone-symbolic", IconSize.SMALL_TOOLBAR);
|
||||||
Util.force_color(image, "#FF5722");
|
} else {
|
||||||
} else {
|
image.set_from_icon_name("dino-device-desktop-symbolic", IconSize.SMALL_TOOLBAR);
|
||||||
Util.force_color(image, "#4CAF50");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
string? status = null;
|
if (show == Presence.Stanza.SHOW_AWAY) {
|
||||||
if (show == Presence.Stanza.SHOW_AWAY) {
|
Util.force_color(image, "#FF9800");
|
||||||
status = "away";
|
} else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) {
|
||||||
} else if (show == Presence.Stanza.SHOW_XA) {
|
Util.force_color(image, "#FF5722");
|
||||||
status = "not available";
|
} else {
|
||||||
} else if (show == Presence.Stanza.SHOW_DND) {
|
Util.force_color(image, "#4CAF50");
|
||||||
status = "do not disturb";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
string? status = null;
|
||||||
if (identity != null && identity.name != null) {
|
if (show == Presence.Stanza.SHOW_AWAY) {
|
||||||
sb.append(identity.name);
|
status = "away";
|
||||||
} else if (full_jid.resourcepart != null && dino_resource_regex.match(full_jid.resourcepart)) {
|
} else if (show == Presence.Stanza.SHOW_XA) {
|
||||||
sb.append("Dino");
|
status = "not available";
|
||||||
} else if (full_jid.resourcepart != null) {
|
} else if (show == Presence.Stanza.SHOW_DND) {
|
||||||
sb.append(full_jid.resourcepart);
|
status = "do not disturb";
|
||||||
} else {
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (status != null) {
|
|
||||||
sb.append(" <i>(").append(status).append(")</i>");
|
|
||||||
}
|
|
||||||
|
|
||||||
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(" <i>(").append(status).append(")</i>");
|
||||||
|
}
|
||||||
|
|
||||||
grid.attach(image, 0, i + 1, 1, 1);
|
Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true };
|
||||||
grid.attach(resource, 1, i + 1, 1, 1);
|
|
||||||
|
grid.attach(image, 0, i_cache + 1, 1, 1);
|
||||||
|
grid.attach(resource, 1, i_cache + 1, 1, 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,9 +93,12 @@ public class ConversationViewController : Object {
|
||||||
|
|
||||||
AccelGroup accel_group = new AccelGroup();
|
AccelGroup accel_group = new AccelGroup();
|
||||||
accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
|
accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => {
|
||||||
if (conversation != null && stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation)) {
|
if (conversation == null) return false;
|
||||||
open_file_picker();
|
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;
|
return false;
|
||||||
});
|
});
|
||||||
((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group);
|
((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group);
|
||||||
|
@ -138,13 +141,15 @@ public class ConversationViewController : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update_file_upload_status() {
|
private void update_file_upload_status() {
|
||||||
bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation);
|
stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => {
|
||||||
chat_input_controller.set_file_upload_active(upload_available);
|
bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res);
|
||||||
if (upload_available && overlay_dialog == null) {
|
chat_input_controller.set_file_upload_active(upload_available);
|
||||||
Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
|
if (upload_available && overlay_dialog == null) {
|
||||||
} else {
|
Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
|
||||||
Gtk.drag_dest_unset(view);
|
} else {
|
||||||
}
|
Gtk.drag_dest_unset(view);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update_conversation_display_name() {
|
private void update_conversation_display_name() {
|
||||||
|
@ -212,18 +217,20 @@ public class ConversationViewController : Object {
|
||||||
FileSendOverlay overlay = new FileSendOverlay(file, file_info);
|
FileSendOverlay overlay = new FileSendOverlay(file, file_info);
|
||||||
overlay.send_file.connect(() => send_file(file));
|
overlay.send_file.connect(() => send_file(file));
|
||||||
|
|
||||||
HashMap<int, long> limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits(conversation);
|
stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.begin(conversation, (_, res) => {
|
||||||
bool something_works = false;
|
HashMap<int, long> limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.end(res);
|
||||||
foreach (var limit in limits.values) {
|
bool something_works = false;
|
||||||
if (limit >= file_info.get_size()) {
|
foreach (var limit in limits.values) {
|
||||||
something_works = true;
|
if (limit >= file_info.get_size()) {
|
||||||
|
something_works = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!something_works && limits.has_key(0)) {
|
||||||
if (!something_works && limits.has_key(0)) {
|
if (!something_works && file_info.get_size() > limits[0]) {
|
||||||
if (!something_works && file_info.get_size() > limits[0]) {
|
overlay.set_file_too_large();
|
||||||
overlay.set_file_too_large();
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
overlay.close.connect(() => {
|
overlay.close.connect(() => {
|
||||||
// We don't want drag'n'drop to be active while the overlay is active
|
// We don't want drag'n'drop to be active while the overlay is active
|
||||||
|
|
|
@ -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;
|
if (!max_file_sizes.has_key(conversation.account)) return false;
|
||||||
|
|
||||||
return file_transfer.size < max_file_sizes[conversation.account];
|
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];
|
long? max_size = max_file_sizes[conversation.account];
|
||||||
if (max_size != null) {
|
if (max_size != null) {
|
||||||
return max_size;
|
return max_size;
|
||||||
|
@ -71,17 +71,17 @@ public class HttpFileSender : FileSender, Object {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool can_encrypt(Conversation conversation, FileTransfer file_transfer) {
|
public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool is_upload_available(Conversation conversation) {
|
public async bool is_upload_available(Conversation conversation) {
|
||||||
lock (max_file_sizes) {
|
lock (max_file_sizes) {
|
||||||
return max_file_sizes.has_key(conversation.account);
|
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) {
|
lock (max_file_sizes) {
|
||||||
return max_file_sizes[account];
|
return max_file_sizes[account];
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,12 @@ public class Module : XmppStreamModule, Jet.EnvelopEncoding {
|
||||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool is_available(XmppStream stream, Jid full_jid) {
|
public async bool is_available(XmppStream stream, Jid full_jid) {
|
||||||
bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI);
|
bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
if (has_feature == null || !(!)has_feature) {
|
if (has_feature == null || !(!)has_feature) {
|
||||||
return false;
|
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() {
|
public string get_type_uri() {
|
||||||
|
|
|
@ -13,7 +13,7 @@ public class EncryptionHelper : JingleFileEncryptionHelper, Object {
|
||||||
return true;
|
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);
|
XmppStream? stream = stream_interactor.get_stream(conversation.account);
|
||||||
if (stream == null) return false;
|
if (stream == null) return false;
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@ public class EncryptionHelper : JingleFileEncryptionHelper, Object {
|
||||||
|
|
||||||
if (full_jid == null) {
|
if (full_jid == null) {
|
||||||
foreach (Jid test_jid in resources) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ namespace Xmpp.Xep.ServiceDiscovery {
|
||||||
public class Flag : XmppStreamFlag {
|
public class Flag : XmppStreamFlag {
|
||||||
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "service_discovery");
|
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "service_discovery");
|
||||||
|
|
||||||
public HashMap<Jid, Gee.List<string>?> entity_features = new HashMap<Jid, Gee.List<string>?>(Jid.hash_func, Jid.equals_func);
|
|
||||||
private HashMap<Jid, Gee.Set<Identity>?> entity_identities = new HashMap<Jid, Gee.Set<Identity>?>(Jid.hash_func, Jid.equals_func);
|
|
||||||
private HashMap<Jid, Gee.List<Item>?> entity_items = new HashMap<Jid, Gee.List<Item>?>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, Gee.List<Item>?> entity_items = new HashMap<Jid, Gee.List<Item>?>(Jid.hash_func, Jid.equals_func);
|
||||||
|
|
||||||
private Gee.Set<string> own_features_ = new HashSet<string>();
|
private Gee.Set<string> own_features_ = new HashSet<string>();
|
||||||
|
@ -23,33 +21,6 @@ public class Flag : XmppStreamFlag {
|
||||||
owned get { return own_identities_.read_only_view; }
|
owned get { return own_identities_.read_only_view; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gee.Set<Identity>? 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<Identity>? identities) {
|
|
||||||
entity_identities[jid] = identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set_entity_features(Jid jid, Gee.List<string>? features) {
|
|
||||||
entity_features[jid] = features;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set_entity_items(Jid jid, Gee.List<Item>? features) {
|
public void set_entity_items(Jid jid, Gee.List<Item>? features) {
|
||||||
entity_items[jid] = features;
|
entity_items[jid] = features;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,10 @@ public class Module : XmppStreamModule, Iq.Handler {
|
||||||
private HashMap<Jid, Future<InfoResult?>> active_info_requests = new HashMap<Jid, Future<InfoResult?>>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, Future<InfoResult?>> active_info_requests = new HashMap<Jid, Future<InfoResult?>>(Jid.hash_func, Jid.equals_func);
|
||||||
|
|
||||||
public Identity own_identity;
|
public Identity own_identity;
|
||||||
|
public CapsCache cache;
|
||||||
|
|
||||||
public Module.with_identity(string category, string type, string? name = null) {
|
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) {
|
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) {
|
public async bool has_entity_feature(XmppStream stream, Jid jid, string feature) {
|
||||||
Flag flag = stream.get_flag(Flag.IDENTITY);
|
return yield this.cache.has_entity_feature(jid, feature);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Gee.Set<Identity>? get_entity_identities(XmppStream stream, Jid jid) {
|
public async Gee.Set<Identity>? get_entity_identities(XmppStream stream, Jid jid) {
|
||||||
Flag flag = stream.get_flag(Flag.IDENTITY);
|
return yield this.cache.get_entity_identities(jid);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async InfoResult? request_info(XmppStream stream, Jid 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<Identity> get_entity_identities(Jid jid);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,16 @@ namespace Xmpp.Xep.EntityCapabilities {
|
||||||
return ver_attribute;
|
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 class Module : XmppStreamModule {
|
||||||
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0115_entity_capabilities");
|
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0115_entity_capabilities");
|
||||||
|
|
||||||
|
@ -40,15 +50,11 @@ namespace Xmpp.Xep.EntityCapabilities {
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
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).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);
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||||
|
|
||||||
check_features_node_ver(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void detach(XmppStream 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).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);
|
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) {
|
public static string compute_hash_for_info_result(ServiceDiscovery.InfoResult info_result) {
|
||||||
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<string> 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;
|
|
||||||
|
|
||||||
Gee.List<DataForms.DataForm> data_forms = new ArrayList<DataForms.DataForm>();
|
Gee.List<DataForms.DataForm> data_forms = new ArrayList<DataForms.DataForm>();
|
||||||
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));
|
data_forms.add(DataForms.DataForm.create_from_node(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compute_hash(query_result.identities, query_result.features, data_forms) == entity) {
|
return compute_hash(info_result.identities, info_result.features, data_forms);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string compute_hash(Gee.Set<ServiceDiscovery.Identity> identities_set, Gee.List<string> features, Gee.List<DataForms.DataForm> data_forms) {
|
public static string compute_hash(Gee.Set<ServiceDiscovery.Identity> identities_set, Gee.List<string> features, Gee.List<DataForms.DataForm> data_forms) {
|
||||||
var identities = new ArrayList<ServiceDiscovery.Identity>();
|
var identities = new ArrayList<ServiceDiscovery.Identity>();
|
||||||
foreach (var identity in identities_set) identities.add(identity);
|
foreach (var identity in identities_set) identities.add(identity);
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ public class Module : XmppStreamModule, Iq.Handler {
|
||||||
}
|
}
|
||||||
return transports[ns_uri];
|
return transports[ns_uri];
|
||||||
}
|
}
|
||||||
public Transport? select_transport(XmppStream stream, TransportType type, Jid receiver_full_jid, Set<string> blacklist) {
|
public async Transport? select_transport(XmppStream stream, TransportType type, Jid receiver_full_jid, Set<string> blacklist) {
|
||||||
Transport? result = null;
|
Transport? result = null;
|
||||||
foreach (Transport transport in transports.values) {
|
foreach (Transport transport in transports.values) {
|
||||||
if (transport.transport_type() != type) {
|
if (transport.transport_type() != type) {
|
||||||
|
@ -158,7 +158,7 @@ public class Module : XmppStreamModule, Iq.Handler {
|
||||||
if (transport.transport_ns_uri() in blacklist) {
|
if (transport.transport_ns_uri() in blacklist) {
|
||||||
continue;
|
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 != null) {
|
||||||
if (result.transport_priority() >= transport.transport_priority()) {
|
if (result.transport_priority() >= transport.transport_priority()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -180,20 +180,20 @@ public class Module : XmppStreamModule, Iq.Handler {
|
||||||
return security_preconditions[ns_uri];
|
return security_preconditions[ns_uri];
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool is_jingle_available(XmppStream stream, Jid full_jid) {
|
private async 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);
|
bool? has_jingle = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
return has_jingle != null && has_jingle;
|
return has_jingle != null && has_jingle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool is_available(XmppStream stream, TransportType type, Jid full_jid) {
|
public async 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;
|
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 {
|
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 (!is_jingle_available(stream, receiver_full_jid)) {
|
if (!yield is_jingle_available(stream, receiver_full_jid)) {
|
||||||
throw new Error.NO_SHARED_PROTOCOLS("No Jingle support");
|
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) {
|
if (transport == null) {
|
||||||
throw new Error.NO_SHARED_PROTOCOLS("No suitable transports");
|
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 interface Transport : Object {
|
||||||
public abstract string transport_ns_uri();
|
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 TransportType transport_type();
|
||||||
public abstract int transport_priority();
|
public abstract int transport_priority();
|
||||||
public abstract TransportParameters create_transport_parameters(XmppStream stream, Jid local_full_jid, Jid peer_full_jid) throws Error;
|
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;
|
transport = null;
|
||||||
} else {
|
} else {
|
||||||
if (role == Role.INITIATOR) {
|
if (role == Role.INITIATOR) {
|
||||||
select_new_transport(stream);
|
select_new_transport.begin(stream);
|
||||||
} else {
|
} else {
|
||||||
state = State.WAITING_FOR_TRANSPORT_REPLACE;
|
state = State.WAITING_FOR_TRANSPORT_REPLACE;
|
||||||
}
|
}
|
||||||
|
@ -640,8 +640,8 @@ public class Session {
|
||||||
}
|
}
|
||||||
content_type.handle_content_session_info(stream, this, info, iq);
|
content_type.handle_content_session_info(stream, this, info, iq);
|
||||||
}
|
}
|
||||||
void select_new_transport(XmppStream stream) {
|
async void select_new_transport(XmppStream stream) {
|
||||||
Transport? new_transport = stream.get_module(Module.IDENTITY).select_transport(stream, type_, peer_full_jid, tried_transport_methods);
|
Transport? new_transport = yield stream.get_module(Module.IDENTITY).select_transport(stream, type_, peer_full_jid, tried_transport_methods);
|
||||||
if (new_transport == null) {
|
if (new_transport == null) {
|
||||||
StanzaNode reason = new StanzaNode.build("reason", NS_URI)
|
StanzaNode reason = new StanzaNode.build("reason", NS_URI)
|
||||||
.put_node(new StanzaNode.build("failed-transport", 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");
|
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));
|
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 {
|
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);
|
Transport? transport = stream.get_module(Module.IDENTITY).get_transport(transport_node.ns_uri);
|
||||||
|
|
|
@ -42,12 +42,12 @@ public class Module : Jingle.ContentType, XmppStreamModule {
|
||||||
|
|
||||||
public signal void file_incoming(XmppStream stream, FileTransfer file_transfer);
|
public signal void file_incoming(XmppStream stream, FileTransfer file_transfer);
|
||||||
|
|
||||||
public bool is_available(XmppStream stream, Jid full_jid) {
|
public async bool is_available(XmppStream stream, Jid full_jid) {
|
||||||
bool? has_feature = stream.get_flag(ServiceDiscovery.Flag.IDENTITY).has_entity_feature(full_jid, NS_URI);
|
bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
if (has_feature == null || !(!)has_feature) {
|
if (has_feature == null || !(!)has_feature) {
|
||||||
return false;
|
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 {
|
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;
|
Jingle.Session session;
|
||||||
try {
|
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"?
|
.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) {
|
} catch (Jingle.Error e) {
|
||||||
throw new IOError.FAILED(@"couldn't create Jingle session: $(e.message)");
|
throw new IOError.FAILED(@"couldn't create Jingle session: $(e.message)");
|
||||||
|
|
|
@ -20,9 +20,8 @@ public class Module : Jingle.Transport, XmppStreamModule {
|
||||||
public override string get_ns() { return NS_URI; }
|
public override string get_ns() { return NS_URI; }
|
||||||
public override string get_id() { return IDENTITY.id; }
|
public override string get_id() { return IDENTITY.id; }
|
||||||
|
|
||||||
public bool is_transport_available(XmppStream stream, Jid full_jid) {
|
public async 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 yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
return result != null && result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string transport_ns_uri() {
|
public string transport_ns_uri() {
|
||||||
|
|
|
@ -21,9 +21,8 @@ public class Module : Jingle.Transport, XmppStreamModule {
|
||||||
public override string get_ns() { return NS_URI; }
|
public override string get_ns() { return NS_URI; }
|
||||||
public override string get_id() { return IDENTITY.id; }
|
public override string get_id() { return IDENTITY.id; }
|
||||||
|
|
||||||
public bool is_transport_available(XmppStream stream, Jid full_jid) {
|
public async 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 yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
return result != null && result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string transport_ns_uri() {
|
public string transport_ns_uri() {
|
||||||
|
|
|
@ -18,9 +18,8 @@ public class Module : XmppStreamModule, SecurityPrecondition {
|
||||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool is_available(XmppStream stream, Jid full_jid) {
|
public async 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 yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI);
|
||||||
return has_feature != null && (!)has_feature;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register_envelop_encoding(EnvelopEncoding encoding) {
|
public void register_envelop_encoding(EnvelopEncoding encoding) {
|
||||||
|
|
Loading…
Reference in a new issue