From 6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Fri, 8 Jul 2022 16:33:40 +0200 Subject: Rewrite MAM logic and add MUC MAM --- xmpp-vala/CMakeLists.txt | 3 + xmpp-vala/src/module/xep/0045_muc/module.vala | 13 +- .../src/module/xep/0059_result_set_management.vala | 30 +++++ .../src/module/xep/0203_delayed_delivery.vala | 2 +- xmpp-vala/src/module/xep/0272_muji.vala | 2 +- .../src/module/xep/0297_stanza_forwarding.vala | 3 + .../xep/0313_2_message_archive_management.vala | 80 +++++++++++ .../xep/0313_message_archive_management.vala | 146 ++++++++++----------- .../src/module/xep/0353_call_invite_message.vala | 2 +- .../module/xep/0353_jingle_message_initiation.vala | 2 +- 10 files changed, 195 insertions(+), 88 deletions(-) create mode 100644 xmpp-vala/src/module/xep/0059_result_set_management.vala create mode 100644 xmpp-vala/src/module/xep/0297_stanza_forwarding.vala create mode 100644 xmpp-vala/src/module/xep/0313_2_message_archive_management.vala (limited to 'xmpp-vala') diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt index 5b767448..4ad7f0e9 100644 --- a/xmpp-vala/CMakeLists.txt +++ b/xmpp-vala/CMakeLists.txt @@ -75,6 +75,7 @@ SOURCES "src/module/xep/0047_in_band_bytestreams.vala" "src/module/xep/0049_private_xml_storage.vala" + "src/module/xep/0059_result_set_management.vala" "src/module/xep/0054_vcard/module.vala" "src/module/xep/0060_pubsub.vala" "src/module/xep/0065_socks5_bytestreams.vala" @@ -126,9 +127,11 @@ SOURCES "src/module/xep/0261_jingle_in_band_bytestreams.vala" "src/module/xep/0272_muji.vala" "src/module/xep/0280_message_carbons.vala" + "src/module/xep/0297_stanza_forwarding.vala" "src/module/xep/0298_coin.vala" "src/module/xep/0308_last_message_correction.vala" "src/module/xep/0313_message_archive_management.vala" + "src/module/xep/0313_2_message_archive_management.vala" "src/module/xep/0333_chat_markers.vala" "src/module/xep/0334_message_processing_hints.vala" "src/module/xep/0353_jingle_message_initiation.vala" diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala index 56d50210..f8ddb6d0 100644 --- a/xmpp-vala/src/module/xep/0045_muc/module.vala +++ b/xmpp-vala/src/module/xep/0045_muc/module.vala @@ -81,7 +81,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, 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) { try { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid.with_resource(nick); @@ -90,10 +90,15 @@ public class Module : XmppStreamModule { if (password != null) { x_node.put_node(new StanzaNode.build("password", NS_URI).put_node(new StanzaNode.text(password))); } - if (history_since != null) { + if (history_since != null || !receive_history) { StanzaNode history_node = new StanzaNode.build("history", NS_URI); - history_node.set_attribute("since", DateTimeProfiles.to_datetime(history_since)); x_node.put_node(history_node); + + if (history_since != null) { + history_node.set_attribute("since", DateTimeProfiles.to_datetime(history_since)); + } else if (!receive_history) { + history_node.set_attribute("maxchars", "0"); + } } presence.stanza.put_node(x_node); @@ -561,7 +566,7 @@ public class ReceivedPipelineListener : StanzaListener { StanzaNode? reason_node = invite_node.get_subnode("reason", NS_URI_USER); string? reason = null; if (reason_node != null) reason = reason_node.get_string_content(); - bool is_mam_message = Xep.MessageArchiveManagement.MessageFlag.get_flag(message) != null; // TODO + bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message) != null; // TODO if (!is_mam_message) outer.invite_received(stream, message.from, from_jid, password, reason); return true; } diff --git a/xmpp-vala/src/module/xep/0059_result_set_management.vala b/xmpp-vala/src/module/xep/0059_result_set_management.vala new file mode 100644 index 00000000..acd630dc --- /dev/null +++ b/xmpp-vala/src/module/xep/0059_result_set_management.vala @@ -0,0 +1,30 @@ +namespace Xmpp.ResultSetManagement { + public const string NS_URI = "http://jabber.org/protocol/rsm"; + + public class ResultSetParameters { + string? before { get; set; } + string? after { get; set; } + int? max { get; set; } + } + + public StanzaNode create_set_rsm_node_before(string? before_id) { + var max_node = (new StanzaNode.build("max", Xmpp.ResultSetManagement.NS_URI)).put_node(new StanzaNode.text("20")); + var node = (new StanzaNode.build("set", Xmpp.ResultSetManagement.NS_URI)).add_self_xmlns() + .put_node(max_node); + var before_node = new StanzaNode.build("before", Xmpp.ResultSetManagement.NS_URI); + if (before_id != null) before_node.put_node(new StanzaNode.text(before_id)); + node.put_node(before_node); + return node; + } + + public StanzaNode create_set_rsm_node_after(string after_id) { + var max_node = (new StanzaNode.build("max", Xmpp.ResultSetManagement.NS_URI)).put_node(new StanzaNode.text("20")); + var node = (new StanzaNode.build("set", Xmpp.ResultSetManagement.NS_URI)).add_self_xmlns() + .put_node(max_node); + + var after_node = new StanzaNode.build("after", Xmpp.ResultSetManagement.NS_URI) + .put_node(new StanzaNode.text(after_id)); + node.put_node(after_node); + return node; + } +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0203_delayed_delivery.vala b/xmpp-vala/src/module/xep/0203_delayed_delivery.vala index 256cba7a..ae344d2e 100644 --- a/xmpp-vala/src/module/xep/0203_delayed_delivery.vala +++ b/xmpp-vala/src/module/xep/0203_delayed_delivery.vala @@ -1,6 +1,6 @@ namespace Xmpp.Xep.DelayedDelivery { -private const string NS_URI = "urn:xmpp:delay"; +public const string NS_URI = "urn:xmpp:delay"; public static DateTime? get_time_for_node(StanzaNode node) { string? time = node.get_attribute("stamp"); diff --git a/xmpp-vala/src/module/xep/0272_muji.vala b/xmpp-vala/src/module/xep/0272_muji.vala index b1dd7f40..243ed93e 100644 --- a/xmpp-vala/src/module/xep/0272_muji.vala +++ b/xmpp-vala/src/module/xep/0272_muji.vala @@ -15,7 +15,7 @@ namespace Xmpp.Xep.Muji { group_call.our_nick = "%08x".printf(Random.next_int()); debug(@"[%s] MUJI joining as %s", stream.get_flag(Bind.Flag.IDENTITY).my_jid.to_string(), group_call.our_nick); - Xep.Muc.JoinResult? result = yield stream.get_module(Muc.Module.IDENTITY).enter(stream, muc_jid, group_call.our_nick, null, null, initial_muji_node); + Xep.Muc.JoinResult? result = yield stream.get_module(Muc.Module.IDENTITY).enter(stream, muc_jid, group_call.our_nick, null, null, false, initial_muji_node); if (result == null || result.nick == null) return null; debug(@"[%s] MUJI joining as %s done", stream.get_flag(Bind.Flag.IDENTITY).my_jid.to_string(), group_call.our_nick); diff --git a/xmpp-vala/src/module/xep/0297_stanza_forwarding.vala b/xmpp-vala/src/module/xep/0297_stanza_forwarding.vala new file mode 100644 index 00000000..ddac7eef --- /dev/null +++ b/xmpp-vala/src/module/xep/0297_stanza_forwarding.vala @@ -0,0 +1,3 @@ +namespace Xmpp.StanzaForwarding { + public const string NS_URI = "urn:xmpp:forward:0"; +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala b/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala new file mode 100644 index 00000000..a710a459 --- /dev/null +++ b/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala @@ -0,0 +1,80 @@ +using Gee; +using Xmpp.Xep; + +namespace Xmpp.MessageArchiveManagement.V2 { + + public class MamQueryParams { + public bool use_ns2_extended = false; + + public string query_id = Xmpp.random_uuid(); + public Jid mam_server { get; set; } + public Jid? with { get; set; } + // "The 'start' field is used to filter out messages before a certain date/time." + public DateTime? start { get; set; } + // "the 'end' field is used to exclude from the results messages after a certain point in time" + public DateTime? end { get; set; } + public string? start_id { get; set; } + public string? end_id { get; set; } + + public MamQueryParams.query_latest(Jid mam_server, DateTime? latest_known_time, string? latest_known_id) { + this.mam_server = mam_server; + this.start = latest_known_time; + this.start_id = latest_known_id; + } + + public MamQueryParams.query_between(Jid mam_server, + DateTime? earliest_time, string? earliest_id, + DateTime? latest_time, string? latest_id) { + this.mam_server = mam_server; + this.start = earliest_time; + this.start_id = earliest_id; + this.end = latest_time; + this.end_id = latest_id; + } + + public MamQueryParams.query_before(Jid mam_server, DateTime? earliest_time, string? earliest_id) { + this.mam_server = mam_server; + this.end = earliest_time; + this.end_id = earliest_id; + } + } + + private StanzaNode create_base_query(XmppStream stream, MamQueryParams mam_params) { + var fields = new ArrayList(); + + if (mam_params.with != null) { + DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="with" }; + field.set_value_string(mam_params.with.to_string()); + fields.add(field); + } + if (mam_params.start != null) { + DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="start" }; + field.set_value_string(DateTimeProfiles.to_datetime(mam_params.start)); + fields.add(field); + } + if (mam_params.end != null) { + DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="end" }; + field.set_value_string(DateTimeProfiles.to_datetime(mam_params.end)); + fields.add(field); + } + + return MessageArchiveManagement.create_base_query(stream, MessageArchiveManagement.NS_URI_2, mam_params.query_id, fields); + } + + public async QueryResult query_archive(XmppStream stream, MamQueryParams mam_params) { + var query_node = create_base_query(stream, mam_params); + if (!mam_params.use_ns2_extended) { + query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id)); + } + + return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node); + } + + public async QueryResult page_through_results(XmppStream stream, MamQueryParams mam_params, QueryResult prev_result) { + var query_node = create_base_query(stream, mam_params); + query_node.put_node(ResultSetManagement.create_set_rsm_node_before(prev_result.first)); + + return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node); + } +} + diff --git a/xmpp-vala/src/module/xep/0313_message_archive_management.vala b/xmpp-vala/src/module/xep/0313_message_archive_management.vala index c24c6b04..36a43ac9 100644 --- a/xmpp-vala/src/module/xep/0313_message_archive_management.vala +++ b/xmpp-vala/src/module/xep/0313_message_archive_management.vala @@ -1,11 +1,18 @@ -namespace Xmpp.Xep.MessageArchiveManagement { +using Gee; +using Xmpp.Xep; + +namespace Xmpp.MessageArchiveManagement { public const string NS_URI = "urn:xmpp:mam:2"; public const string NS_URI_2 = "urn:xmpp:mam:2"; public const string NS_URI_1 = "urn:xmpp:mam:1"; -private static string NS_VER(XmppStream stream) { - return stream.get_flag(Flag.IDENTITY).ns_ver; +public class QueryResult { + public bool error { get; set; default=false; } + public bool malformed { get; set; default=false; } + public bool complete { get; set; default=false; } + public string first { get; set; } + public string last { get; set; } } public class Module : XmppStreamModule { @@ -15,54 +22,6 @@ public class Module : XmppStreamModule { private ReceivedPipelineListener received_pipeline_listener = new ReceivedPipelineListener(); - private StanzaNode crate_base_query(XmppStream stream, string? jid, string? queryid, DateTime? start, DateTime? end) { - DataForms.DataForm data_form = new DataForms.DataForm(); - DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" }; - form_type_field.set_value_string(NS_VER(stream)); - data_form.add_field(form_type_field); - if (jid != null) { - DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="with" }; - field.set_value_string(jid); - data_form.add_field(field); - } - if (start != null) { - DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="start" }; - field.set_value_string(DateTimeProfiles.to_datetime(start)); - data_form.add_field(field); - } - if (end != null) { - DataForms.DataForm.Field field = new DataForms.DataForm.Field() { var="end" }; - field.set_value_string(DateTimeProfiles.to_datetime(end)); - data_form.add_field(field); - } - StanzaNode query_node = new StanzaNode.build("query", NS_VER(stream)).add_self_xmlns().put_node(data_form.get_submit_node()); - if (queryid != null) { - query_node.put_attribute("queryid", queryid); - } - return query_node; - } - - private StanzaNode create_set_rsm_node(string? before_id) { - var before_node = new StanzaNode.build("before", "http://jabber.org/protocol/rsm"); - if (before_id != null) { - before_node.put_node(new StanzaNode.text(before_id)); - } - var max_node = (new StanzaNode.build("max", "http://jabber.org/protocol/rsm")).put_node(new StanzaNode.text("20")); - return (new StanzaNode.build("set", "http://jabber.org/protocol/rsm")).add_self_xmlns() - .put_node(before_node) - .put_node(max_node); - } - - public async Iq.Stanza? query_archive(XmppStream stream, string? jid, string? query_id, DateTime? start_time, string? start_id, DateTime? end_time, string? end_id) { - if (stream.get_flag(Flag.IDENTITY) == null) return null; - - var query_node = crate_base_query(stream, jid, query_id, start_time, end_time); - query_node.put_node(create_set_rsm_node(end_id)); - Iq.Stanza iq = new Iq.Stanza.set(query_node); - - return yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); - } - public override void attach(XmppStream stream) { stream.get_module(MessageModule.IDENTITY).received_pipeline.connect(received_pipeline_listener); stream.stream_negotiated.connect(query_availability); @@ -75,25 +34,6 @@ public class Module : XmppStreamModule { public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } - public async Iq.Stanza? page_through_results(XmppStream stream, string? jid, string? query_id, DateTime? start_time, DateTime? end_time, Iq.Stanza iq) { - - string? complete = iq.stanza.get_deep_attribute("urn:xmpp:mam:2:fin", "complete"); - if (complete == "true") { - return null; - } - string? first = iq.stanza.get_deep_string_content(NS_VER(stream) + ":fin", "http://jabber.org/protocol/rsm" + ":set", "first"); - if (first == null) { - return null; - } - - var query_node = crate_base_query(stream, jid, query_id, start_time, end_time); - query_node.put_node(create_set_rsm_node(first)); - - Iq.Stanza paging_iq = new Iq.Stanza.set(query_node); - - return yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, paging_iq); - } - private async void query_availability(XmppStream stream) { Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid; @@ -113,6 +53,52 @@ public class Module : XmppStreamModule { } } + internal StanzaNode create_base_query(XmppStream stream, string ns, string? queryid, Gee.List fields) { + DataForms.DataForm data_form = new DataForms.DataForm(); + + DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" }; + form_type_field.set_value_string(NS_VER(stream)); + data_form.add_field(form_type_field); + + foreach (var field in fields) { + data_form.add_field(field); + } + + StanzaNode query_node = new StanzaNode.build("query", NS_VER(stream)).add_self_xmlns().put_node(data_form.get_submit_node()); + if (queryid != null) { + query_node.put_attribute("queryid", queryid); + } + return query_node; + } + + internal async QueryResult query_archive(XmppStream stream, string ns, Jid? mam_server, StanzaNode query_node) { + var res = new QueryResult(); + + if (stream.get_flag(Flag.IDENTITY) == null) { res.error = true; return res; } + + // Build and send query + Iq.Stanza iq = new Iq.Stanza.set(query_node) { to=mam_server }; + + print(@"OUT:\n$(iq.stanza.to_string())\n"); + Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); + + print(result_iq.stanza.to_string() + "\n"); + + // Parse the response IQ into a QueryResult. + StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", ns); + if (fin_node == null) { print(@"$ns a1\n"); res.malformed = true; return res; } + + StanzaNode? rsm_node = fin_node.get_subnode("set", Xmpp.ResultSetManagement.NS_URI); + if (rsm_node == null) { print("a2\n"); res.malformed = true; return res; } + + res.first = rsm_node.get_deep_string_content("first"); + res.last = rsm_node.get_deep_string_content("last"); + if ((res.first == null) != (res.last == null)) { print("a3\n"); res.malformed = true; } + res.complete = fin_node.get_attribute_bool("complete", false, ns); + + return res; + } + public class ReceivedPipelineListener : StanzaListener { private string[] after_actions_const = {}; @@ -123,19 +109,13 @@ public class ReceivedPipelineListener : StanzaListener { public override async bool run(XmppStream stream, MessageStanza message) { if (stream.get_flag(Flag.IDENTITY) == null) return false; - StanzaNode? message_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", "urn:xmpp:forward:0:forwarded", Xmpp.NS_URI + ":message"); + StanzaNode? message_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message"); if (message_node != null) { - // MAM messages must come from our server // TODO or a MUC server - if (!message.from.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid)) { - warning("Received alleged MAM message from %s, ignoring", message.from.to_string()); - return true; - } - - StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", "urn:xmpp:forward:0:forwarded", DelayedDelivery.NS_URI + ":delay"); + StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); DateTime? datetime = DelayedDelivery.get_time_for_node(forward_node); string? mam_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":id"); string? query_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":queryid"); - message.add_flag(new MessageFlag(datetime, mam_id, query_id)); + message.add_flag(new MessageFlag(message.from, datetime, mam_id, query_id)); message.stanza = message_node; message.rerun_parsing = true; @@ -160,11 +140,13 @@ public class Flag : XmppStreamFlag { public class MessageFlag : Xmpp.MessageFlag { public const string ID = "message_archive_management"; + public Jid sender_jid { get; private set; } public DateTime? server_time { get; private set; } public string? mam_id { get; private set; } public string? query_id { get; private set; } - public MessageFlag(DateTime? server_time, string? mam_id, string? query_id) { + public MessageFlag(Jid sender_jid, DateTime? server_time, string? mam_id, string? query_id) { + this.sender_jid = sender_jid; this.server_time = server_time; this.mam_id = mam_id; this.query_id = query_id; @@ -176,4 +158,8 @@ public class MessageFlag : Xmpp.MessageFlag { public override string get_id() { return ID; } } +private static string NS_VER(XmppStream stream) { + return stream.get_flag(Flag.IDENTITY).ns_ver; } + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0353_call_invite_message.vala b/xmpp-vala/src/module/xep/0353_call_invite_message.vala index 8031beaf..c467cde7 100644 --- a/xmpp-vala/src/module/xep/0353_call_invite_message.vala +++ b/xmpp-vala/src/module/xep/0353_call_invite_message.vala @@ -68,7 +68,7 @@ namespace Xmpp.Xep.CallInvites { } private void on_received_message(XmppStream stream, MessageStanza message) { - Xep.MessageArchiveManagement.MessageFlag? mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message); + Xmpp.MessageArchiveManagement.MessageFlag? mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); if (mam_flag != null) return; StanzaNode? relevant_node = null; diff --git a/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala b/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala index 730f07e8..2596abbb 100644 --- a/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala +++ b/xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala @@ -53,7 +53,7 @@ namespace Xmpp.Xep.JingleMessageInitiation { private void on_received_message(XmppStream stream, MessageStanza message) { if (message.type_ == MessageStanza.TYPE_GROUPCHAT) return; - Xep.MessageArchiveManagement.MessageFlag? mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message); + Xmpp.MessageArchiveManagement.MessageFlag? mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); if (mam_flag != null) return; StanzaNode? mi_node = null; -- cgit v1.2.3-70-g09d2