aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep/0045_muc/module.vala
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2026-05-22 17:09:04 +0200
committerfiaxh <git@lightrise.org>2026-05-22 17:11:37 +0200
commitddbb00ee56ca8ddc5db3ed9b6f4003bb6020ed6b (patch)
tree9e59681e4c89f747ce4d83ab3cb5b0aedc4f523b /xmpp-vala/src/module/xep/0045_muc/module.vala
parent9abf8f6869bc35c76bdfa86416012d4e7f0dda78 (diff)
Add support for MUC affiliations versioning (XEP-0463), add affiliation storage
Diffstat (limited to 'xmpp-vala/src/module/xep/0045_muc/module.vala')
-rw-r--r--xmpp-vala/src/module/xep/0045_muc/module.vala62
1 files changed, 45 insertions, 17 deletions
diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala
index 88ff819f..bdaabb3e 100644
--- a/xmpp-vala/src/module/xep/0045_muc/module.vala
+++ b/xmpp-vala/src/module/xep/0045_muc/module.vala
@@ -2,10 +2,10 @@ using Gee;
namespace Xmpp.Xep.Muc {
-private const string NS_URI = "http://jabber.org/protocol/muc";
+public const string NS_URI = "http://jabber.org/protocol/muc";
private const string NS_URI_ADMIN = NS_URI + "#admin";
private const string NS_URI_OWNER = NS_URI + "#owner";
-private const string NS_URI_USER = NS_URI + "#user";
+public const string NS_URI_USER = NS_URI + "#user";
private const string NS_URI_REQUEST = NS_URI + "#request";
public enum MucEnterError {
@@ -69,8 +69,7 @@ public class JoinResult {
public class Module : XmppStreamModule {
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0045_muc_module");
- public signal void received_occupant_affiliation(XmppStream stream, Jid jid, Affiliation? affiliation);
- public signal void received_occupant_jid(XmppStream stream, Jid jid, Jid? real_jid);
+ public signal void received_affiliations(XmppStream stream, Jid muc_jid, HashMap<Jid, Affiliation> affiliations, string? mav_since, string? mav_until);
public signal void received_occupant_role(XmppStream stream, Jid jid, Role? role);
public signal void subject_set(XmppStream stream, string? subject, Jid jid);
public signal void invite_received(XmppStream stream, Jid room_jid, Jid from_jid, string? password, string? reason);
@@ -86,7 +85,7 @@ public class Module : XmppStreamModule {
received_pipeline_listener = new ReceivedPipelineListener(this);
}
- public async JoinResult? enter(XmppStream stream, Jid bare_jid, string nick, string? password, DateTime? history_since, bool receive_history, StanzaNode? additional_node) {
+ public async JoinResult? enter(XmppStream stream, Jid bare_jid, string nick, string? password, DateTime? history_since, bool receive_history, StanzaNode? additional_node, string? mav_since) {
try {
Presence.Stanza presence = new Presence.Stanza();
presence.to = bare_jid.with_resource(nick);
@@ -105,6 +104,8 @@ public class Module : XmppStreamModule {
history_node.set_attribute("maxchars", "0");
}
}
+ x_node.put_node(Xep.MucAffiliationsVersioning.get_join_presence_node(mav_since));
+
presence.stanza.put_node(x_node);
if (additional_node != null) {
@@ -360,11 +361,6 @@ public class Module : XmppStreamModule {
query_room_info.begin(stream, bare_jid);
- // TODO only query that if we actually have the rights to
- query_affiliation.begin(stream, bare_jid, "member");
- query_affiliation.begin(stream, bare_jid, "admin");
- query_affiliation.begin(stream, bare_jid, "owner");
-
flag.finish_muc_enter(bare_jid);
var join_result = new JoinResult() { nick=presence.from.resourcepart, newly_created=status_codes.contains(StatusCode.NEW_ROOM_CREATED) };
flag.enter_futures[bare_jid].set_value(join_result);
@@ -377,17 +373,22 @@ public class Module : XmppStreamModule {
if (affiliation_str != null) {
affiliation = parse_affiliation(affiliation_str);
flag.set_affiliation(presence.from.bare_jid, presence.from, affiliation);
- received_occupant_affiliation(stream, presence.from, affiliation);
}
string? jid_ = x_node.get_deep_attribute("item", "jid");
if (jid_ != null) {
try {
- Jid jid = new Jid(jid_);
- flag.set_real_jid(presence.from, jid);
+ Jid real_jid = new Jid(jid_);
+ flag.set_real_jid(presence.from, real_jid);
if (affiliation != null) {
- stream.get_flag(Flag.IDENTITY).set_offline_member(presence.from, jid, affiliation);
+ stream.get_flag(Flag.IDENTITY).set_offline_member(presence.from, real_jid, affiliation);
+ var affiliations = new HashMap<Jid, Affiliation>();
+ affiliations[real_jid.bare_jid] = affiliation;
+
+ string? mav_since = MucAffiliationsVersioning.get_mav_since(x_node);
+ string? mav_until = MucAffiliationsVersioning.get_mav_until(x_node);
+
+ received_affiliations(stream, presence.from.bare_jid, affiliations, mav_since, mav_until);
}
- received_occupant_jid(stream, presence.from, jid);
} catch (InvalidJidError e) {
warning("Received invalid occupant jid: %s", e.message);
}
@@ -468,6 +469,12 @@ public class Module : XmppStreamModule {
room_info_updated(stream, jid);
}
+ public async void fetch_affiliations(XmppStream stream, Jid muc_jid) {
+ query_affiliation.begin(stream, muc_jid, "member");
+ query_affiliation.begin(stream, muc_jid, "admin");
+ yield query_affiliation(stream, muc_jid, "owner");
+ }
+
private async Gee.List<Jid>? query_affiliation(XmppStream stream, Jid jid, string affiliation) {
Iq.Stanza iq = new Iq.Stanza.get(
new StanzaNode.build("query", NS_URI_ADMIN)
@@ -492,7 +499,6 @@ public class Module : XmppStreamModule {
Jid jid_ = new Jid(jid__);
stream.get_flag(Flag.IDENTITY).set_offline_member(iq_result.from, jid_, parse_affiliation(affiliation_));
ret_jids.add(jid_);
- received_occupant_jid(stream, iq_result.from, jid_);
} catch (InvalidJidError e) {
warning("Received invalid occupant jid: %s", e.message);
}
@@ -509,7 +515,7 @@ public class Module : XmppStreamModule {
return ret;
}
- private static Affiliation parse_affiliation(string affiliation_str) {
+ public static Affiliation parse_affiliation(string affiliation_str) {
Affiliation affiliation;
switch (affiliation_str) {
case "admin":
@@ -559,6 +565,7 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
if (message.type_ == MessageStanza.TYPE_NORMAL) {
StanzaNode? x_node = message.stanza.get_subnode("x", NS_URI_USER);
if (x_node != null) {
+ // Parse Invite
StanzaNode? invite_node = x_node.get_subnode("invite", NS_URI_USER);
string? password = null;
StanzaNode? password_node = x_node.get_subnode("password", NS_URI_USER);
@@ -580,6 +587,27 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
return true;
}
}
+
+ // Parse affiliation update
+ Gee.List<StanzaNode> item_nodes = x_node.get_subnodes("item", Muc.NS_URI_USER);
+ if (item_nodes.size > 0) {
+ var affiliations = new HashMap<Jid, Affiliation>();
+ foreach (StanzaNode item_node in item_nodes) {
+ string jid_str = item_node.get_attribute("jid", Muc.NS_URI_USER);
+ Jid real_jid = new Jid(jid_str);
+ string affiliation_str = item_node.get_attribute("affiliation", Muc.NS_URI_USER);
+ Muc.Affiliation affiliation = Muc.Module.parse_affiliation(affiliation_str);
+
+ affiliations[real_jid] = affiliation;
+
+ stream.get_flag(Flag.IDENTITY).set_offline_member(message.from, real_jid, affiliation);
+ }
+
+ string? mav_since = MucAffiliationsVersioning.get_mav_since(x_node);
+ string? mav_until = MucAffiliationsVersioning.get_mav_until(x_node);
+
+ outer.received_affiliations(stream, message.from, affiliations, mav_since, mav_until);
+ }
}
StanzaNode? x_field_node = message.stanza.get_subnode("x", DataForms.NS_URI);