From 7889b698476397b60e0d43e7c498dd81ae2eb95e Mon Sep 17 00:00:00 2001 From: defanor Date: Sat, 28 Mar 2020 23:48:02 +0300 Subject: Handle service discovery requests --- README | 10 ++++------ src/rexmpp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/rexmpp.h | 3 +++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/README b/README index fd1723f..3a77d6e 100644 --- a/README +++ b/README @@ -56,16 +56,14 @@ A rough roadmap: - Primary IM features: -[.] XMPP IM (RFC 6121): loading and managing of the roster, possibly - keeping track of contact presences. Optional roster management - (loading and updates on roster push, with versioning) is - implemented. +[.] XMPP IM (RFC 6121): roster management is implemented, but not keeping + track of contact presences yet. - Common and reliable IM features: -[.] XEP-0030: Service Discovery (implemented partially, just to - discover server features) +[.] XEP-0030: Service Discovery: implemented, but without tracking of + features provided by known entities yet. [ ] XEP-0115: Entity Capabilities [ ] XEP-0045: Multi-User Chat [ ] XEP-0166: Jingle diff --git a/src/rexmpp.c b/src/rexmpp.c index d2d3dd8..776069a 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -52,6 +52,34 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...) } } +xmlNodePtr rexmpp_xml_feature (const char *var) { + xmlNodePtr feature = xmlNewNode(NULL, "feature"); + xmlNewProp(feature, "var", var); + return feature; +} + +xmlNodePtr rexmpp_xml_error (const char *type, const char *condition) { + xmlNodePtr error = xmlNewNode(NULL, "error"); + xmlNewProp(error, "type", type); + xmlNodePtr cond = xmlNewNode(NULL, condition); + xmlNewNs(cond, "urn:ietf:params:xml:ns:xmpp-stanzas", NULL); + xmlAddChild(error, cond); + return error; +} + +xmlNodePtr rexmpp_xml_default_disco_info () { + /* There must be at least one identity, so filling in somewhat + sensible defaults. A basic client may leave them be, while an + advanced one would adjust and/or extend them. */ + xmlNodePtr identity = xmlNewNode(NULL, "identity"); + xmlNewProp(identity, "category", "client"); + xmlNewProp(identity, "type", "console"); + xmlNewProp(identity, "name", "rexmpp"); + xmlNodePtr disco_feature = rexmpp_xml_feature("http://jabber.org/protocol/disco#info"); + identity->next = disco_feature; + return identity; +} + rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid, log_function_t log_function, @@ -174,6 +202,8 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, } gsasl_callback_set(s->sasl_ctx, s->sasl_property_cb); + s->disco_info = rexmpp_xml_default_disco_info(); + return REXMPP_SUCCESS; } @@ -253,6 +283,10 @@ void rexmpp_done (rexmpp_t *s) { free(s->roster_ver); s->roster_ver = NULL; } + if (s->disco_info != NULL) { + xmlFreeNodeList(s->disco_info); + s->disco_info = NULL; + } while (s->stanza_queue != NULL) { xmlNodePtr next = xmlNextElementSibling(s->stanza_queue); xmlFreeNode(s->send_queue); @@ -1156,7 +1190,10 @@ void rexmpp_stream_bind (rexmpp_t *s) { void rexmpp_process_element (rexmpp_t *s) { xmlNodePtr elem = s->current_element; - /* IQs */ + /* IQs. These are the ones that should be processed by the library; + if a user-facing application wants to handle them on its own, it + should cancel further processing by the library (so we can send + errors for unhandled IQs here). */ if (rexmpp_xml_match(elem, "jabber:client", "iq")) { char *type = xmlGetProp(elem, "type"); /* IQ responses. */ @@ -1228,6 +1265,24 @@ void rexmpp_process_element (rexmpp_t *s) { if (s->roster_cache_file != NULL) { rexmpp_roster_cache_write(s); } + } else { + /* An unknown request. */ + rexmpp_iq_reply(s, elem, "error", + rexmpp_xml_error("cancel", "service-unavailable")); + } + } + /* IQ "get" requests. */ + if (strcmp(type, "get") == 0) { + xmlNodePtr query = xmlFirstElementChild(elem); + if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info", "query")) { + xmlNodePtr result = xmlNewNode(NULL, "query"); + xmlNewNs(result, "http://jabber.org/protocol/disco#info", NULL); + xmlAddChild(result, xmlCopyNodeList(s->disco_info)); + rexmpp_iq_reply(s, elem, "result", result); + } else { + /* An unknown request. */ + rexmpp_iq_reply(s, elem, "error", + rexmpp_xml_error("cancel", "service-unavailable")); } } free(type); diff --git a/src/rexmpp.h b/src/rexmpp.h index 7679546..f064154 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -252,6 +252,9 @@ struct rexmpp xmlNodePtr roster_items; char *roster_ver; + /* Other dynamic data. */ + xmlNodePtr disco_info; + /* IQs we're waiting for responses to. */ rexmpp_iq_t *active_iq; -- cgit v1.2.3