summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2021-09-25 11:46:16 +0300
committerdefanor <defanor@uberspace.net>2021-09-25 11:46:16 +0300
commit6efd6ba8e375b25ac34224e16b191ea11fd5ca0f (patch)
tree743b7dfef5d3f6eb8151e655364c3b0c8cf88357
parent8021dfe24fc44c7a1adbcf8bd49c48e488e8ba2c (diff)
Introduce IQ caching
Aiming its usage for service discovery, and possibly similar information retrieval activities.
-rw-r--r--README11
-rw-r--r--src/rexmpp.c91
-rw-r--r--src/rexmpp.h22
3 files changed, 120 insertions, 4 deletions
diff --git a/README b/README
index fe4a62f..0a4095e 100644
--- a/README
+++ b/README
@@ -76,17 +76,20 @@ A rough roadmap:
[ ] XEP-0363: HTTP File Upload?
[ ] XEP-0184: Message Delivery Receipts?
[ ] OTR/OMEMO/MLS encryption?
+[ ] A/V calls?
- Additional state tracking:
[+] XMPP IM (RFC 6121): track presences of contacts.
-[+] XEP-0163 v1.2: Personal Eventing Protocol: track contacts' published items.
+[+] XEP-0163 v1.2: Personal Eventing Protocol: track contacts'
+ published items.
+[+] IQ response caching.
[ ] XEP-0030: Service Discovery: track features provided by known
- entities.
+ entities?
[ ] XEP-0115: Entity Capabilities: maintain a capability database,
- track capabilities of known entities.
-[ ] XEP-0045: Multi-User Chat: tracking of related states/presences.
+ track capabilities of known entities?
+[ ] XEP-0045: Multi-User Chat: tracking of related states/presences?
- Various utility functions:
diff --git a/src/rexmpp.c b/src/rexmpp.c
index bf396ca..b0286c1 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -35,6 +35,11 @@
#include "rexmpp_openpgp.h"
#include "rexmpp_console.h"
+struct rexmpp_iq_cacher {
+ rexmpp_iq_callback_t cb;
+ void *cb_data;
+};
+
const char *rexmpp_strerror (rexmpp_err_t error) {
switch (error) {
case REXMPP_SUCCESS: return "No error";
@@ -420,6 +425,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->stanza_queue = NULL;
s->stream_id = NULL;
s->active_iq = NULL;
+ s->iq_cache = NULL;
s->reconnect_number = 0;
s->next_reconnect_time.tv_sec = 0;
s->next_reconnect_time.tv_usec = 0;
@@ -428,6 +434,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->stanza_queue_size = 1024;
s->send_queue_size = 1024;
s->iq_queue_size = 1024;
+ s->iq_cache_size = 1024;
s->log_function = log_func;
s->sasl_property_cb = NULL;
s->xml_in_cb = NULL;
@@ -615,6 +622,10 @@ void rexmpp_done (rexmpp_t *s) {
s->active_iq = next;
rexmpp_iq_finish(s, iq, 0, NULL);
}
+ if (s->iq_cache != NULL) {
+ xmlFreeNodeList(s->iq_cache);
+ s->iq_cache = NULL;
+ }
}
void rexmpp_schedule_reconnect (rexmpp_t *s) {
@@ -711,6 +722,17 @@ int rexmpp_xml_match (xmlNodePtr node,
return 1;
}
+int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2) {
+ /* Just serialize and compare strings for now: awkward, but
+ simple. */
+ char *n1str = rexmpp_xml_serialize(n1);
+ char *n2str = rexmpp_xml_serialize(n2);
+ int eq = (strcmp(n1str, n2str) == 0);
+ free(n1str);
+ free(n2str);
+ return eq;
+}
+
xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node,
const char *namespace,
const char *name)
@@ -1016,6 +1038,75 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
return rexmpp_send(s, iq_stanza);
}
+void rexmpp_iq_cache_cb (rexmpp_t *s,
+ void *cb_data,
+ xmlNodePtr request,
+ xmlNodePtr response,
+ int success)
+{
+ if (success && response != NULL) {
+ xmlNodePtr prev_last = NULL, last = NULL, ciq = s->iq_cache;
+ uint32_t size = 0;
+ while (ciq != NULL && ciq->next != NULL) {
+ prev_last = last;
+ last = ciq;
+ size++;
+ ciq = ciq->next->next;
+ }
+ if (size >= s->iq_queue_size && prev_last != NULL) {
+ xmlFreeNode(last->next);
+ xmlFreeNode(last);
+ prev_last->next->next = NULL;
+ }
+ xmlNodePtr req = xmlCopyNode(request, 1);
+ xmlNodePtr resp = xmlCopyNode(response, 1);
+ req->next = resp;
+ resp->next = s->iq_cache;
+ s->iq_cache = req;
+ }
+ struct rexmpp_iq_cacher *cacher = cb_data;
+ if (cacher->cb != NULL) {
+ cacher->cb(s, cacher->cb_data, request, response, success);
+ }
+ free(cacher);
+}
+
+rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
+ const char *type,
+ const char *to,
+ xmlNodePtr payload,
+ rexmpp_iq_callback_t cb,
+ void *cb_data,
+ int fresh)
+{
+ if (! fresh) {
+ xmlNodePtr ciq = s->iq_cache;
+ while (ciq != NULL && ciq->next != NULL) {
+ xmlNodePtr ciq_pl = xmlFirstElementChild(ciq);
+ char *ciq_type = xmlGetProp(ciq, "type");
+ char *ciq_to = xmlGetProp(ciq, "to");
+ int matches = (rexmpp_xml_eq(ciq_pl, payload) &&
+ strcmp(ciq_type, type) == 0 &&
+ strcmp(ciq_to, to) == 0);
+ free(ciq_to);
+ free(ciq_type);
+ if (matches) {
+ xmlFreeNode(payload);
+ if (cb != NULL) {
+ cb(s, cb_data, ciq, ciq->next, 1);
+ }
+ return REXMPP_SUCCESS;
+ }
+ ciq = ciq->next->next;
+ }
+ }
+ struct rexmpp_iq_cacher *cacher = malloc(sizeof(struct rexmpp_iq_cacher));
+ cacher->cb = cb;
+ cacher->cb_data = cb_data;
+ return rexmpp_iq_new(s, type, to, payload, rexmpp_iq_cache_cb, cacher);
+}
+
+
rexmpp_err_t rexmpp_sm_ack (rexmpp_t *s) {
char buf[11];
xmlNodePtr ack = xmlNewNode(NULL, "a");
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 5e40d30..addd4d4 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -277,6 +277,7 @@ struct rexmpp
uint32_t stanza_queue_size;
uint32_t send_queue_size;
uint32_t iq_queue_size;
+ uint32_t iq_cache_size;
/* Callbacks. */
log_function_t log_function;
@@ -300,6 +301,9 @@ struct rexmpp
/* IQs we're waiting for responses to. */
rexmpp_iq_t *active_iq;
+ /* Cached IQ requests and responses. */
+ xmlNodePtr iq_cache;
+
/* Connection and stream management. */
unsigned int reconnect_number;
time_t reconnect_seconds;
@@ -431,6 +435,19 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
void *cb_data);
/**
+ @brief Same as ::rexmpp_iq_new, but caches responses, and can use
+ cached ones.
+ @param[in] fresh Do not read cache, make a new request.
+*/
+rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
+ const char *type,
+ const char *to,
+ xmlNodePtr payload,
+ rexmpp_iq_callback_t cb,
+ void *cb_data,
+ int fresh);
+
+/**
@brief Determines the maximum time to wait before the next
::rexmpp_run call.
@param[in] s ::rexmpp
@@ -499,6 +516,11 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...);
char *rexmpp_get_name (rexmpp_t *s, const char *jid_str);
/**
+ @brief Compares two XML elements.
+*/
+int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2);
+
+/**
@brief Matches an XML node against a namespace and an element name.
@param[in] node An XML node to match.
@param[in] namespace An XML namespace. Can be NULL (matches