refactor: share MAM archive lookup helper

Amolith created

Amp-Thread-ID: https://ampcode.com/threads/T-019f2fe4-f392-7688-8b2f-8d40eb9fa4b8

Change summary

src/mode/mam.c     | 248 +++++++++++++++++++++++++++++++++++++++++---
src/mode/mam.h     |   4 
src/mode/message.c | 267 ++++-------------------------------------------
3 files changed, 262 insertions(+), 257 deletions(-)

Detailed changes

src/mode/mam.c 🔗

@@ -51,11 +51,14 @@
 #define FORWARD_NS "urn:xmpp:forward:0"
 #define DELAY_NS "urn:xmpp:delay"
 #define RSM_NS "http://jabber.org/protocol/rsm"
+#define SID_NS "urn:xmpp:sid:0"
 
 #define MAM_DEFAULT_MAX 5
 #define MAM_MAX_CAP 50
 #define MAM_DEFAULT_TIMEOUT_SECONDS 300
 #define MAM_POLL_PERIOD_MS 2000
+#define MAM_ARCHIVE_LOOKUP_TIMEOUT_SECONDS 15
+#define MAM_ARCHIVE_LOOKUP_POLL_MS 1000
 
 typedef struct {
   char *with;
@@ -79,17 +82,42 @@ typedef struct {
   bool done;
 } mam_run_t;
 
+typedef struct {
+  xmppc_t *xmppc;
+  char *with;
+  char *message_id;
+  char *start;
+  char *queryid;
+  char *archive_id;
+  gint64 deadline_us;
+  bool done;
+  MamArchiveLookupCallback callback;
+  void *userdata;
+} mam_archive_lookup_t;
+
+static mam_archive_lookup_t *mam_archive_lookup_active = NULL;
+
 static void _mam_options_init(mam_options_t *options);
 static void _mam_options_free(mam_options_t *options);
 static bool _mam_parse_terms(xmppc_t *xmppc, int argc, char *argv[], mam_options_t *options, bool receive);
 static void _mam_start_request(xmppc_t *xmppc, mam_options_t *options, bool pretty, bool receive);
 static void _mam_send_request(mam_run_t *run);
+static void _mam_send_query(xmppc_t *xmppc, mam_options_t *options, char **queryid, xmpp_handler finish_handler, void *userdata);
 static int _mam_finish(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
 static int _mam_receive_poll(xmpp_conn_t *const conn, void *const userdata);
 static int _mam_receive_timeout(xmpp_conn_t *const conn, void *const userdata);
 static int _mam_display_message(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
 static int _mam_display_pretty_message(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
 static void _mam_disconnect_and_free(xmpp_conn_t *conn, mam_run_t *run, bool delete_id_handler);
+static void _mam_send_archive_lookup_query(mam_archive_lookup_t *lookup);
+static int _mam_archive_lookup_result_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
+static int _mam_archive_lookup_finished(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
+static int _mam_archive_lookup_poll(xmpp_conn_t *const conn, void *const userdata);
+static int _mam_archive_lookup_timeout(xmpp_conn_t *const conn, void *const userdata);
+static bool _mam_archive_lookup_result_matches(mam_archive_lookup_t *lookup, xmpp_stanza_t *result);
+static bool _mam_has_origin_id(xmpp_stanza_t *message, const char *id);
+static void _mam_archive_lookup_done(xmpp_conn_t *conn, mam_archive_lookup_t *lookup, bool delete_id_handler);
+static void _mam_archive_lookup_free(mam_archive_lookup_t *lookup);
 static void _mam_add_field(xmpp_ctx_t *ctx, xmpp_stanza_t *x, const char *var, const char *value, const char *type);
 static void _mam_add_ids_field(xmpp_ctx_t *ctx, xmpp_stanza_t *x, const char *ids);
 static void _mam_add_text_child(xmpp_ctx_t *ctx, xmpp_stanza_t *parent, const char *name, const char *text);
@@ -287,15 +315,18 @@ static void _mam_start_request(xmppc_t *xmppc, mam_options_t *options, bool pret
 }
 
 static void _mam_send_request(mam_run_t *run) {
-  xmppc_t *xmppc = run->xmppc;
+  _mam_send_query(run->xmppc, &run->options, &run->queryid, _mam_finish, run);
+}
+
+static void _mam_send_query(xmppc_t *xmppc, mam_options_t *options, char **queryid, xmpp_handler finish_handler, void *userdata) {
   xmpp_ctx_t *ctx = xmppc->ctx;
   char max[16];
 
-  g_snprintf(max, sizeof(max), "%d", run->options.max);
+  g_snprintf(max, sizeof(max), "%d", options->max);
 
   char* id = xmpp_uuid_gen(ctx);
-  g_free(run->queryid);
-  run->queryid = g_strdup(id);
+  g_free(*queryid);
+  *queryid = g_strdup(id);
 
   xmpp_stanza_t *iq = xmpp_iq_new(ctx, "set", id);
   xmpp_stanza_t *query = xmpp_stanza_new(ctx);
@@ -311,33 +342,33 @@ static void _mam_send_request(mam_run_t *run) {
 
   _mam_add_field(ctx, x, "FORM_TYPE", MAM_NS, "hidden");
 
-  if (run->options.with) {
-    _mam_add_field(ctx, x, "with", run->options.with, NULL);
+  if (options->with) {
+    _mam_add_field(ctx, x, "with", options->with, NULL);
   }
-  if (run->options.start) {
-    _mam_add_field(ctx, x, "start", run->options.start, NULL);
+  if (options->start) {
+    _mam_add_field(ctx, x, "start", options->start, NULL);
   }
-  if (run->options.end) {
-    _mam_add_field(ctx, x, "end", run->options.end, NULL);
+  if (options->end) {
+    _mam_add_field(ctx, x, "end", options->end, NULL);
   }
-  if (run->options.ids) {
-    _mam_add_ids_field(ctx, x, run->options.ids);
+  if (options->ids) {
+    _mam_add_ids_field(ctx, x, options->ids);
   }
 
   xmpp_stanza_t *set = xmpp_stanza_new(ctx);
   xmpp_stanza_set_name(set, "set");
   xmpp_stanza_set_ns(set, RSM_NS);
   _mam_add_text_child(ctx, set, "max", max);
-  if (run->options.after_id) {
-    _mam_add_text_child(ctx, set, "after", run->options.after_id);
+  if (options->after_id) {
+    _mam_add_text_child(ctx, set, "after", options->after_id);
   }
-  if (run->options.before_id) {
-    _mam_add_text_child(ctx, set, "before", run->options.before_id);
+  if (options->before_id) {
+    _mam_add_text_child(ctx, set, "before", options->before_id);
   }
   xmpp_stanza_add_child(query, set);
 
   xmpp_stanza_add_child(iq,query);
-  xmpp_id_handler_add(xmppc->conn, _mam_finish, id, run);
+  xmpp_id_handler_add(xmppc->conn, finish_handler, id, userdata);
   xmpp_send(xmppc->conn,iq);
 
   xmpp_stanza_release(iq);
@@ -417,6 +448,189 @@ static void _mam_disconnect_and_free(xmpp_conn_t *conn, mam_run_t *run, bool del
   xmpp_disconnect(conn);
 }
 
+void mam_lookup_message_archive_id(xmppc_t *xmppc, const char *with, const char *message_id, MamArchiveLookupCallback callback, void *userdata) {
+  if (!with || !message_id) {
+    if (callback) {
+      callback(xmppc->conn, NULL, userdata);
+    }
+    return;
+  }
+
+  if (mam_archive_lookup_active) {
+    logError(xmppc, "MAM archive lookup already active\n");
+    if (callback) {
+      callback(xmppc->conn, NULL, userdata);
+    }
+    return;
+  }
+
+  mam_archive_lookup_t *lookup = g_new0(mam_archive_lookup_t, 1);
+  lookup->xmppc = xmppc;
+  lookup->with = g_strdup(with);
+  lookup->message_id = g_strdup(message_id);
+  lookup->start = _mam_normalize_datetime("-30s");
+  lookup->deadline_us = g_get_monotonic_time() + ((gint64)MAM_ARCHIVE_LOOKUP_TIMEOUT_SECONDS * G_USEC_PER_SEC);
+  lookup->callback = callback;
+  lookup->userdata = userdata;
+  mam_archive_lookup_active = lookup;
+
+  xmpp_handler_add(xmppc->conn, _mam_archive_lookup_result_handler, NULL, "message", NULL, lookup);
+  xmpp_timed_handler_add(xmppc->conn, _mam_archive_lookup_timeout, MAM_ARCHIVE_LOOKUP_TIMEOUT_SECONDS * 1000, lookup);
+  _mam_send_archive_lookup_query(lookup);
+}
+
+static void _mam_send_archive_lookup_query(mam_archive_lookup_t *lookup) {
+  mam_options_t options;
+  _mam_options_init(&options);
+  options.with = lookup->with;
+  options.start = lookup->start;
+  options.before_id = "";
+  options.max = MAM_MAX_CAP;
+
+  _mam_send_query(lookup->xmppc, &options, &lookup->queryid, _mam_archive_lookup_finished, lookup);
+}
+
+static int _mam_archive_lookup_result_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) {
+  mam_archive_lookup_t *lookup = (mam_archive_lookup_t *)userdata;
+  xmpp_stanza_t* result = xmpp_stanza_get_child_by_name(stanza,"result");
+
+  if (!lookup->done && result && _mam_archive_lookup_result_matches(lookup, result)) {
+    const char *archive_id = xmpp_stanza_get_attribute(result, "id");
+    if (archive_id) {
+      g_free(lookup->archive_id);
+      lookup->archive_id = g_strdup(archive_id);
+    }
+  }
+
+  return 1;
+}
+
+static int _mam_archive_lookup_finished(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) {
+  mam_archive_lookup_t *lookup = (mam_archive_lookup_t *)userdata;
+  const char *type = xmpp_stanza_get_type(stanza);
+
+  if (lookup->done) {
+    return 0;
+  }
+
+  if (lookup->archive_id) {
+    _mam_archive_lookup_done(conn, lookup, false);
+    return 0;
+  }
+
+  if (type && strcmp(type, "error") == 0) {
+    logError(lookup->xmppc, "MAM archive lookup failed\n");
+    _mam_archive_lookup_done(conn, lookup, false);
+    return 0;
+  }
+
+  if (g_get_monotonic_time() < lookup->deadline_us) {
+    xmpp_timed_handler_add(conn, _mam_archive_lookup_poll, MAM_ARCHIVE_LOOKUP_POLL_MS, lookup);
+  } else {
+    logError(lookup->xmppc, "MAM archive lookup timed out\n");
+    _mam_archive_lookup_done(conn, lookup, false);
+  }
+
+  return 0;
+}
+
+static int _mam_archive_lookup_poll(xmpp_conn_t *const conn, void *const userdata) {
+  mam_archive_lookup_t *lookup = (mam_archive_lookup_t *)userdata;
+
+  if (lookup->done) {
+    return 0;
+  }
+
+  if (g_get_monotonic_time() >= lookup->deadline_us) {
+    logError(lookup->xmppc, "MAM archive lookup timed out\n");
+    _mam_archive_lookup_done(conn, lookup, false);
+    return 0;
+  }
+
+  _mam_send_archive_lookup_query(lookup);
+  return 0;
+}
+
+static int _mam_archive_lookup_timeout(xmpp_conn_t *const conn, void *const userdata) {
+  mam_archive_lookup_t *lookup = (mam_archive_lookup_t *)userdata;
+
+  if (!lookup->done) {
+    logError(lookup->xmppc, "MAM archive lookup timed out\n");
+    _mam_archive_lookup_done(conn, lookup, true);
+  }
+
+  return 0;
+}
+
+static bool _mam_archive_lookup_result_matches(mam_archive_lookup_t *lookup, xmpp_stanza_t *result) {
+  const char *ns = xmpp_stanza_get_ns(result);
+  if (!ns || strcmp(MAM_NS, ns) != 0) {
+    return false;
+  }
+
+  const char *queryid = xmpp_stanza_get_attribute(result, "queryid");
+  if (queryid && lookup->queryid && strcmp(queryid, lookup->queryid) != 0) {
+    return false;
+  }
+
+  xmpp_stanza_t* forwarded = xmpp_stanza_get_child_by_ns(result, FORWARD_NS);
+  xmpp_stanza_t* message = forwarded ? xmpp_stanza_get_child_by_name(forwarded, "message") : NULL;
+  if (!message) {
+    return false;
+  }
+
+  const char *archived_message_id = xmpp_stanza_get_id(message);
+  if (archived_message_id && strcmp(archived_message_id, lookup->message_id) == 0) {
+    return true;
+  }
+
+  return _mam_has_origin_id(message, lookup->message_id);
+}
+
+static bool _mam_has_origin_id(xmpp_stanza_t *message, const char *id) {
+  for (xmpp_stanza_t *child = xmpp_stanza_get_children(message); child; child = xmpp_stanza_get_next(child)) {
+    const char *name = xmpp_stanza_get_name(child);
+    const char *ns = xmpp_stanza_get_ns(child);
+    const char *origin_id = xmpp_stanza_get_attribute(child, "id");
+
+    if (name && ns && origin_id && strcmp(name, "origin-id") == 0 && strcmp(ns, SID_NS) == 0 && strcmp(origin_id, id) == 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static void _mam_archive_lookup_done(xmpp_conn_t *conn, mam_archive_lookup_t *lookup, bool delete_id_handler) {
+  if (lookup->done) {
+    return;
+  }
+
+  lookup->done = true;
+  xmpp_handler_delete(conn, _mam_archive_lookup_result_handler);
+  xmpp_timed_handler_delete(conn, _mam_archive_lookup_poll);
+  xmpp_timed_handler_delete(conn, _mam_archive_lookup_timeout);
+  if (delete_id_handler && lookup->queryid) {
+    xmpp_id_handler_delete(conn, _mam_archive_lookup_finished, lookup->queryid);
+  }
+  if (mam_archive_lookup_active == lookup) {
+    mam_archive_lookup_active = NULL;
+  }
+  if (lookup->callback) {
+    lookup->callback(conn, lookup->archive_id, lookup->userdata);
+  }
+  _mam_archive_lookup_free(lookup);
+}
+
+static void _mam_archive_lookup_free(mam_archive_lookup_t *lookup) {
+  g_free(lookup->with);
+  g_free(lookup->message_id);
+  g_free(lookup->start);
+  g_free(lookup->queryid);
+  g_free(lookup->archive_id);
+  g_free(lookup);
+}
+
 static void _mam_add_field(xmpp_ctx_t *ctx, xmpp_stanza_t *x, const char *var, const char *value, const char *type) {
   xmpp_stanza_t* field = xmpp_stanza_new(ctx);
   xmpp_stanza_set_name(field,"field");

src/mode/mam.h 🔗

@@ -45,6 +45,10 @@
 
 #include "xmppc.h"
 
+typedef void (*MamArchiveLookupCallback)(xmpp_conn_t *conn, const char *archive_id, void *userdata);
+
 void mam_execute_command(xmppc_t *xmppc, int agrc, char *argv[]);
 
+void mam_lookup_message_archive_id(xmppc_t *xmppc, const char *with, const char *message_id, MamArchiveLookupCallback callback, void *userdata);
+
 #endif // XMPPC_MAM_H__

src/mode/message.c 🔗

@@ -41,45 +41,26 @@
  */
 
 #include "message.h"
+#include "mam.h"
 #include "glib.h"
 #include "stdbool.h"
 #include "stdlib.h"
 #include "string.h"
 
-#define MAM_NS "urn:xmpp:mam:2"
-#define FORWARD_NS "urn:xmpp:forward:0"
-#define RSM_NS "http://jabber.org/protocol/rsm"
 #define SID_NS "urn:xmpp:sid:0"
 
-#define MESSAGE_ARCHIVE_LOOKUP_TIMEOUT_SECONDS 15
-#define MESSAGE_ARCHIVE_LOOKUP_POLL_MS 1000
-
 typedef struct {
-  xmppc_t *xmppc;
   char *to;
   char *message_id;
-  char *start;
-  char *queryid;
   char *archive_id;
-  gint64 deadline_us;
   bool done;
-} message_archive_lookup_t;
+} message_send_t;
 
 static void _message_send_text(xmppc_t *xmppc, char* to, char* message);
-static void _message_lookup_archive_id(xmppc_t *xmppc, const char *to, const char *message_id);
-static void _message_send_archive_query(message_archive_lookup_t *lookup);
-static int _message_archive_result_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
-static int _message_archive_query_finished(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
-static int _message_archive_query_poll(xmpp_conn_t *const conn, void *const userdata);
-static int _message_archive_lookup_timeout(xmpp_conn_t *const conn, void *const userdata);
-static bool _message_archive_result_matches(message_archive_lookup_t *lookup, xmpp_stanza_t *result);
-static bool _message_has_origin_id(xmpp_stanza_t *message, const char *id);
-static void _message_add_text_child(xmpp_ctx_t *ctx, xmpp_stanza_t *parent, const char *name, const char *text);
-static void _message_add_field(xmpp_ctx_t *ctx, xmpp_stanza_t *x, const char *var, const char *value, const char *type);
-static char *_message_lookup_start_time(void);
-static void _message_finish_sent(xmpp_conn_t *conn, message_archive_lookup_t *lookup, bool delete_id_handler);
-static void _message_archive_lookup_free(message_archive_lookup_t *lookup);
-static void _message_print_sent(message_archive_lookup_t *lookup);
+static void _message_archive_id_found(xmpp_conn_t *conn, const char *archive_id, void *userdata);
+static void _message_finish_sent(xmpp_conn_t *conn, message_send_t *sent);
+static void _message_send_free(message_send_t *sent);
+static void _message_print_sent(message_send_t *sent);
 static void _message_print_escaped_attr(const char *text);
 
 void message_execute_command(xmppc_t *xmppc, int argc, char *argv[]) {
@@ -106,8 +87,12 @@ void _message_send_text(xmppc_t *xmppc, char* to, char* text) {
     xmpp_stanza_add_child(message, origin);
     xmpp_stanza_release(origin);
 
+    message_send_t *sent = g_new0(message_send_t, 1);
+    sent->to = g_strdup(to);
+    sent->message_id = g_strdup(id);
+
     xmpp_send(conn, message);
-    _message_lookup_archive_id(xmppc, to, id);
+    mam_lookup_message_archive_id(xmppc, to, id, _message_archive_id_found, sent);
   } else {
     xmpp_disconnect(conn);
   }
@@ -115,233 +100,35 @@ void _message_send_text(xmppc_t *xmppc, char* to, char* text) {
   free(id);
 }
 
-static void _message_lookup_archive_id(xmppc_t *xmppc, const char *to, const char *message_id) {
-  message_archive_lookup_t *lookup = g_new0(message_archive_lookup_t, 1);
-  lookup->xmppc = xmppc;
-  lookup->to = g_strdup(to);
-  lookup->message_id = g_strdup(message_id);
-  lookup->start = _message_lookup_start_time();
-  lookup->deadline_us = g_get_monotonic_time() + ((gint64)MESSAGE_ARCHIVE_LOOKUP_TIMEOUT_SECONDS * G_USEC_PER_SEC);
-
-  xmpp_handler_add(xmppc->conn, _message_archive_result_handler, NULL, "message", NULL, lookup);
-  xmpp_timed_handler_add(xmppc->conn, _message_archive_lookup_timeout, MESSAGE_ARCHIVE_LOOKUP_TIMEOUT_SECONDS * 1000, lookup);
-  _message_send_archive_query(lookup);
-}
-
-static void _message_send_archive_query(message_archive_lookup_t *lookup) {
-  xmppc_t *xmppc = lookup->xmppc;
-  xmpp_ctx_t *ctx = xmppc->ctx;
-  char* id = xmpp_uuid_gen(ctx);
-  g_free(lookup->queryid);
-  lookup->queryid = g_strdup(id);
-
-  xmpp_stanza_t *iq = xmpp_iq_new(ctx, "set", id);
-  xmpp_stanza_t *query = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_name(query, "query");
-  xmpp_stanza_set_ns(query, MAM_NS);
-  xmpp_stanza_set_attribute(query, "queryid", id);
-
-  xmpp_stanza_t* x = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_name(x,"x");
-  xmpp_stanza_set_ns(x, "jabber:x:data");
-  xmpp_stanza_set_type(x,"submit");
-  xmpp_stanza_add_child(query,x);
-
-  _message_add_field(ctx, x, "FORM_TYPE", MAM_NS, "hidden");
-  _message_add_field(ctx, x, "with", lookup->to, NULL);
-  _message_add_field(ctx, x, "start", lookup->start, NULL);
-
-  xmpp_stanza_t *set = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_name(set, "set");
-  xmpp_stanza_set_ns(set, RSM_NS);
-  _message_add_text_child(ctx, set, "max", "50");
-  _message_add_text_child(ctx, set, "before", "");
-  xmpp_stanza_add_child(query, set);
-
-  xmpp_stanza_add_child(iq, query);
-  xmpp_id_handler_add(xmppc->conn, _message_archive_query_finished, id, lookup);
-  xmpp_send(xmppc->conn, iq);
-
-  xmpp_stanza_release(iq);
-  xmpp_stanza_release(query);
-  xmpp_stanza_release(x);
-  xmpp_stanza_release(set);
-  free(id);
-}
-
-static int _message_archive_result_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) {
-  message_archive_lookup_t *lookup = (message_archive_lookup_t *)userdata;
-  xmpp_stanza_t* result = xmpp_stanza_get_child_by_name(stanza,"result");
-
-  if (!lookup->done && result && _message_archive_result_matches(lookup, result)) {
-    const char *archive_id = xmpp_stanza_get_attribute(result, "id");
-    if (archive_id) {
-      g_free(lookup->archive_id);
-      lookup->archive_id = g_strdup(archive_id);
-    }
-  }
-
-  return 1;
-}
-
-static int _message_archive_query_finished(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) {
-  message_archive_lookup_t *lookup = (message_archive_lookup_t *)userdata;
-  const char *type = xmpp_stanza_get_type(stanza);
-
-  if (lookup->done) {
-    return 0;
-  }
-
-  if (lookup->archive_id) {
-    _message_finish_sent(conn, lookup, false);
-    return 0;
-  }
-
-  if (type && strcmp(type, "error") == 0) {
-    logError(lookup->xmppc, "MAM archive lookup failed\n");
-    _message_finish_sent(conn, lookup, false);
-    return 0;
-  }
-
-  if (g_get_monotonic_time() < lookup->deadline_us) {
-    xmpp_timed_handler_add(conn, _message_archive_query_poll, MESSAGE_ARCHIVE_LOOKUP_POLL_MS, lookup);
-  } else {
-    logError(lookup->xmppc, "MAM archive lookup timed out\n");
-    _message_finish_sent(conn, lookup, false);
-  }
-
-  return 0;
-}
-
-static int _message_archive_query_poll(xmpp_conn_t *const conn, void *const userdata) {
-  message_archive_lookup_t *lookup = (message_archive_lookup_t *)userdata;
-
-  if (lookup->done) {
-    return 0;
-  }
-
-  if (g_get_monotonic_time() >= lookup->deadline_us) {
-    logError(lookup->xmppc, "MAM archive lookup timed out\n");
-    _message_finish_sent(conn, lookup, false);
-    return 0;
-  }
-
-  _message_send_archive_query(lookup);
-  return 0;
-}
-
-static int _message_archive_lookup_timeout(xmpp_conn_t *const conn, void *const userdata) {
-  message_archive_lookup_t *lookup = (message_archive_lookup_t *)userdata;
+static void _message_archive_id_found(xmpp_conn_t *conn, const char *archive_id, void *userdata) {
+  message_send_t *sent = (message_send_t *)userdata;
 
-  if (!lookup->done) {
-    logError(lookup->xmppc, "MAM archive lookup timed out\n");
-    _message_finish_sent(conn, lookup, true);
+  if (archive_id) {
+    sent->archive_id = g_strdup(archive_id);
   }
 
-  return 0;
+  _message_finish_sent(conn, sent);
 }
 
-static bool _message_archive_result_matches(message_archive_lookup_t *lookup, xmpp_stanza_t *result) {
-  const char *ns = xmpp_stanza_get_ns(result);
-  if (!ns || strcmp(MAM_NS, ns) != 0) {
-    return false;
-  }
-
-  const char *queryid = xmpp_stanza_get_attribute(result, "queryid");
-  if (queryid && lookup->queryid && strcmp(queryid, lookup->queryid) != 0) {
-    return false;
-  }
-
-  xmpp_stanza_t* forwarded = xmpp_stanza_get_child_by_ns(result, FORWARD_NS);
-  xmpp_stanza_t* message = forwarded ? xmpp_stanza_get_child_by_name(forwarded, "message") : NULL;
-  if (!message) {
-    return false;
-  }
-
-  const char *message_id = xmpp_stanza_get_id(message);
-  if (message_id && strcmp(message_id, lookup->message_id) == 0) {
-    return true;
-  }
-
-  return _message_has_origin_id(message, lookup->message_id);
-}
-
-static bool _message_has_origin_id(xmpp_stanza_t *message, const char *id) {
-  for (xmpp_stanza_t *child = xmpp_stanza_get_children(message); child; child = xmpp_stanza_get_next(child)) {
-    const char *name = xmpp_stanza_get_name(child);
-    const char *ns = xmpp_stanza_get_ns(child);
-    const char *origin_id = xmpp_stanza_get_attribute(child, "id");
-
-    if (name && ns && origin_id && strcmp(name, "origin-id") == 0 && strcmp(ns, SID_NS) == 0 && strcmp(origin_id, id) == 0) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-static void _message_add_text_child(xmpp_ctx_t *ctx, xmpp_stanza_t *parent, const char *name, const char *text) {
-  xmpp_stanza_t *child = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_name(child, name);
-
-  xmpp_stanza_t *body = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_text(body, text ? text : "");
-  xmpp_stanza_add_child(child, body);
-  xmpp_stanza_release(body);
-
-  xmpp_stanza_add_child(parent, child);
-  xmpp_stanza_release(child);
-}
-
-static void _message_add_field(xmpp_ctx_t *ctx, xmpp_stanza_t *x, const char *var, const char *value, const char *type) {
-  xmpp_stanza_t* field = xmpp_stanza_new(ctx);
-  xmpp_stanza_set_name(field,"field");
-  xmpp_stanza_set_attribute(field, "var", var);
-  if (type) {
-    xmpp_stanza_set_type(field, type);
-  }
-
-  _message_add_text_child(ctx, field, "value", value);
-  xmpp_stanza_add_child(x, field);
-  xmpp_stanza_release(field);
-}
-
-static char *_message_lookup_start_time(void) {
-  GDateTime *now = g_date_time_new_now_utc();
-  GDateTime *then = g_date_time_add_seconds(now, -30);
-  char *formatted = g_date_time_format(then, "%Y-%m-%dT%H:%M:%SZ");
-  g_date_time_unref(then);
-  g_date_time_unref(now);
-  return formatted;
-}
-
-static void _message_finish_sent(xmpp_conn_t *conn, message_archive_lookup_t *lookup, bool delete_id_handler) {
-  if (lookup->done) {
+static void _message_finish_sent(xmpp_conn_t *conn, message_send_t *sent) {
+  if (sent->done) {
     return;
   }
 
-  lookup->done = true;
-  _message_print_sent(lookup);
-  xmpp_handler_delete(conn, _message_archive_result_handler);
-  xmpp_timed_handler_delete(conn, _message_archive_query_poll);
-  xmpp_timed_handler_delete(conn, _message_archive_lookup_timeout);
-  if (delete_id_handler && lookup->queryid) {
-    xmpp_id_handler_delete(conn, _message_archive_query_finished, lookup->queryid);
-  }
-  _message_archive_lookup_free(lookup);
+  sent->done = true;
+  _message_print_sent(sent);
+  _message_send_free(sent);
   xmpp_disconnect(conn);
 }
 
-static void _message_archive_lookup_free(message_archive_lookup_t *lookup) {
-  g_free(lookup->to);
-  g_free(lookup->message_id);
-  g_free(lookup->start);
-  g_free(lookup->queryid);
-  g_free(lookup->archive_id);
-  g_free(lookup);
+static void _message_send_free(message_send_t *sent) {
+  g_free(sent->to);
+  g_free(sent->message_id);
+  g_free(sent->archive_id);
+  g_free(sent);
 }
 
-static void _message_print_sent(message_archive_lookup_t *lookup) {
+static void _message_print_sent(message_send_t *lookup) {
   printf("<sent archive-id=\"");
   _message_print_escaped_attr(lookup->archive_id ? lookup->archive_id : "");
   printf("\" message-id=\"");