diff options
| author | Marvin W <git@larma.de> | 2026-07-18 18:10:27 +0200 |
|---|---|---|
| committer | Marvin W <git@larma.de> | 2026-08-02 12:44:10 +0200 |
| commit | 64f09ce50063e6ccc135a535fe98f58751d7ac7d (patch) | |
| tree | f8e407f0ab94a44ef1208202df3778ca10f89cb5 /xmpp-vala/src/module/xep | |
| parent | 45723ac201c1905571f9f78c8c970828db97139a (diff) | |
Add support for XEP-0490 Message Displayed Synchronization
Diffstat (limited to 'xmpp-vala/src/module/xep')
| -rw-r--r-- | xmpp-vala/src/module/xep/0060_pubsub.vala | 11 | ||||
| -rw-r--r-- | xmpp-vala/src/module/xep/0490_message_displayed_synchronization.vala | 92 |
2 files changed, 98 insertions, 5 deletions
diff --git a/xmpp-vala/src/module/xep/0060_pubsub.vala b/xmpp-vala/src/module/xep/0060_pubsub.vala index c6395eb9..c9af4263 100644 --- a/xmpp-vala/src/module/xep/0060_pubsub.vala +++ b/xmpp-vala/src/module/xep/0060_pubsub.vala @@ -41,9 +41,9 @@ namespace Xmpp.Xep.Pubsub { retract_listeners.unset(node); } - public async Gee.List<StanzaNode>? request_all(XmppStream stream, Jid jid, string node) { // TODO multiple nodes gehen auch + public async Gee.List<StanzaNode>? request_all(XmppStream stream, Jid? jid, string node) { // TODO multiple nodes gehen auch Iq.Stanza request_iq = new Iq.Stanza.get(new StanzaNode.build("pubsub", NS_URI).add_self_xmlns().put_node(new StanzaNode.build("items", NS_URI).put_attribute("node", node))); - request_iq.to = jid; + if (jid != null) request_iq.to = jid; Iq.Stanza iq_res = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, request_iq); @@ -56,9 +56,9 @@ namespace Xmpp.Xep.Pubsub { } public delegate void OnResult(XmppStream stream, Jid jid, string? id, StanzaNode? node); - public void request(XmppStream stream, Jid jid, string node, owned OnResult listener) { // TODO multiple nodes gehen auch + public void request(XmppStream stream, Jid? jid, string node, owned OnResult listener) { // TODO multiple nodes gehen auch Iq.Stanza request_iq = new Iq.Stanza.get(new StanzaNode.build("pubsub", NS_URI).add_self_xmlns().put_node(new StanzaNode.build("items", NS_URI).put_attribute("node", node))); - request_iq.to = jid; + if (jid != null) request_iq.to = jid; stream.get_module(Iq.Module.IDENTITY).send_iq(stream, request_iq, (stream, iq) => { StanzaNode event_node = iq.stanza.get_subnode("pubsub", NS_URI); StanzaNode items_node = event_node != null ? event_node.get_subnode("items", NS_URI) : null; @@ -100,6 +100,7 @@ namespace Xmpp.Xep.Pubsub { } Iq.Stanza iq = new Iq.Stanza.set(pubsub_node); + if (jid != null) iq.to = jid; // If the node was configured differently before, reconfigure it to meet our requirements and try again Iq.Stanza iq_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); @@ -222,7 +223,7 @@ namespace Xmpp.Xep.Pubsub { } private async bool change_node_config(XmppStream stream, Jid jid, string node, PublishOptions publish_options) { - DataForms.DataForm? data_form = yield stream.get_module(Pubsub.Module.IDENTITY).request_node_config(stream, null, node); + DataForms.DataForm? data_form = yield stream.get_module(Pubsub.Module.IDENTITY).request_node_config(stream, jid, node); if (data_form == null) return false; foreach (DataForms.DataForm.Field field in data_form.fields) { diff --git a/xmpp-vala/src/module/xep/0490_message_displayed_synchronization.vala b/xmpp-vala/src/module/xep/0490_message_displayed_synchronization.vala new file mode 100644 index 00000000..38437735 --- /dev/null +++ b/xmpp-vala/src/module/xep/0490_message_displayed_synchronization.vala @@ -0,0 +1,92 @@ +using Gee; +namespace Xmpp.Xep.MessageDisplayedSynchronization { + public const string NS_URI = "urn:xmpp:mds:displayed:0"; + public const string NS_URI_SERVER_ASSIST = "urn:xmpp:mds:server-assist:0"; + + public class Module : XmppStreamModule { + public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0490_message_displayed_synchronization"); + + public signal void on_message_displayed(Jid conversation_jid, Jid stanza_id_by_jid, string stanza_id); + + private void handle_displayed_node_item(XmppStream stream, string? id, StanzaNode? item) { + if (id == null || item == null) return; + Jid conversation_jid = Jid.from_string(id); + StanzaNode? stanza_id_node = item.get_subnode("stanza-id", UniqueStableStanzaIDs.NS_URI); + if (stanza_id_node == null || stanza_id_node.get_attribute("by") == null || stanza_id_node.get_attribute("id") == null) return; + Jid stanza_id_by_jid = Jid.from_string(stanza_id_node.get_attribute("by")); + string stanza_id = stanza_id_node.get_attribute("id"); + if (stream.has_flag(Flag.IDENTITY)) { + stream.get_flag(Flag.IDENTITY).learn_mds(conversation_jid, stanza_id_by_jid, stanza_id); + } + on_message_displayed(conversation_jid, stanza_id_by_jid, stanza_id); + } + + private void on_pubsub_item(XmppStream stream, Jid jid, string id, StanzaNode? item) { + if (!jid.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid)) { + warning("Received alleged mds item from %s, ignoring", jid.to_string()); + return; + } + handle_displayed_node_item(stream, id, item); + } + + private void on_initial_presence_sent(XmppStream stream) { + var pubsub = stream.get_module(Pubsub.Module.IDENTITY); + pubsub.request_all.begin(stream, null, NS_URI, (_, res) => { + Gee.List<StanzaNode>? items = pubsub.request_all.end(res); + foreach (StanzaNode node_item in items) { + handle_displayed_node_item(stream, node_item.get_attribute("id"), node_item.get_subnode("displayed", NS_URI)); + } + }); + } + + public async bool update_message_displayed(XmppStream stream, Jid conversation_jid, Jid stanza_id_by_jid, string stanza_id) { + var pubsub = stream.get_module(Pubsub.Module.IDENTITY); + var node = new StanzaNode.build("displayed", NS_URI).add_self_xmlns() + .put_node(new StanzaNode.build("stanza-id", UniqueStableStanzaIDs.NS_URI).add_self_xmlns() + .put_attribute("by", stanza_id_by_jid.to_string()) + .put_attribute("id", stanza_id)); + var options = new Pubsub.PublishOptions() + .set_persist_items(true) + .set_max_items("max") + .set_send_last_published_item("never") + .set_access_model("whitelist"); + return yield pubsub.publish(stream, stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid, NS_URI, conversation_jid.to_string(), node, options); + } + + public override void attach(XmppStream stream) { + stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NS_URI, on_pubsub_item, null, null); + stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.connect(on_initial_presence_sent); + if (!stream.has_flag(Flag.IDENTITY)) { + stream.add_flag(new Flag()); + } + } + + public override void detach(XmppStream stream) { + stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.disconnect(on_initial_presence_sent); + stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NS_URI); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return IDENTITY.id; } + + } + + public class Flag : XmppStreamFlag { + public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "mds"); + + private Map<Jid, Jid> stanza_id_bys = new HashMap<Jid, Jid>(Jid.hash_func, Jid.equals_func); + private Map<Jid, string> stanza_ids = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func); + + public void learn_mds(Jid jid, Jid stanza_by, string stanza_id) { + stanza_id_bys[jid] = stanza_by; + stanza_ids[jid] = stanza_id; + } + + public bool is_displayed(Jid conversation_jid, Jid stanza_by, string stanza_id) { + return stanza_id_bys[conversation_jid].equals(stanza_by) && stanza_ids[conversation_jid] == stanza_id; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return IDENTITY.id; } + } +}
\ No newline at end of file |
