aboutsummaryrefslogtreecommitdiff
path: root/libdino
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2026-08-22 21:02:05 +0200
committerfiaxh <git@lightrise.org>2026-10-23 16:22:06 +0200
commitdeea3cae8e848388ee962a92de58dc42b77915d1 (patch)
tree48cc3b5f2c2b1c71cdaeaac869ff8145d0fa1bd6 /libdino
parent8d49d83b3b45ab22d7b9d945c4b32296b07cb49e (diff)
Store MUC features only in EntityInfo (not in MucFlag), update on change
Diffstat (limited to 'libdino')
-rw-r--r--libdino/meson.build1
-rw-r--r--libdino/src/service/entity_capabilities_storage.vala71
-rw-r--r--libdino/src/service/entity_info.vala31
-rw-r--r--libdino/src/service/message_processor.vala8
-rw-r--r--libdino/src/service/module_manager.vala1
-rw-r--r--libdino/src/service/muc_manager.vala49
6 files changed, 49 insertions, 112 deletions
diff --git a/libdino/meson.build b/libdino/meson.build
index bf29dbf9..12665b18 100644
--- a/libdino/meson.build
+++ b/libdino/meson.build
@@ -55,7 +55,6 @@ sources = files(
'src/service/conversation_manager.vala',
'src/service/counterpart_interaction_manager.vala',
'src/service/database.vala',
- 'src/service/entity_capabilities_storage.vala',
'src/service/entity_info.vala',
'src/service/fallback_body.vala',
'src/service/file_manager.vala',
diff --git a/libdino/src/service/entity_capabilities_storage.vala b/libdino/src/service/entity_capabilities_storage.vala
deleted file mode 100644
index c6d0bbbd..00000000
--- a/libdino/src/service/entity_capabilities_storage.vala
+++ /dev/null
@@ -1,71 +0,0 @@
-using Gee;
-using Qlite;
-using Xmpp;
-using Xmpp.Xep.ServiceDiscovery;
-
-namespace Dino {
-
-public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object {
-
- private Database db;
- private HashMap<string, Gee.List<string>> features_cache = new HashMap<string, Gee.List<string>>();
- private HashMap<string, Identity> identity_cache = new HashMap<string, Identity>();
-
- public EntityCapabilitiesStorage(Database db) {
- this.db = db;
- }
-
- public void store_features(string entity, Gee.List<string> features) {
- if (features_cache.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();
- }
- }
-
- public void store_identities(string entity, Gee.Set<Identity> identities) {
- foreach (Identity identity in identities) {
- if (identity.category == Identity.CATEGORY_CLIENT) {
- db.entity_identity.insert()
- .value(db.entity_identity.entity, entity)
- .value(db.entity_identity.category, identity.category)
- .value(db.entity_identity.type, identity.type_)
- .value(db.entity_identity.entity_name, identity.name)
- .perform();
- return;
- }
- }
- }
-
- public Gee.List<string> get_features(string entity) {
- Gee.List<string>? features = features_cache[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]);
- }
- features_cache[entity] = features;
- return features;
- }
-
- public Identity? get_identities(string entity) {
- Identity? identity = identity_cache[entity];
- if (identity != null) {
- return identity;
- }
-
- RowOption row = db.entity_identity.select().with(db.entity_identity.entity, "=", entity).single().row();
- if (row.is_present()) {
- identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]);
- }
- identity_cache[entity] = identity;
- return identity;
- }
-}
-}
diff --git a/libdino/src/service/entity_info.vala b/libdino/src/service/entity_info.vala
index 53d14b15..03828d1d 100644
--- a/libdino/src/service/entity_info.vala
+++ b/libdino/src/service/entity_info.vala
@@ -12,7 +12,6 @@ public class EntityInfo : StreamInteractionModule, Object {
private StreamInteractor stream_interactor;
private Database db;
- private EntityCapabilitiesStorage entity_capabilities_storage;
private Dino.Entities.Settings settings;
private HashMap<Jid, string> entity_caps_hashes = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
@@ -40,7 +39,6 @@ public class EntityInfo : StreamInteractionModule, Object {
private EntityInfo(StreamInteractor stream_interactor, Database db, Dino.Entities.Settings settings) {
this.stream_interactor = stream_interactor;
this.db = db;
- this.entity_capabilities_storage = new EntityCapabilitiesStorage(db);
this.settings = settings;
stream_interactor.account_added.connect(on_account_added);
@@ -58,18 +56,32 @@ public class EntityInfo : StreamInteractionModule, Object {
Timeout.add_seconds(60 * 60, () => { remove_old_entities(); return true; });
}
- public async Gee.Set<Identity>? get_identities(Account account, Jid jid) {
+ public Gee.Set<Identity>? get_identities_offline(Account account, Jid jid) {
if (jid_identity.has_key(jid)) {
return jid_identity[jid];
}
- string? hash = entity_caps_hashes[jid];
- if (hash != null) {
- Gee.Set<Identity>? identities = get_stored_identities(hash);
- if (identities != null) return identities;
+ var qry = db.entity.select()
+ .with(db.entity.account_id, "=", account.id)
+ .with(db.entity.jid_id, "=", db.get_jid_id(jid))
+ .with(db.entity.resource, "=", jid.resourcepart ?? "")
+ .join_with(db.entity_identity, db.entity.caps_hash, db.entity_identity.entity);
+
+ HashSet<Identity> identities = new HashSet<Identity>(Identity.hash_func, Identity.equals_func);
+
+ foreach (Row row in qry) {
+ var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]);
+ identities.add(identity);
}
- ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, hash);
+ return identities;
+ }
+
+ public async Gee.Set<Identity>? get_identities(Account account, Jid jid) {
+ var identities = get_identities_offline(account, jid);
+ if (identities != null) return identities;
+
+ ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, entity_caps_hashes[jid]);
if (info_result != null) {
return info_result.identities;
}
@@ -261,7 +273,7 @@ public class EntityInfo : StreamInteractionModule, Object {
return identities;
}
- private async ServiceDiscovery.InfoResult? get_info_result(Account account, Jid jid, string? hash = null) {
+ public 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;
@@ -298,7 +310,6 @@ public class EntityInfo : StreamInteractionModule, Object {
}
private void initialize_modules(Account account, ArrayList<XmppStreamModule> modules) {
- modules.add(new Xep.EntityCapabilities.Module(entity_capabilities_storage));
var entity_time_module = modules.first_match((it) => it is EntityTime.Module) as EntityTime.Module;
if (entity_time_module != null) {
settings.bind_property("share-time", entity_time_module, "enabled", BindingFlags.SYNC_CREATE);
diff --git a/libdino/src/service/message_processor.vala b/libdino/src/service/message_processor.vala
index b9c23240..9190cd9d 100644
--- a/libdino/src/service/message_processor.vala
+++ b/libdino/src/service/message_processor.vala
@@ -472,12 +472,8 @@ public class MessageProcessor : StreamInteractionModule, Object {
// Set an origin ID if a MUC doen't guarantee to keep IDs
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
- Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY);
- if (flag == null) {
- message.marked = Entities.Message.Marked.UNSENT;
- return;
- }
- if(!flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.STABLE_ID)) {
+ var supports_stable_ids = stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(conversation.account, conversation.counterpart, Xep.Muc.FEATURE_STABLE_ID);
+ if(!supports_stable_ids) {
UniqueStableStanzaIDs.set_origin_id(new_message, message.stanza_id);
}
}
diff --git a/libdino/src/service/module_manager.vala b/libdino/src/service/module_manager.vala
index 5ba4fe80..f7cad0fe 100644
--- a/libdino/src/service/module_manager.vala
+++ b/libdino/src/service/module_manager.vala
@@ -69,6 +69,7 @@ public class ModuleManager {
module_map[account].add(new Presence.Module());
module_map[account].add(new Xmpp.MessageModule());
module_map[account].add(new Xmpp.MessageArchiveManagement.Module());
+ module_map[account].add(new Xep.EntityCapabilities.Module());
module_map[account].add(new Xep.MessageCarbons.Module());
module_map[account].add(new Xep.BitsOfBinary.Module());
module_map[account].add(new Xep.Muc.Module());
diff --git a/libdino/src/service/muc_manager.vala b/libdino/src/service/muc_manager.vala
index b47a52bf..4959074b 100644
--- a/libdino/src/service/muc_manager.vala
+++ b/libdino/src/service/muc_manager.vala
@@ -108,19 +108,11 @@ public class MucManager : StreamInteractionModule, Object {
joined_conversation.nickname = nick;
stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(joined_conversation);
- bool can_do_affiliation_versioning = yield entity_info.has_feature(account, jid, Xmpp.Xep.MucAffiliationsVersioning.NS_URI);
- if (!can_do_affiliation_versioning) {
- stream.get_module(Xep.Muc.Module.IDENTITY).fetch_affiliations.begin(stream, jid.bare_jid, () => {
- muc_affiliations.clear_affiliations(account, jid);
- persist_muc_affiliations(account, jid.bare_jid);
- });
- }
-
if (can_do_mam) {
var history_sync = stream_interactor.get_module(MessageProcessor.IDENTITY).history_sync;
if (conversation == null) {
// We never joined the conversation before, just fetch the latest MAM page
- yield history_sync.fetch_latest_page(account, jid.bare_jid, null, new DateTime.from_unix_utc(0), cancellable);
+ history_sync.fetch_latest_page.begin(account, jid.bare_jid, null, new DateTime.from_unix_utc(0), cancellable);
} else {
// Fetch everything up to the last time the user actively joined
if (!mucs_sync_cancellables.has_key(account)) {
@@ -137,6 +129,17 @@ public class MucManager : StreamInteractionModule, Object {
}
}
}
+
+ bool can_do_affiliation_versioning = yield entity_info.has_feature(account, jid, Xmpp.Xep.MucAffiliationsVersioning.NS_URI);
+ if (!can_do_affiliation_versioning) {
+ stream.get_module(Xep.Muc.Module.IDENTITY).fetch_affiliations.begin(stream, jid.bare_jid, () => {
+ muc_affiliations.clear_affiliations(account, jid);
+ persist_muc_affiliations(account, jid.bare_jid);
+ });
+ }
+
+ room_info_updated(account, jid); // EntityInfo.has_feature trigged a disco query
+
} else if (res.muc_error != null) {
// Join failed
enter_errors[jid] = res.muc_error;
@@ -254,19 +257,11 @@ public class MucManager : StreamInteractionModule, Object {
//the term `private room` is a short hand for members-only+non-anonymous rooms
public bool is_private_room(Account account, Jid jid) {
var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
- return entity_info.has_feature_offline(account, jid, "muc_membersonly") && entity_info.has_feature_offline(account, jid, "muc_nonanonymous");
+ return entity_info.has_feature_offline(account, jid, Xep.Muc.FEATURE_MEMBERS_ONLY) && entity_info.has_feature_offline(account, jid, Xep.Muc.FEATURE_NON_ANONYMOUS);
}
public bool is_moderated_room(Account account, Jid jid) {
- XmppStream? stream = stream_interactor.get_stream(account);
- if (stream == null) {
- return false;
- }
- Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY);
- if (flag == null) {
- return false;
- }
- return flag.has_room_feature(jid, Xep.Muc.Feature.MODERATED);
+ return stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(account, jid, Xep.Muc.FEATURE_MODERATED);
}
public bool is_public_room(Account account, Jid jid) {
@@ -332,9 +327,13 @@ public class MucManager : StreamInteractionModule, Object {
}
public string? get_room_name(Account account, Jid jid) {
- Xep.Muc.Flag? flag = get_muc_flag(account);
- if (flag != null) {
- return flag.get_room_name(jid);
+ var identities = stream_interactor.get_module(EntityInfo.IDENTITY).get_identities_offline(account, jid);
+ if (identities == null) return null;
+
+ foreach (var identity in identities) {
+ if (identity.category == "conference") {
+ return identity.name;
+ }
}
return null;
}
@@ -471,8 +470,10 @@ public class MucManager : StreamInteractionModule, Object {
private_room_occupants_updated(account, muc_jid);
}
});
- stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_info_updated.connect( (stream, muc_jid) => {
- room_info_updated(account, muc_jid);
+ stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_info_changed.connect( (stream, muc_jid) => {
+ stream_interactor.get_module(EntityInfo.IDENTITY).get_info_result.begin(account, muc_jid, null, () => {
+ room_info_updated(account, muc_jid);
+ });
});
stream_interactor.module_manager.get_module(account, Xep.OccupantIds.Module.IDENTITY).received_own_occupant_id.connect( (stream, jid, occupant_id) => {
if (!own_occupant_ids.has_key(account)) {