aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2022-07-08 16:33:40 +0200
committerfiaxh <git@lightrise.org>2022-10-10 21:55:15 +0200
commit6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92 (patch)
treef92df8980bb6e65d9ce81f1395773201b664a779 /xmpp-vala
parent9c736af765d8c62838440afbfd2ad7ee78b44951 (diff)
downloaddino-6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92.tar.gz
dino-6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92.zip
Rewrite MAM logic and add MUC MAM
Diffstat (limited to 'xmpp-vala')
-rw-r--r--xmpp-vala/CMakeLists.txt3
-rw-r--r--xmpp-vala/src/module/xep/0045_muc/module.vala13
-rw-r--r--xmpp-vala/src/module/xep/0059_result_set_management.vala30
-rw-r--r--xmpp-vala/src/module/xep/0203_delayed_delivery.vala2
-rw-r--r--xmpp-vala/src/module/xep/0272_muji.vala2
-rw-r--r--xmpp-vala/src/module/xep/0297_stanza_forwarding.vala3
-rw-r--r--xmpp-vala/src/module/xep/0313_2_message_archive_management.vala80
-rw-r--r--xmpp-vala/src/module/xep/0313_message_archive_management.vala146
-rw-r--r--xmpp-vala/src/module/xep/0353_call_invite_message.vala2
-rw-r--r--xmpp-vala/src/module/xep/0353_jingle_message_initiation.vala2
10 files changed, 195 insertions, 88 deletions
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<MessageStanza> {
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<DataForms.DataForm.Field>();
+
+ 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<DataForms.DataForm.Field> 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<MessageStanza> {
private string[] after_actions_const = {};
@@ -123,19 +109,13 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
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;